]> Sergey Matveev's repositories - dht-bootstrap.git/commitdiff
privsep, capsicum, kqueue
authorSergey Matveev <stargrave@stargrave.org>
Fri, 11 Nov 2022 18:25:45 +0000 (21:25 +0300)
committerSergey Matveev <stargrave@stargrave.org>
Fri, 11 Nov 2022 18:42:41 +0000 (21:42 +0300)
CHANGES
dht-bootstrap.c

diff --git a/CHANGES b/CHANGES
index e8a770db4cda223d0af4c654c18d736e314fcec1..b1c71818b4da1edf134a75c3670384120a4c4131 100644 (file)
--- 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
index 9320ca79fe94249d92d914440c0b2c1ba57d7688..9b6f714a7324df36078c6006464eea1fcc3bfb9c 100644 (file)
@@ -25,18 +25,24 @@ THE SOFTWARE.
 #include <fcntl.h>
 #include <netdb.h>
 #include <netinet/in.h>
-#include <poll.h>
 #include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <sys/resource.h>
-#include <sys/socket.h>
 #include <time.h>
 #include <unistd.h>
 
+#include <sys/caprights.h>
+#include <sys/capsicum.h>
+#include <sys/event.h>
+#include <sys/resource.h>
+#include <sys/socket.h>
+
+#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;
                 }