taps: wip: init
This commit is contained in:
parent
db5f9a1ac8
commit
8bbafe0f0a
6 changed files with 309 additions and 0 deletions
9
pkgs/ch-proxy/sendfd.h
Normal file
9
pkgs/ch-proxy/sendfd.h
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
#ifndef _CH_PROXY_SENFD
|
||||
#define _CH_PROXY_SENFD
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
ssize_t send_fd(int dst_fd, int fd);
|
||||
|
||||
#endif _CH_PROXY_SENFD
|
||||
|
||||
1
pkgs/taps/.envrc
Normal file
1
pkgs/taps/.envrc
Normal file
|
|
@ -0,0 +1 @@
|
|||
use nix ../../ -A pkgs.taps
|
||||
241
pkgs/taps/main.c
Normal file
241
pkgs/taps/main.c
Normal file
|
|
@ -0,0 +1,241 @@
|
|||
#define _GNU_SOURCE
|
||||
|
||||
#include <stdlib.h> /* secure_getenv */
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/un.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <ctype.h>
|
||||
#include <pwd.h>
|
||||
#include <fnmatch.h>
|
||||
#include <fcntl.h> /* open, O_NONBLOCK, &c */
|
||||
#include <stdio.h> /* perror */
|
||||
#include <errno.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
|
||||
#define __UAPI_DEF_IF_IFNAMSIZ 1
|
||||
#include <linux/if_tun.h>
|
||||
#include <linux/if.h>
|
||||
|
||||
#include "sendfd.h"
|
||||
|
||||
// From `man unix`
|
||||
#define SUN_PATH_SZ 108
|
||||
#define N_CONNS 16
|
||||
|
||||
#define IFR_FLAGS_ALLOWED (IFF_NO_PI | IFF_TAP | IFF_TUN | IFF_VNET_HDR | IFF_MULTI_QUEUE)
|
||||
#define IFR_FLAGS_DEFAULT (IFF_NO_PI | IFF_TAP | IFF_VNET_HDR)
|
||||
|
||||
#define DO_OR_DIE(expr) DO_OR_DIE_X((expr) == 0)
|
||||
#define DO_OR_DIE_X(expr) \
|
||||
do if (!(expr)) { \
|
||||
perror((#expr)); \
|
||||
exit(EXIT_FAILURE); \
|
||||
} while(false)
|
||||
|
||||
struct allow_pattern {
|
||||
// enum { USER = 1, GROUP = 2 } type;
|
||||
// union { uid_t uid, gid_t gid } xid;
|
||||
char *name;
|
||||
};
|
||||
struct allow_patterns {
|
||||
size_t n;
|
||||
struct allow_pattern *patterns;
|
||||
};
|
||||
|
||||
/* Running on the same host, not caring for alignment */
|
||||
struct tap_request {
|
||||
short ifrFlags; /* 0 to use defaults: IFF_TAP | IFF_NO_PI | IFF_VNET_HDR */
|
||||
char name[IFNAMSIZ];
|
||||
};
|
||||
|
||||
struct tap_reply {
|
||||
enum { OK = 0, AUTH_ERROR = 1 } status;
|
||||
char name[IFNAMSIZ];
|
||||
};
|
||||
|
||||
int tuntap_alloc(char *dev, short openFlags, short ifrFlags, int *out_fd);
|
||||
|
||||
bool match_mask(const char *test_addr, const char *expected_addr, const char *mask, int n) {
|
||||
for (int octet = 0; octet < n; ++octet) {
|
||||
if ((test_addr[octet] & mask[octet]) != expected_addr[octet]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Adapted from spectrum's `mktuntap.c` (2019 Alyssa Ross <hi@alyssa.is>
|
||||
* GPL-2.0-only), which in turn adapts `tun_alloc` from
|
||||
* `linux/Documentation/networking/tuntap.rst`.
|
||||
*
|
||||
* ifrFlags: IFF_TUN - TUN device (no Ethernet headers)
|
||||
* IFF_TAP - TAP device
|
||||
*
|
||||
* IFF_NO_PI - Do not provide packet information
|
||||
*/
|
||||
int tuntap_alloc(char *dev, short openFlags, short ifrFlags, int *out_fd) {
|
||||
struct ifreq ifr;
|
||||
int fd, err;
|
||||
|
||||
// if ((fd = open("/dev/net/tun", O_RDWR)) < 0) {
|
||||
// return tun_alloc_old(dev);
|
||||
// }
|
||||
|
||||
DO_OR_DIE_X((fd = open("/dev/net/tun", openFlags)) >= 0);
|
||||
|
||||
memset(&ifr, 0, sizeof(ifr));
|
||||
|
||||
if (*dev) {
|
||||
int devLen = strlen(dev);
|
||||
if (devLen >= IFNAMSIZ) {
|
||||
/* If client requests a name, we do want the entire name to fit */
|
||||
errno = EINVAL;
|
||||
return EINVAL;
|
||||
}
|
||||
strncpy(ifr.ifr_name, dev, IFNAMSIZ);
|
||||
}
|
||||
|
||||
if ((err = ioctl(fd, TUNSETIFF, (void *)&ifr)) < 0) {
|
||||
close(fd);
|
||||
return err;
|
||||
}
|
||||
|
||||
strcpy(dev, ifr.ifr_name);
|
||||
*out_fd = fd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int acceptRequests(const char *requestsPath, const struct allow_patterns *patterns) {
|
||||
int listener;
|
||||
struct sockaddr_un addr;
|
||||
const bool t = 1;
|
||||
|
||||
DO_OR_DIE(listener = socket(AF_UNIX, SOCK_SEQPACKET, 0));
|
||||
DO_OR_DIE(setsockopt(listener, SOL_SOCKET, SO_PASSCRED, &t, 1) != 0);
|
||||
|
||||
addr.sun_family = AF_UNIX;
|
||||
strncpy(addr.sun_path, requestsPath, SUN_PATH_SZ);
|
||||
DO_OR_DIE (bind(listener, &addr, sizeof(addr)) == -1);
|
||||
|
||||
DO_OR_DIE(listen(listener, N_CONNS));
|
||||
|
||||
for (;;) {
|
||||
int sock = -1;
|
||||
struct ucred cred = { 0 };
|
||||
struct msghdr msg = { 0 };
|
||||
struct cmsghdr *cmsg = NULL;
|
||||
struct iovec iov = { 0 };
|
||||
struct tap_request req = { 0 };
|
||||
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
|
||||
iov.iov_base = &req;
|
||||
iov.iov_len = sizeof(struct tap_request);
|
||||
|
||||
DO_OR_DIE(sock = accept(listener, NULL, NULL));
|
||||
|
||||
DO_OR_DIE_X(recvmsg(sock, &msg, 0) > 0);
|
||||
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
|
||||
if (!(cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDENTIALS)) {
|
||||
continue;
|
||||
}
|
||||
memcpy(&cred, CMSG_DATA(cmsg), sizeof(struct ucred));
|
||||
break;
|
||||
}
|
||||
|
||||
if (req.ifrFlags == 0) {
|
||||
req.ifrFlags = IFR_FLAGS_DEFAULT;
|
||||
}
|
||||
|
||||
bool allowed = false;
|
||||
for (int i = 0; !allowed && i < patterns->n; ++i) {
|
||||
bool ifnameOk = fnmatch(patterns->patterns[i].name, req.name, 0) == 0;
|
||||
bool flagsOk = (req.ifrFlags & IFR_FLAGS_ALLOWED) == req.ifrFlags;
|
||||
allowed = ifnameOk && flagsOk;
|
||||
}
|
||||
|
||||
struct tap_reply reply = { 0 };
|
||||
if (!allowed) { reply.status = AUTH_ERROR; }
|
||||
if (allowed) {
|
||||
/* O_CLOEXEC? */
|
||||
int fd = 0;
|
||||
DO_OR_DIE(tuntap_alloc(req.name, O_RDWR | O_NONBLOCK, req.ifrFlags, &fd));
|
||||
struct iovec iov = { 0 };
|
||||
iov.iov_base = &reply;
|
||||
iov.iov_len = sizeof(struct tap_reply);
|
||||
DO_OR_DIE_X(send_fd(sock, fd, &iov) > 0);
|
||||
}
|
||||
close(sock);
|
||||
}
|
||||
close(listener);
|
||||
}
|
||||
|
||||
struct allow_patterns parsePatterns(const char *raw) {
|
||||
const size_t rawLen = strlen(raw);
|
||||
|
||||
size_t nPatterns = 0;
|
||||
for (int i = 0; i < rawLen; ++i) {
|
||||
const int start = i;
|
||||
if (isspace(raw[i])) { continue; }
|
||||
for (; i < rawLen && !isspace(raw[i]); ++i) { }
|
||||
if (start < i) { ++nPatterns; }
|
||||
}
|
||||
|
||||
struct allow_pattern *patterns = calloc(nPatterns, sizeof(struct allow_pattern));
|
||||
|
||||
int iPattern = 0;
|
||||
for (int i = 0; i < rawLen; ++i) {
|
||||
if (isspace(raw[i])) { continue; }
|
||||
/* used to have per-group/per-user patterns, "u:$username:$pattern", &c - gone */
|
||||
{
|
||||
const int start = i;
|
||||
for (; i < rawLen && !isspace(raw[i]); ++i) { }
|
||||
if (i < rawLen) {
|
||||
patterns[iPattern].name = strndup(&raw[start], i - start);
|
||||
iPattern += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
struct allow_patterns out = {
|
||||
.n = nPatterns,
|
||||
.patterns = patterns
|
||||
};
|
||||
return out;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
bool cmdServe = false;
|
||||
bool cmdGet = false;
|
||||
|
||||
char **rest = argv + 1;
|
||||
|
||||
if (strcmp(rest[0], "serve") == 0) {
|
||||
cmdServe = true;
|
||||
++rest;
|
||||
} else if (strcmp(rest[0], "get") == 0) {
|
||||
cmdGet = true;
|
||||
++rest;
|
||||
} else {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
const char *patternsRaw = secure_getenv("TAPS_ALLOW");
|
||||
struct allow_patterns patterns = { 0 };
|
||||
if (cmdServe && patternsRaw != NULL) { patterns = parsePatterns(patternsRaw); }
|
||||
DO_OR_DIE_X(patterns.patterns != NULL);
|
||||
|
||||
const char *servePath = secure_getenv("TAPS_SOCK");
|
||||
|
||||
if (cmdServe && patterns.patterns != NULL) {
|
||||
acceptRequests(servePath, &patterns);
|
||||
} else if (cmdGet) {
|
||||
} else {
|
||||
exit(EXIT_FAILURE); }
|
||||
|
||||
return 0;
|
||||
}
|
||||
4
pkgs/taps/meson.build
Normal file
4
pkgs/taps/meson.build
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
project('taps', 'c')
|
||||
|
||||
sendfd = dependency('sendfd')
|
||||
executable('taps', 'main.c', dependencies: [sendfd], install: true)
|
||||
44
pkgs/taps/package.nix
Normal file
44
pkgs/taps/package.nix
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
{
|
||||
lib,
|
||||
stdenv,
|
||||
meson,
|
||||
pkg-config,
|
||||
rustc,
|
||||
ninja,
|
||||
ch-proxy,
|
||||
}:
|
||||
|
||||
stdenv.mkDerivation {
|
||||
pname = "taps";
|
||||
version = "0.0.0";
|
||||
src =
|
||||
let
|
||||
fs = lib.fileset;
|
||||
in
|
||||
fs.toSource {
|
||||
root = ./.;
|
||||
fileset = fs.unions [
|
||||
./meson.build
|
||||
./main.c
|
||||
];
|
||||
};
|
||||
|
||||
nativeBuildInputs = [
|
||||
ninja
|
||||
meson
|
||||
pkg-config
|
||||
rustc
|
||||
];
|
||||
buildInputs = [ ch-proxy ];
|
||||
}
|
||||
# { lib, rustPlatform }:
|
||||
#
|
||||
# rustPlatform.buildRustPackage {
|
||||
# pname = "taps";
|
||||
# version = "0.0.0";
|
||||
# src = let fs = lib.filesystem; in fs.toSource {
|
||||
# root = ./.;
|
||||
# fileset = fs.unions [
|
||||
# ];
|
||||
# };
|
||||
# };
|
||||
Loading…
Add table
Add a link
Reference in a new issue