From 8343888afd4658e381d7c9f6323ce7690b65ee9d Mon Sep 17 00:00:00 2001 From: Sergey Matveev Date: Fri, 11 Nov 2022 21:25:45 +0300 Subject: [PATCH] privsep, capsicum, kqueue --- CHANGES | 6 ++ dht-bootstrap.c | 245 +++++++++++++++++++++++++++++------------------- 2 files changed, 156 insertions(+), 95 deletions(-) diff --git a/CHANGES b/CHANGES index e8a770d..b1c7181 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,9 @@ +2022-11-11: + + * Privilege-separated packets sender + * FreeBSD Capsicum sandboxing + * poll() replaced with kqueue() + 2022-11-09: * Ability to explicitly specify IP addresses to bind to diff --git a/dht-bootstrap.c b/dht-bootstrap.c index 9320ca7..9b6f714 100644 --- a/dht-bootstrap.c +++ b/dht-bootstrap.c @@ -25,18 +25,24 @@ THE SOFTWARE. #include #include #include -#include #include #include #include #include -#include -#include #include #include +#include +#include +#include +#include +#include + +#undef MIN #define MIN(x, y) ((x) <= (y) ? (x) : (y)) +#define MAX_PKT_SIZE 1536 + #define ERROR 0 #define REPLY 1 #define PING 2 @@ -61,6 +67,7 @@ struct node { unsigned char id[160]; struct sockaddr_storage ss; socklen_t sslen; + char _pad[4]; }; #define CIRCULAR_LIST_SIZE 256 @@ -79,6 +86,17 @@ static int token_bucket_tokens; static bool verbose = true; +struct Pkt { + unsigned char buf[MAX_PKT_SIZE]; + struct sockaddr_storage ss; + size_t len; + socklen_t salen; + char _pad[4]; +}; + +static struct Pkt pkt; +static int sockW = -1; + // ------------------------ >8 ------------------------ static const char * @@ -238,7 +256,7 @@ parse_message( if (p) { size_t i = p - buf + 9; unsigned long j = 0, j6 = 0; - while (1) { + for (;;) { l = strtoul((const char *)buf + i, &q, 10); if (q && *q == ':' && l > 0) { CHECK(q + 1, l); @@ -352,17 +370,17 @@ overflow: #define ADD_V(buf, offset, size) COPY(buf, offset, my_v, (sizeof my_v), size) static bool -is_martian(const struct sockaddr *sa) +is_martian(const struct sockaddr_storage *ss) { - switch (sa->sa_family) { + switch (((const struct sockaddr *)ss)->sa_family) { case AF_INET: { - const struct sockaddr_in *sin = (const struct sockaddr_in *)sa; + const struct sockaddr_in *sin = (const struct sockaddr_in *)ss; const unsigned char *address = (const unsigned char *)&sin->sin_addr; return sin->sin_port == 0 || (address[0] == 0) || (address[0] == 127) || ((address[0] & 0xE0) == 0xE0); } case AF_INET6: { - const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sa; + const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)ss; const unsigned char *address = (const unsigned char *)&sin6->sin6_addr; return sin6->sin6_port == 0 || (address[0] == 0xFF) || (address[0] == 0xFE && (address[1] & 0xC0) == 0x80) || @@ -515,7 +533,7 @@ token_bucket(void) time_t now = time(NULL); if (token_bucket_tokens == 0) { token_bucket_tokens = - MIN(MAX_TOKEN_BUCKET_TOKENS, 4 * (now - token_bucket_time)); + MIN(MAX_TOKEN_BUCKET_TOKENS, 4 * (int)(now - token_bucket_time)); token_bucket_time = now; } if (token_bucket_tokens == 0) @@ -524,6 +542,24 @@ token_bucket(void) return true; } +static ssize_t +pktSend(const struct Pkt *p) +{ + int s = -1; + const struct sockaddr *sa = (const struct sockaddr *)&(p->ss); + switch (sa->sa_family) { + case AF_INET: + s = dht_socket; + break; + case AF_INET6: + s = dht_socket6; + break; + default: + abort(); + } + return sendto(s, p->buf, p->len, 0, sa, p->salen); +} + static ssize_t dht_send( const void *buf, @@ -533,18 +569,13 @@ dht_send( { if (salen == 0) abort(); - int s; - if (sa->sa_family == AF_INET) - s = dht_socket; - else if (sa->sa_family == AF_INET6) - s = dht_socket6; - else - s = -1; - if (s < 0) { - errno = EAFNOSUPPORT; - return -1; - } - return sendto(s, buf, len, 0, sa, salen); + memcpy(&(pkt.ss), sa, sizeof(struct sockaddr_storage)); + memcpy(pkt.buf, buf, len); + pkt.len = len; + pkt.salen = salen; + if (sockW == -1) + return pktSend(&pkt); + return write(sockW, &pkt, sizeof(struct Pkt)); } static ssize_t @@ -654,14 +685,11 @@ send_request( struct sockaddr_storage ss; socklen_t sslen; - int rc; if (dopop) { - rc = list_pop(list, &ss, &sslen); - if (rc == 0) + if (list_pop(list, &ss, &sslen) == 0) return 0; } else { - rc = list_random(list, NULL, &ss, &sslen); - if (rc == 0) + if (list_random(list, NULL, &ss, &sslen) == 0) return 0; } @@ -771,11 +799,9 @@ buffer_random_nodes(int af, unsigned char *nodes) struct sockaddr_storage ss; socklen_t sslen; unsigned char id[20]; - int rc; int n = 0; while (n < 8) { - rc = list_random(list, id, &ss, &sslen); - if (rc < 1) + if (list_random(list, id, &ss, &sslen) < 1) break; switch (af) { case AF_INET: { @@ -860,7 +886,7 @@ main(int argc, char **argv) char *ipv6addr = NULL; int opt = 0; - while (1) { + for (;;) { opt = getopt(argc, argv, "q4:6:"); if (opt < 0) break; @@ -895,6 +921,7 @@ main(int argc, char **argv) arc4random_buf(myid, sizeof myid); memcpy(my_v, "1:v4:JB\0\0", 9); + memset(&(pkt.ss), 0, sizeof(struct sockaddr_storage)); int rc = 0; unsigned char ttid[4]; @@ -919,63 +946,129 @@ main(int argc, char **argv) infop = info; while (infop) { make_tid(ttid, "pn", 0); - send_ping(infop->ai_addr, infop->ai_addrlen, ttid, 4); + if (send_ping(infop->ai_addr, infop->ai_addrlen, ttid, 4) == -1) + err(EXIT_FAILURE, "sendto()"); infop = infop->ai_next; } freeaddrinfo(info); - i++; } - token_bucket_time = time(NULL); - token_bucket_tokens = MAX_TOKEN_BUCKET_TOKENS; - bool send4 = false; - struct pollfd fds[2]; - fds[0].fd = dht_socket; - fds[0].events = POLLIN; - fds[1].fd = dht_socket6; - fds[1].events = POLLIN; - close(STDIN_FILENO); if (!verbose) close(STDOUT_FILENO); + + int sockets[2]; + if (socketpair(PF_LOCAL, SOCK_DGRAM, 0, sockets) != 0) + err(EXIT_FAILURE, "socketpair()"); + int sockR = sockets[0]; + sockW = sockets[1]; + pid_t pid = fork(); + if (pid == -1) + err(EXIT_FAILURE, "fork()"); + if (pid == 0) { + close(sockW); + + rlimited(RLIMIT_NPROC); + rlimited(RLIMIT_FSIZE); + rlimited(RLIMIT_NOFILE); + ssize_t n = 0; + unsigned char buf[sizeof(struct Pkt)]; + for (;;) { + n = read(sockR, buf, sizeof buf); + if (n == -1) { + perror("read()"); + continue; + } + if (pktSend((struct Pkt *)buf) == -1) + perror("sendto()"); + } + } + close(sockR); + + cap_rights_t caprights; + cap_rights_init(&caprights, CAP_WRITE); + if (cap_rights_limit(STDERR_FILENO, &caprights) != 0) + err(EXIT_FAILURE, "cap_rights_limit(stderr)"); + if (!verbose) + if (cap_rights_limit(STDOUT_FILENO, &caprights) != 0) + err(EXIT_FAILURE, "cap_rights_limit(stdout)"); + if (cap_rights_limit(sockW, &caprights) != 0) + err(EXIT_FAILURE, "cap_rights_limit(sockW)"); + if (cap_enter() != 0) + err(EXIT_FAILURE, "cap_enter()"); + + token_bucket_time = time(NULL); + token_bucket_tokens = MAX_TOKEN_BUCKET_TOKENS; + + int kq = kqueue(); + if (kq == -1) + err(EXIT_FAILURE, "kqueue()"); + struct kevent chs[2]; + struct kevent ev; + int chsLen = 0; + if (dht_socket != -1) { + EV_SET(&(chs[chsLen]), dht_socket, EVFILT_READ, EV_ADD, 0, 0, NULL); + chsLen++; + } + if (dht_socket6 != -1) { + EV_SET(&(chs[chsLen]), dht_socket6, EVFILT_READ, EV_ADD, 0, 0, NULL); + chsLen++; + } + rlimited(RLIMIT_NPROC); rlimited(RLIMIT_FSIZE); -#if __FreeBSD__ rlimited(RLIMIT_NOFILE); -#endif // __FreeBSD__ - while (1) { - int tv_sec = 0; + struct timespec tm; + bool send4; + for (;;) { if ((dht_socket >= 0 && list_elements(&v4_confirmed) <= 16) || (dht_socket6 >= 0 && list_elements(&v6_confirmed) <= 16)) - tv_sec = 0; + tm.tv_sec = 0; else - tv_sec = random() % 30; - int tv_msec = random() % 1000; + tm.tv_sec = random() % 30; + tm.tv_nsec = 1000000 * (random() % 1000); -#ifndef __linux__ setproctitle_fast( "%d+%d %d+%d", list_elements(&v4_confirmed), list_elements(&v6_confirmed), list_elements(&v4_new), list_elements(&v6_new)); -#endif // __linux__ - rc = poll(fds, 2, tv_sec * 1000 + tv_msec); - if (rc < 0) { - perror("poll"); - sleep(1); - } + rc = kevent(kq, chs, chsLen, &ev, 1, &tm); + if (rc < 0) + err(EXIT_FAILURE, "kevent()"); if (rc > 0) { + ssize_t got = -1; + unsigned char buf[MAX_PKT_SIZE]; + struct sockaddr_storage source_storage; + struct sockaddr *source = (struct sockaddr *)&source_storage; + socklen_t sourcelen = sizeof(source_storage); + if (ev.flags & EV_ERROR) { + fprintf(stderr, "EV_ERROR: %s\n", strerror((int)(ev.data))); + } else { + got = recvfrom((int)ev.ident, buf, MAX_PKT_SIZE, 0, source, &sourcelen); + } + if (got < 0 || sourcelen > sizeof(struct sockaddr_storage)) + goto dontread; + if (is_martian(&source_storage)) + goto dontread; + if (got < MAX_PKT_SIZE) { + buf[got] = '\0'; + } else { + if (verbose) + printf("%s : overlong message\n", getAddr(source, sourcelen)); + goto dontread; + } + int message; unsigned char tid[16]; unsigned char id[20]; unsigned char info_hash[20]; unsigned char target[20]; - unsigned char buf[1536]; unsigned char nodes[256]; unsigned char nodes6[1024]; unsigned char token[128]; @@ -989,44 +1082,6 @@ main(int argc, char **argv) size_t values_len = sizeof values; size_t values6_len = sizeof values6; int want; - struct sockaddr_storage source_storage; - struct sockaddr *source = (struct sockaddr *)&source_storage; - socklen_t sourcelen = sizeof(source_storage); - ssize_t got = 0; - if (fds[0].revents != 0) { - if ((fds[0].revents & (POLLERR | POLLNVAL)) > 0) { - fprintf(stderr, "error in fds[0]"); - got = -1; - } else { - got = recvfrom(dht_socket, buf, 1536, 0, source, &sourcelen); - } - } else if (fds[1].revents != 0) { - if ((fds[1].revents & (POLLERR | POLLNVAL)) > 0) { - fprintf(stderr, "error in fds[1]"); - got = -1; - } else { - got = recvfrom(dht_socket6, buf, 1536, 0, source, &sourcelen); - } - } - - if (got < 0 || sourcelen > sizeof(struct sockaddr_storage)) - goto dontread; - - if (is_martian(source)) - goto dontread; - - /* There's a bug in parse_message -- it will happily overflow the - buffer if it's not NUL-terminated. For now, put a NUL at the - end of buffers. */ - - if (got < 1536) { - buf[got] = '\0'; - } else { - if (verbose) - printf("%s : overlong message\n", getAddr(source, sourcelen)); - goto dontread; - } - message = parse_message( buf, (size_t)got, @@ -1063,7 +1118,7 @@ main(int argc, char **argv) if (!token_bucket()) { if (verbose) printf( - "%s : dropping request due to rate limiting.\n", + "%s : dropping request due to rate limiting\n", getAddr(source, sourcelen)); goto dontread; } -- 2.44.0