]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
privsep: Implement a resource limited sandbox
authorRoy Marples <roy@marples.name>
Tue, 9 Jun 2020 17:25:18 +0000 (18:25 +0100)
committerRoy Marples <roy@marples.name>
Tue, 9 Jun 2020 17:25:18 +0000 (18:25 +0100)
For systems without Capsicum or Pledge we can create a resource
limited sandbox provided that either ppoll(2) or works with
RLIMIT_NOFILES set to zero.

As far as dhcpcd is concerned, that means Linux and Solaris
won't work with this, but NetBSD and DragonFlyBSD will.

To achieve this, a special control proxy process will be spawned
just to accept new connections over the control socket because
this *cannot* be limited by RLIMIT_NOFILES.

configure
src/Makefile
src/control.c
src/control.h
src/dhcpcd.c
src/dhcpcd.h
src/privsep-control.c [new file with mode: 0644]
src/privsep-control.h [new file with mode: 0644]
src/privsep.c
src/privsep.h
src/script.c

index b2532680652c4fbae46faccc6353528b94244b4b..4c947841f21fc736352eceb50f99561887a745cc 100755 (executable)
--- a/configure
+++ b/configure
@@ -580,16 +580,18 @@ if [ "$PRIVSEP" = yes ]; then
        echo "#ifndef PRIVSEP_USER" >>$CONFIG_H
        echo "#define PRIVSEP_USER               \"$PRIVSEP_USER\"" >>$CONFIG_H
        echo "#endif" >>$CONFIG_H
-       echo "DHCPCD_SRCS+=     privsep.c privsep-root.c privsep-inet.c" \
+       echo "PRIVSEP_SRCS=     privsep.c privsep-root.c privsep-inet.c" \
                >>$CONFIG_MK
        if [ -z "$INET" ] || [ "$INET" = yes ]; then
-               echo "DHCPCD_SRCS+=     privsep-bpf.c" >>$CONFIG_MK
+               echo "PRIVSEP_SRCS+=    privsep-bpf.c" >>$CONFIG_MK
        fi
        case "$OS" in
-       linux*)          echo "DHCPCD_SRCS+=    privsep-linux.c" >>$CONFIG_MK;;
-       solaris*|sunos*) echo "DHCPCD_SRCS+=    privsep-sun.c" >>$CONFIG_MK;;
-       *)               echo "DHCPCD_SRCS+=    privsep-bsd.c" >>$CONFIG_MK;;
+       linux*)          echo "PRIVSEP_SRCS+=   privsep-linux.c" >>$CONFIG_MK;;
+       solaris*|sunos*) echo "PRIVSEP_SRCS+=   privsep-sun.c" >>$CONFIG_MK;;
+       *)               echo "PRIVSEP_SRCS+=   privsep-bsd.c" >>$CONFIG_MK;;
        esac
+else
+       echo "PRIVSEP_SRCS=" >>$CONFIG_MK
 fi
 
 echo "Using compiler .. $CC"
@@ -622,16 +624,8 @@ fi
 [ "$CC" != cc ] && echo "CC=           $CC" >>$CONFIG_MK
 $CC --version | $SED -e '1!d'
 
-if [ -z "$EMBEDDED" -o "$EMBEDDED" = yes ]; then
-       echo "$DHCPCD_DEFS will be embedded in dhcpcd itself"
-       echo "DHCPCD_SRCS+=     dhcpcd-embedded.c" >>$CONFIG_MK
-else
-       echo "$DHCPCD_DEFS will be installed to $LIBEXECDIR"
-       echo "CPPFLAGS+=        -DEMBEDDED_CONFIG=\\\"$LIBEXECDIR/dhcpcd-definitions.conf\\\"" >>$CONFIG_MK
-       echo "EMBEDDEDINSTALL=  _embeddedinstall" >>$CONFIG_MK
-fi
-
 if [ "$PRIVSEP" = yes ]; then
+       PRIVSEP_CONTROLLER=true
        printf "Testing for capsicum ... "
        cat <<EOF >_capsicum.c
 #include <sys/capsicum.h>
@@ -642,6 +636,7 @@ EOF
        if $XCC _capsicum.c -o _capsicum 2>&3; then
                echo "yes"
                echo "#define   HAVE_CAPSICUM" >>$CONFIG_H
+               PRIVSEP_CONTROLLER=false
        else
                echo "no"
        fi
@@ -657,10 +652,25 @@ EOF
         if $XCC _pledge.c -o _pledge 2>&3; then
                 echo "yes"
                 echo "#define   HAVE_PLEDGE" >>$CONFIG_H
+               PRIVSEP_CONTROLLER=false
         else
                 echo "no"
         fi
         rm -f _pledge.c _pledge
+
+       if $PRIVSEP_CONTROLLER; then
+               echo "#define   PRIVSEP_CONTROLLER" >>$CONFIG_H
+               echo "PRIVSEP_SRCS+=    privsep-control.c" >>$CONFIG_MK
+       fi
+fi
+
+if [ -z "$EMBEDDED" -o "$EMBEDDED" = yes ]; then
+       echo "$DHCPCD_DEFS will be embedded in dhcpcd itself"
+       echo "DHCPCD_SRCS+=     dhcpcd-embedded.c" >>$CONFIG_MK
+else
+       echo "$DHCPCD_DEFS will be installed to $LIBEXECDIR"
+       echo "CPPFLAGS+=        -DEMBEDDED_CONFIG=\\\"$LIBEXECDIR/dhcpcd-definitions.conf\\\"" >>$CONFIG_MK
+       echo "EMBEDDEDINSTALL=  _embeddedinstall" >>$CONFIG_MK
 fi
 
 if [ "$OS" = linux ]; then
index 28fb6e35655502ca81d0d25df81c763a99ffee2a..930108da354b415afc2cae8c878d578e53f2a02a 100644 (file)
@@ -15,7 +15,7 @@ CSTD?=                c99
 CFLAGS+=       -std=${CSTD}
 CPPFLAGS+=     -I${TOP} -I${TOP}/src -I./crypt
 
-SRCS+=         ${DHCPCD_SRCS}
+SRCS+=         ${DHCPCD_SRCS} ${PRIVSEP_SRCS}
 DHCPCD_DEF?=   dhcpcd-definitions.conf
 DHCPCD_DEFS=   dhcpcd-definitions.conf dhcpcd-definitions-small.conf
 
index 6b4ecd214d720b5178a1a4f36e6cecf0b91b1aa5..2977423e59b41307eff0057200698f48b2c4aa2f 100644 (file)
@@ -64,7 +64,6 @@ control_queue_free(struct fd_list *fd)
                        free(fdp->data);
                free(fdp);
        }
-       fd->queue_len = 0;
 
 #ifdef CTL_FREE_LIST
        while ((fdp = TAILQ_FIRST(&fd->free_queue))) {
@@ -76,56 +75,118 @@ control_queue_free(struct fd_list *fd)
 #endif
 }
 
-static void
-control_delete(struct fd_list *fd)
+void
+control_free(struct fd_list *fd)
 {
 
+#ifdef PRIVSEP_CONTROLLER
+       if (fd->ctx->ps_control_client == fd)
+               fd->ctx->ps_control_client = NULL;
+#endif
+
+       eloop_event_remove_writecb(fd->ctx->eloop, fd->fd);
        TAILQ_REMOVE(&fd->ctx->control_fds, fd, next);
-       eloop_event_delete(fd->ctx->eloop, fd->fd);
-       close(fd->fd);
        control_queue_free(fd);
        free(fd);
 }
 
+void
+control_delete(struct fd_list *fd)
+{
+
+#ifdef PRIVSEP_CONTROLLER
+       if (IN_PRIVSEP_SE(fd->ctx))
+               return;
+#endif
+
+       eloop_event_delete(fd->ctx->eloop, fd->fd);
+       close(fd->fd);
+       control_free(fd);
+}
+
 static void
 control_handle_data(void *arg)
 {
        struct fd_list *fd = arg;
-       char buffer[1024], *e, *p, *argvp[255], **ap, *a;
+       char buffer[1024];
        ssize_t bytes;
-       size_t len;
-       int argc;
 
        bytes = read(fd->fd, buffer, sizeof(buffer) - 1);
+
        if (bytes == -1 || bytes == 0) {
                /* Control was closed or there was an error.
                 * Remove it from our list. */
                control_delete(fd);
                return;
        }
-       buffer[bytes] = '\0';
-       p = buffer;
-       e = buffer + bytes;
+
+#ifdef PRIVSEP_CONTROLLER
+       if (IN_PRIVSEP(fd->ctx)) {
+               ssize_t err;
+
+               fd->flags |= FD_SENDLEN;
+               err = ps_ctl_handleargs(fd, buffer, (size_t)bytes);
+               fd->flags &= ~FD_SENDLEN;
+               if (err == -1) {
+                       logerr(__func__);
+                       return;
+               }
+               if (err == 1 &&
+                   ps_ctl_sendargs(fd, buffer, (size_t)bytes) == -1) {
+                       logerr(__func__);
+                       control_delete(fd);
+               }
+               return;
+       }
+#endif
+
+       control_recvdata(fd, buffer, (size_t)bytes);
+}
+
+void
+control_recvdata(struct fd_list *fd, char *data, size_t len)
+{
+       char *p = data, *e;
+       char *argvp[255], **ap;
+       int argc;
 
        /* Each command is \n terminated
         * Each argument is NULL separated */
-       while (p < e) {
+       while (len != 0) {
                argc = 0;
                ap = argvp;
-               while (p < e) {
-                       argc++;
+               while (len != 0) {
+                       if (*p == '\0') {
+                               p++;
+                               len--;
+                               continue;
+                       }
+                       e = memchr(p, '\0', len);
+                       if (e == NULL) {
+                               errno = EINVAL;
+                               logerrx("%s: no terminator", __func__);
+                               return;
+                       }
                        if ((size_t)argc >= sizeof(argvp) / sizeof(argvp[0])) {
                                errno = ENOBUFS;
+                               logerrx("%s: no arg buffer", __func__);
                                return;
                        }
-                       a = *ap++ = p;
-                       len = strlen(p);
-                       p += len + 1;
-                       if (len && a[len - 1] == '\n') {
-                               a[len - 1] = '\0';
+                       *ap++ = p;
+                       argc++;
+                       e++;
+                       len -= (size_t)(e - p);
+                       p = e;
+                       e--;
+                       if (*(--e) == '\n') {
+                               *e = '\0';
                                break;
                        }
                }
+               if (argc == 0) {
+                       logerrx("%s: no args", __func__);
+                       continue;
+               }
                *ap = NULL;
                if (dhcpcd_handleargs(fd->ctx, fd, argc, argvp) == -1) {
                        logerr(__func__);
@@ -137,6 +198,26 @@ control_handle_data(void *arg)
        }
 }
 
+struct fd_list *
+control_new(struct dhcpcd_ctx *ctx, int fd, unsigned int flags)
+{
+       struct fd_list *l;
+
+       l = malloc(sizeof(*l));
+       if (l == NULL)
+               return NULL;
+
+       l->ctx = ctx;
+       l->fd = fd;
+       l->flags = flags;
+       TAILQ_INIT(&l->queue);
+#ifdef CTL_FREE_LIST
+       TAILQ_INIT(&l->free_queue);
+#endif
+       TAILQ_INSERT_TAIL(&ctx->control_fds, l, next);
+       return l;
+}
+
 static void
 control_handle1(struct dhcpcd_ctx *ctx, int lfd, unsigned int fd_flags)
 {
@@ -155,20 +236,19 @@ control_handle1(struct dhcpcd_ctx *ctx, int lfd, unsigned int fd_flags)
            fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1)
                goto error;
 
-       l = malloc(sizeof(*l));
+#ifdef PRIVSEP_CONTROLLER
+       if (IN_PRIVSEP(ctx) && !IN_PRIVSEP_SE(ctx))
+               ;
+       else
+#endif
+       fd_flags |= FD_SENDLEN;
+
+       l = control_new(ctx, fd, fd_flags);
        if (l == NULL)
                goto error;
 
-       l->ctx = ctx;
-       l->fd = fd;
-       l->flags = fd_flags;
-       TAILQ_INIT(&l->queue);
-       l->queue_len = 0;
-#ifdef CTL_FREE_LIST
-       TAILQ_INIT(&l->free_queue);
-#endif
-       TAILQ_INSERT_TAIL(&ctx->control_fds, l, next);
-       eloop_event_add(ctx->eloop, l->fd, control_handle_data, l);
+       if (eloop_event_add(ctx->eloop, l->fd, control_handle_data, l) == -1)
+               logerr(__func__);
        return;
 
 error:
@@ -193,6 +273,26 @@ control_handle_unpriv(void *arg)
        control_handle1(ctx, ctx->control_unpriv_fd, FD_UNPRIV);
 }
 
+static int
+make_path(char *path, size_t len, const char *ifname, sa_family_t family)
+{
+       const char *per;
+
+       switch(family) {
+       case AF_INET:
+               per = "-4";
+               break;
+       case AF_INET6:
+               per = "-6";
+               break;
+       default:
+               per = "";
+               break;
+       }
+       return snprintf(path, len, CONTROLSOCKET,
+           ifname ? ifname : "", ifname ? per : "", ifname ? "." : "");
+}
+
 static int
 make_sock(struct sockaddr_un *sa, const char *ifname, sa_family_t family,
     bool unpriv)
@@ -205,23 +305,8 @@ make_sock(struct sockaddr_un *sa, const char *ifname, sa_family_t family,
        sa->sun_family = AF_UNIX;
        if (unpriv)
                strlcpy(sa->sun_path, UNPRIVSOCKET, sizeof(sa->sun_path));
-       else {
-               const char *per;
-
-               switch(family) {
-               case AF_INET:
-                       per = "-4";
-                       break;
-               case AF_INET6:
-                       per = "-6";
-                       break;
-               default:
-                       per = "";
-                       break;
-               }
-               snprintf(sa->sun_path, sizeof(sa->sun_path), CONTROLSOCKET,
-                   ifname ? ifname : "", ifname ? per : "", ifname ? "." : "");
-       }
+       else
+               make_path(sa->sun_path, sizeof(sa->sun_path), ifname, family);
        return fd;
 }
 
@@ -272,6 +357,14 @@ control_start(struct dhcpcd_ctx *ctx, const char *ifname, sa_family_t family)
 {
        int fd;
 
+#ifdef PRIVSEP_CONTROLLER
+       if (IN_PRIVSEP_SE(ctx)) {
+               make_path(ctx->control_sock, sizeof(ctx->control_sock),
+                   ifname, family);
+               return 0;
+       }
+#endif
+
        if ((fd = control_start1(ctx, ifname, family, S_PRIV)) == -1)
                return -1;
 
@@ -313,8 +406,21 @@ control_stop(struct dhcpcd_ctx *ctx)
        int retval = 0;
        struct fd_list *l;
 
-       if (ctx->options & DHCPCD_FORKED)
-               return 0;
+       while ((l = TAILQ_FIRST(&ctx->control_fds)) != NULL) {
+               control_free(l);
+       }
+
+#ifdef PRIVSEP_CONTROLLER
+       if (IN_PRIVSEP_SE(ctx)) {
+               if (ps_root_unlink(ctx, ctx->control_sock) == -1)
+                       retval = -1;
+               if (ctx->options & DHCPCD_MASTER &&
+                   control_unlink(ctx, UNPRIVSOCKET) == -1)
+                       retval = -1;
+               return retval;
+       } else if (ctx->options & DHCPCD_FORKED)
+               return retval;
+#endif
 
        if (ctx->control_fd != -1) {
                eloop_event_delete(ctx->eloop, ctx->control_fd);
@@ -332,14 +438,6 @@ control_stop(struct dhcpcd_ctx *ctx)
                        retval = -1;
        }
 
-       while ((l = TAILQ_FIRST(&ctx->control_fds))) {
-               TAILQ_REMOVE(&ctx->control_fds, l, next);
-               eloop_event_delete(ctx->eloop, l->fd);
-               close(l->fd);
-               control_queue_free(l);
-               free(l);
-       }
-
        return retval;
 }
 
@@ -390,23 +488,31 @@ control_writeone(void *arg)
 {
        struct fd_list *fd;
        struct iovec iov[2];
+       int iov_len;
        struct fd_data *data;
 
        fd = arg;
        data = TAILQ_FIRST(&fd->queue);
-       iov[0].iov_base = &data->data_len;
-       iov[0].iov_len = sizeof(size_t);
-       iov[1].iov_base = data->data;
-       iov[1].iov_len = data->data_len;
-       if (writev(fd->fd, iov, 2) == -1) {
-               logerr(__func__);
-               if (errno != EINTR && errno != EAGAIN)
-                       control_delete(fd);
+
+       if (data->data_flags & FD_SENDLEN) {
+               iov[0].iov_base = &data->data_len;
+               iov[0].iov_len = sizeof(size_t);
+               iov[1].iov_base = data->data;
+               iov[1].iov_len = data->data_len;
+               iov_len = 2;
+       } else {
+               iov[0].iov_base = data->data;
+               iov[0].iov_len = data->data_len;
+               iov_len = 1;
+       }
+
+       if (writev(fd->fd, iov, iov_len) == -1) {
+               logerr("%s: write", __func__);
+               control_delete(fd);
                return;
        }
 
        TAILQ_REMOVE(&fd->queue, data, next);
-       fd->queue_len--;
 #ifdef CTL_FREE_LIST
        TAILQ_INSERT_TAIL(&fd->free_queue, data, next);
 #else
@@ -415,30 +521,34 @@ control_writeone(void *arg)
        free(data);
 #endif
 
-       if (TAILQ_FIRST(&fd->queue) == NULL)
-               eloop_event_remove_writecb(fd->ctx->eloop, fd->fd);
+       if (TAILQ_FIRST(&fd->queue) != NULL)
+               return;
+
+       eloop_event_remove_writecb(fd->ctx->eloop, fd->fd);
+#ifdef PRIVSEP_CONTROLLER
+       if (IN_PRIVSEP_SE(fd->ctx) && !(fd->flags & FD_LISTEN)) {
+               if (ps_ctl_sendeof(fd) == -1)
+                       logerr(__func__);
+               control_free(fd);
+       }
+#endif
 }
 
 int
-control_queue(struct fd_list *fd, void *data, size_t data_len, bool fit)
+control_queue(struct fd_list *fd, void *data, size_t data_len)
 {
        struct fd_data *d;
 
-       if (data_len == 0)
-               return 0;
+       if (data_len == 0) {
+               errno = EINVAL;
+               return -1;
+       }
 
 #ifdef CTL_FREE_LIST
        struct fd_data *df;
 
        d = NULL;
        TAILQ_FOREACH(df, &fd->free_queue, next) {
-               if (!fit) {
-                       if (df->data_size == 0) {
-                               d = df;
-                               break;
-                       }
-                       continue;
-               }
                if (d == NULL || d->data_size < df->data_size) {
                        d = df;
                        if (d->data_size <= data_len)
@@ -450,28 +560,11 @@ control_queue(struct fd_list *fd, void *data, size_t data_len, bool fit)
        else
 #endif
        {
-               if (fd->queue_len == CONTROL_QUEUE_MAX) {
-                       errno = ENOBUFS;
-                       return -1;
-               }
-               fd->queue_len++;
                d = calloc(1, sizeof(*d));
                if (d == NULL)
                        return -1;
        }
 
-       if (!fit) {
-#ifdef CTL_FREE_LIST
-               if (d->data_size != 0) {
-                       free(d->data);
-                       d->data_size = 0;
-               }
-#endif
-               d->data = data;
-               d->data_len = data_len;
-               goto queue;
-       }
-
        if (d->data_size == 0)
                d->data = NULL;
        if (d->data_size < data_len) {
@@ -486,8 +579,8 @@ control_queue(struct fd_list *fd, void *data, size_t data_len, bool fit)
        }
        memcpy(d->data, data, data_len);
        d->data_len = data_len;
+       d->data_flags = fd->flags & FD_SENDLEN;
 
-queue:
        TAILQ_INSERT_TAIL(&fd->queue, d, next);
        eloop_event_add_w(fd->ctx->eloop, fd->fd, control_writeone, fd);
        return 0;
index a702af2f1cc66022ccf2953bf827016d62a49de1..606f94b69dd3168739db22a1d93e9201502f43ca 100644 (file)
@@ -47,6 +47,7 @@ struct fd_data {
        void *data;
        size_t data_size;
        size_t data_len;
+       unsigned int data_flags;
 };
 TAILQ_HEAD(fd_data_head, fd_data);
 
@@ -56,20 +57,23 @@ struct fd_list {
        int fd;
        unsigned int flags;
        struct fd_data_head queue;
-       size_t queue_len;
 #ifdef CTL_FREE_LIST
        struct fd_data_head free_queue;
 #endif
 };
 TAILQ_HEAD(fd_list_head, fd_list);
 
-#define FD_LISTEN      (1<<0)
-#define FD_UNPRIV      (1<<1)
+#define        FD_LISTEN       0x01U
+#define        FD_UNPRIV       0x02U
+#define        FD_SENDLEN      0x04U
 
 int control_start(struct dhcpcd_ctx *, const char *, sa_family_t);
 int control_stop(struct dhcpcd_ctx *);
 int control_open(const char *, sa_family_t, bool);
 ssize_t control_send(struct dhcpcd_ctx *, int, char * const *);
-int control_queue(struct fd_list *, void *, size_t, bool);
-
+struct fd_list *control_new(struct dhcpcd_ctx *, int, unsigned int);
+void control_free(struct fd_list *);
+void control_delete(struct fd_list *);
+int control_queue(struct fd_list *, void *, size_t);
+void control_recvdata(struct fd_list *fd, char *, size_t);
 #endif
index 905a102948cffa8257a9f24c4ac4f58e9bf101f0..234761c69da240df5d465d65209b06a30416e754 100644 (file)
@@ -1400,6 +1400,11 @@ dhcpcd_signal_cb(int sig, void *arg)
        unsigned long long opts;
        int exit_code;
 
+       if (ctx->options & DHCPCD_DUMPLEASE) {
+               eloop_exit(ctx->eloop, EXIT_FAILURE);
+               return;
+       }
+
        if (sig != SIGCHLD && ctx->options & DHCPCD_FORKED) {
                pid_t pid = pidfile_read(ctx->pidfile);
                if (pid == -1) {
@@ -1462,48 +1467,6 @@ dhcpcd_signal_cb(int sig, void *arg)
 }
 #endif
 
-static void
-dhcpcd_getinterfaces(void *arg)
-{
-       struct fd_list *fd = arg;
-       struct interface *ifp;
-       size_t len;
-
-       len = 0;
-       TAILQ_FOREACH(ifp, fd->ctx->ifaces, next) {
-               if (!ifp->active)
-                       continue;
-               len++;
-#ifdef INET
-               if (D_STATE_RUNNING(ifp))
-                       len++;
-#endif
-#ifdef IPV4LL
-               if (IPV4LL_STATE_RUNNING(ifp))
-                       len++;
-#endif
-#ifdef INET6
-               if (IPV6_STATE_RUNNING(ifp))
-                       len++;
-               if (RS_STATE_RUNNING(ifp))
-                       len++;
-#endif
-#ifdef DHCP6
-               if (D6_STATE_RUNNING(ifp))
-                       len++;
-#endif
-       }
-       if (write(fd->fd, &len, sizeof(len)) != sizeof(len))
-               return;
-       eloop_event_remove_writecb(fd->ctx->eloop, fd->fd);
-       TAILQ_FOREACH(ifp, fd->ctx->ifaces, next) {
-               if (!ifp->active)
-                       continue;
-               if (send_interface(fd, ifp, AF_UNSPEC) == -1)
-                       logerr(__func__);
-       }
-}
-
 int
 dhcpcd_handleargs(struct dhcpcd_ctx *ctx, struct fd_list *fd,
     int argc, char **argv)
@@ -1511,23 +1474,23 @@ dhcpcd_handleargs(struct dhcpcd_ctx *ctx, struct fd_list *fd,
        struct interface *ifp;
        unsigned long long opts;
        int opt, oi, do_reboot, do_renew, af = AF_UNSPEC;
-       size_t len, l;
+       size_t len, l, nifaces;
        char *tmp, *p;
 
        /* Special commands for our control socket
         * as the other end should be blocking until it gets the
         * expected reply we should be safely able just to change the
         * write callback on the fd */
+       /* Make any change here in privsep-control.c as well. */
        if (strcmp(*argv, "--version") == 0) {
                return control_queue(fd, UNCONST(VERSION),
-                   strlen(VERSION) + 1, false);
+                   strlen(VERSION) + 1);
        } else if (strcmp(*argv, "--getconfigfile") == 0) {
                return control_queue(fd, UNCONST(fd->ctx->cffile),
-                   strlen(fd->ctx->cffile) + 1, false);
+                   strlen(fd->ctx->cffile) + 1);
        } else if (strcmp(*argv, "--getinterfaces") == 0) {
-               eloop_event_add_w(fd->ctx->eloop, fd->fd,
-                   dhcpcd_getinterfaces, fd);
-               return 0;
+               optind = argc = 0;
+               goto dumplease;
        } else if (strcmp(*argv, "--listen") == 0) {
                fd->flags |= FD_LISTEN;
                return 0;
@@ -1591,8 +1554,8 @@ dhcpcd_handleargs(struct dhcpcd_ctx *ctx, struct fd_list *fd,
 
        if (opts & DHCPCD_DUMPLEASE) {
                ctx->options |= DHCPCD_DUMPLEASE;
-               size_t nifaces = 0;
-
+dumplease:
+               nifaces = 0;
                TAILQ_FOREACH(ifp, ctx->ifaces, next) {
                        if (!ifp->active)
                                continue;
@@ -1672,68 +1635,113 @@ dumperr:
        return 0;
 }
 
-static int
-dhcpcd_readdump(struct dhcpcd_ctx *ctx)
+static void dhcpcd_readdump1(void *);
+
+static void
+dhcpcd_readdump2(void *arg)
 {
-       int error = 0;
-       size_t nifaces, buflen = 0, dlen;
+       struct dhcpcd_ctx *ctx = arg;
        ssize_t len;
-       char *buf = NULL;
+       int exit_code = EXIT_FAILURE;
 
-again1:
-       len = read(ctx->control_fd, &nifaces, sizeof(nifaces));
+       len = read(ctx->control_fd, ctx->ctl_buf + ctx->ctl_bufpos,
+           ctx->ctl_buflen - ctx->ctl_bufpos);
        if (len == -1) {
-               if (errno == EAGAIN)
-                       goto again1;
-               return -1;
+               logerr(__func__);
+               goto finished;
+       } else if (len == 0)
+               goto finished;
+       if ((size_t)len + ctx->ctl_bufpos != ctx->ctl_buflen) {
+               ctx->ctl_bufpos += (size_t)len;
+               return;
        }
-       if (len != sizeof(nifaces)) {
-               errno = EINVAL;
-               return -1;
+
+       script_dump(ctx->ctl_buf, ctx->ctl_buflen);
+       fflush(stdout);
+       if (--ctx->ctl_extra != 0) {
+               putchar('\n');
+               eloop_event_add(ctx->eloop, ctx->control_fd,
+                   dhcpcd_readdump1, ctx);
+               return;
        }
-       for (; nifaces > 0; nifaces--) {
-again2:
-               len = read(ctx->control_fd, &dlen, sizeof(dlen));
-               if (len == -1) {
-                       if (errno == EAGAIN)
-                               goto again2;
-                       error = -1;
-                       goto out;
-               }
-               if (len != sizeof(dlen)) {
+       exit_code = EXIT_SUCCESS;
+
+finished:
+       shutdown(ctx->control_fd, SHUT_RDWR);
+       eloop_exit(ctx->eloop, exit_code);
+}
+
+static void
+dhcpcd_readdump1(void *arg)
+{
+       struct dhcpcd_ctx *ctx = arg;
+       ssize_t len;
+
+       len = read(ctx->control_fd, &ctx->ctl_buflen, sizeof(ctx->ctl_buflen));
+       if (len != sizeof(ctx->ctl_buflen)) {
+               if (len != -1)
                        errno = EINVAL;
-                       goto out;
-               }
-               if (dlen > buflen) {
-                       char *nbuf = realloc(buf, dlen);
-                       if (nbuf == NULL) {
-                               error = -1;
-                               goto out;
-                       }
-                       buf = nbuf;
-                       buflen = dlen;
-               }
-               if (dlen == 0) {
+               goto err;
+       }
+
+       free(ctx->ctl_buf);
+       ctx->ctl_buf = malloc(ctx->ctl_buflen);
+       if (ctx->ctl_buf == NULL)
+               goto err;
+
+       ctx->ctl_bufpos = 0;
+       eloop_event_add(ctx->eloop, ctx->control_fd,
+           dhcpcd_readdump2, ctx);
+       return;
+
+err:
+       logerr(__func__);
+       eloop_exit(ctx->eloop, EXIT_FAILURE);
+}
+
+static void
+dhcpcd_readdump0(void *arg)
+{
+       struct dhcpcd_ctx *ctx = arg;
+       ssize_t len;
+
+       len = read(ctx->control_fd, &ctx->ctl_extra, sizeof(ctx->ctl_extra));
+       if (len != sizeof(ctx->ctl_extra)) {
+               if (len != -1)
                        errno = EINVAL;
-                       error = -1;
-                       goto out;
-               }
-again3:
-               if (read(ctx->control_fd, buf, dlen) != (ssize_t)dlen) {
-                       if (errno == EAGAIN)
-                               goto again3;
-                       error = -1;
-                       goto out;
-               }
-               script_dump(buf, dlen);
-               fflush(stdout);
-               if (nifaces != 1)
-                       putchar('\n');
+               logerr(__func__);
+               eloop_exit(ctx->eloop, EXIT_FAILURE);
+               return;
        }
 
-out:
-       free(buf);
-       return error;
+       if (ctx->ctl_extra == 0) {
+               eloop_exit(ctx->eloop, EXIT_SUCCESS);
+               return;
+       }
+
+       eloop_event_add(ctx->eloop, ctx->control_fd,
+           dhcpcd_readdump1, ctx);
+}
+
+static void
+dhcpcd_readdumptimeout(void *arg)
+{
+       struct dhcpcd_ctx *ctx = arg;
+
+       logerrx(__func__);
+       eloop_exit(ctx->eloop, EXIT_FAILURE);
+}
+
+static int
+dhcpcd_readdump(struct dhcpcd_ctx *ctx)
+{
+
+       ctx->options |= DHCPCD_FORKED;
+       if (eloop_timeout_add_sec(ctx->eloop, 5,
+           dhcpcd_readdumptimeout, ctx) == -1)
+               return -1;
+       return eloop_event_add(ctx->eloop, ctx->control_fd,
+           dhcpcd_readdump0, ctx);
 }
 
 static void
@@ -1844,6 +1852,9 @@ main(int argc, char **argv)
 #endif
 #ifdef PRIVSEP
        ctx.ps_root_fd = ctx.ps_data_fd = -1;
+#ifdef PRIVSEP_COMTROLLER
+       ctx.ps_ctl_fd = -1;
+#endif
        TAILQ_INIT(&ctx.ps_processes);
 #endif
        rt_init(&ctx);
@@ -2156,6 +2167,7 @@ printpidfile:
                                        logerr("%s: dhcpcd_readdump", __func__);
                                        goto exit_failure;
                                }
+                               goto run_loop;
                        }
                        goto exit_success;
                } else {
@@ -2166,6 +2178,8 @@ printpidfile:
                                        logerrx("dhcpcd is not running");
                                goto exit_failure;
                        }
+                       if (errno == EPERM || errno == EACCES)
+                               goto exit_failure;
                }
                ctx.options &= ~DHCPCD_FORKED;
        }
@@ -2260,7 +2274,7 @@ printpidfile:
        }
 
 #ifdef PRIVSEP
-       if (ctx.options & DHCPCD_PRIVSEP && ps_start(&ctx) == -1) {
+       if (IN_PRIVSEP(&ctx) && ps_start(&ctx) == -1) {
                logerr("ps_start");
                goto exit_failure;
        }
@@ -2268,12 +2282,14 @@ printpidfile:
                goto run_loop;
 #endif
 
-       if (!(ctx.options & DHCPCD_TEST) &&
-           control_start(&ctx,
-           ctx.options & DHCPCD_MASTER ? NULL : argv[optind], family) == -1)
-       {
-               logerr("%s: control_start", __func__);
-               goto exit_failure;
+       if (!(ctx.options & DHCPCD_TEST)) {
+               if (control_start(&ctx,
+                   ctx.options & DHCPCD_MASTER ?
+                   NULL : argv[optind], family) == -1)
+               {
+                       logerr("%s: control_start", __func__);
+                       goto exit_failure;
+               }
        }
 
 #ifdef PLUGIN_DEV
@@ -2479,6 +2495,7 @@ exit1:
                loginfox(PACKAGE " exited");
        logclose();
        free(ctx.logfile);
+       free(ctx.ctl_buf);
 #ifdef SETPROCTITLE_H
        setproctitle_free();
 #endif
index 21497559a112b00187310ff15c4d78f7c5b2aeb8..7ab95e8618a3990055d4fd7373a5e6e26f84bf73 100644 (file)
@@ -144,6 +144,11 @@ struct dhcpcd_ctx {
        size_t duid_len;
        struct if_head *ifaces;
 
+       char *ctl_buf;
+       size_t ctl_buflen;
+       size_t ctl_bufpos;
+       size_t ctl_extra;
+
        rb_tree_t routes;       /* our routes */
 #ifdef RT_FREE_ROUTE_TABLE
        rb_tree_t froutes;      /* free routes for re-use */
@@ -204,6 +209,13 @@ struct dhcpcd_ctx {
        struct ps_process_head ps_processes;    /* List of spawned processes */
        pid_t ps_inet_pid;
        int ps_inet_fd;         /* Network Proxy commands and data */
+#ifdef PRIVSEP_CONTROLLER
+       pid_t ps_control_pid;
+       int ps_control_fd;
+       int ps_control_data_fd;
+       struct fd_list *ps_control;
+       struct fd_list *ps_control_client;
+#endif
 #endif
 
 #ifdef INET
diff --git a/src/privsep-control.c b/src/privsep-control.c
new file mode 100644 (file)
index 0000000..114603a
--- /dev/null
@@ -0,0 +1,339 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+/*
+ * Privilege Separation for dhcpcd, control proxy
+ * Copyright (c) 2006-2020 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/resource.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "dhcpcd.h"
+#include "control.h"
+#include "eloop.h"
+#include "logerr.h"
+#include "privsep.h"
+
+int
+ps_ctl_limitresources(struct dhcpcd_ctx *ctx)
+{
+       struct rlimit rzero = { .rlim_cur = 0, .rlim_max = 0 };
+
+       if (ctx->ps_control_pid != getpid()) {
+               /* Prohibit new files, sockets, etc */
+               if (setrlimit(RLIMIT_NOFILE, &rzero) == -1) {
+                       logerr("setrlimit RLIMIT_NOFILE");
+                       return -1;
+               }
+       }
+
+       /* Prohibit large files */
+       if (setrlimit(RLIMIT_FSIZE, &rzero) == -1) {
+               logerr("setrlimit RLIMIT_FSIZE");
+               return -1;
+       }
+
+       /* Prohibit forks */
+       if (setrlimit(RLIMIT_NPROC, &rzero) == -1) {
+               logerr("setrlimit RLIMIT_NPROC");
+               return -1;
+       }
+
+       return 0;
+}
+
+static int
+ps_ctl_startcb(void *arg)
+{
+       struct dhcpcd_ctx *ctx = arg;
+       sa_family_t af;
+
+       if (ctx->options & DHCPCD_MASTER) {
+               setproctitle("[control proxy]");
+               af = AF_UNSPEC;
+       } else {
+               setproctitle("[control proxy] %s%s%s",
+                   ctx->ifv[0],
+                   ctx->options & DHCPCD_IPV4 ? " [ip4]" : "",
+                   ctx->options & DHCPCD_IPV6 ? " [ip6]" : "");
+               if ((ctx->options &
+                   (DHCPCD_IPV4 | DHCPCD_IPV6)) == DHCPCD_IPV4)
+                       af = AF_INET;
+               else if ((ctx->options &
+                   (DHCPCD_IPV4 | DHCPCD_IPV6)) == DHCPCD_IPV6)
+                       af = AF_INET6;
+               else
+                       af = AF_UNSPEC;
+       }
+
+       ctx->ps_control_pid = getpid();
+
+       return control_start(ctx,
+           ctx->options & DHCPCD_MASTER ? NULL : *ctx->ifv, af);
+}
+
+static ssize_t
+ps_ctl_recvmsgcb(void *arg, struct ps_msghdr *psm, __unused struct msghdr *msg)
+{
+       struct dhcpcd_ctx *ctx = arg;
+
+       if (psm->ps_cmd != PS_CTL_EOF) {
+               errno = ENOTSUP;
+               return -1;
+       }
+
+       if (ctx->ps_control_client != NULL)
+               ctx->ps_control_client = NULL;
+       return 0;
+}
+
+static void
+ps_ctl_recvmsg(void *arg)
+{
+       struct dhcpcd_ctx *ctx = arg;
+
+       if (ps_recvpsmsg(ctx, ctx->ps_control_fd, ps_ctl_recvmsgcb, ctx) == -1)
+               logerr(__func__);
+}
+
+static void
+ps_ctl_signalcb(int sig, void *arg)
+{
+       struct dhcpcd_ctx *ctx = arg;
+
+       /* Ignore SIGINT, respect PS_STOP command or SIGTERM. */
+       if (sig == SIGINT)
+               return;
+
+       shutdown(ctx->ps_control_fd, SHUT_RDWR);
+       eloop_exit(ctx->eloop, sig == SIGTERM ? EXIT_SUCCESS : EXIT_FAILURE);
+}
+
+ssize_t
+ps_ctl_handleargs(struct fd_list *fd, char *data, size_t len)
+{
+
+       /* Make any change here in dhcpcd.c as well. */
+       if (strncmp(data, "--version",
+           MIN(strlen("--version"), len)) == 0) {
+               return control_queue(fd, UNCONST(VERSION),
+                   strlen(VERSION) + 1);
+       } else if (strncmp(data, "--getconfigfile",
+           MIN(strlen("--getconfigfile"), len)) == 0) {
+               return control_queue(fd, UNCONST(fd->ctx->cffile),
+                   strlen(fd->ctx->cffile) + 1);
+       } else if (strncmp(data, "--listen",
+           MIN(strlen("--listen"), len)) == 0) {
+               fd->flags |= FD_LISTEN;
+               return 0;
+       }
+
+       if (fd->ctx->ps_control_client != NULL &&
+           fd->ctx->ps_control_client != fd)
+       {
+               logerrx("%s: cannot handle another client", __func__);
+               return 0;
+       }
+       return 1;
+}
+
+static ssize_t
+ps_ctl_dispatch(void *arg, struct ps_msghdr *psm, struct msghdr *msg)
+{
+       struct dhcpcd_ctx *ctx = arg;
+       struct iovec *iov = msg->msg_iov;
+       struct fd_list *fd;
+       unsigned int fd_flags = FD_SENDLEN;
+
+       switch (psm->ps_flags) {
+       case PS_CTL_PRIV:
+               break;
+       case PS_CTL_UNPRIV:
+               fd_flags |= FD_UNPRIV;
+               break;
+       }
+
+       switch (psm->ps_cmd) {
+       case PS_CTL:
+               if (msg->msg_iovlen != 1) {
+                       errno = EINVAL;
+                       return -1;
+               }
+               if (ctx->ps_control_client != NULL) {
+                       logerrx("%s: cannot handle another client", __func__);
+                       return 0;
+               }
+               fd = control_new(ctx, ctx->ps_control_data_fd, fd_flags);
+               if (fd == NULL)
+                       return -1;
+               ctx->ps_control_client = fd;
+               control_recvdata(fd, iov->iov_base, iov->iov_len);
+               break;
+       case PS_CTL_EOF:
+               fd = ctx->ps_control_client;
+               control_free(ctx->ps_control_client);
+               break;
+       default:
+               errno = ENOTSUP;
+               return -1;
+       }
+       return 0;
+}
+
+static void
+ps_ctl_dodispatch(void *arg)
+{
+       struct dhcpcd_ctx *ctx = arg;
+
+       if (ps_recvpsmsg(ctx, ctx->ps_control_fd, ps_ctl_dispatch, ctx) == -1)
+               logerr(__func__);
+}
+
+static void
+ps_ctl_recv(void *arg)
+{
+       struct dhcpcd_ctx *ctx = arg;
+       char buf[BUFSIZ];
+       ssize_t len;
+
+       errno = 0;
+       len = read(ctx->ps_control_data_fd, buf, sizeof(buf));
+       if (len == -1 || len == 0) {
+               logerr("%s: read", __func__);
+               eloop_exit(ctx->eloop, EXIT_FAILURE);
+               return;
+       }
+       if (ctx->ps_control_client == NULL) /* client disconnected */
+               return;
+       errno = 0;
+       if (control_queue(ctx->ps_control_client, buf, (size_t)len) == -1)
+               logerr("%s: control_queue", __func__);
+}
+
+static void
+ps_ctl_listen(void *arg)
+{
+       struct dhcpcd_ctx *ctx = arg;
+       char buf[BUFSIZ];
+       ssize_t len;
+       struct fd_list *fd;
+
+       errno = 0;
+       len = read(ctx->ps_control->fd, buf, sizeof(buf));
+       if (len == -1 || len == 0) {
+               logerr("%s: read", __func__);
+               eloop_exit(ctx->eloop, EXIT_FAILURE);
+               return;
+       }
+
+       /* Send to our listeners */
+       TAILQ_FOREACH(fd, &ctx->control_fds, next) {
+               if (!(fd->flags & FD_LISTEN))
+                       continue;
+               if (control_queue(fd, buf, (size_t)len)== -1)
+                       logerr("%s: control_queue", __func__);
+       }
+}
+
+pid_t
+ps_ctl_start(struct dhcpcd_ctx *ctx)
+{
+       int data_fd[2], listen_fd[2];
+       pid_t pid;
+
+       if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CXNB, 0, data_fd) == -1)
+               return -1;
+       if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CXNB, 0, listen_fd) == -1)
+               return -1;
+#ifdef PRIVSEP_RIGHTS
+       if (ps_rights_limit_fdpair(data_fd) == -1)
+               return -1;
+       if (ps_rights_limit_fdpair(listen_fd) == -1)
+               return -1;
+#endif
+
+       pid = ps_dostart(ctx, &ctx->ps_control_pid, &ctx->ps_control_fd,
+           ps_ctl_recvmsg, ps_ctl_dodispatch, ctx,
+           ps_ctl_startcb, ps_ctl_signalcb,
+           PSF_DROPPRIVS);
+
+       if (pid != 0) {
+               ctx->ps_control_data_fd = data_fd[1];
+               close(data_fd[0]);
+               ctx->ps_control = control_new(ctx,
+                   listen_fd[1], FD_SENDLEN | FD_LISTEN);
+               if (ctx->ps_control == NULL)
+                       return -1;
+               close(listen_fd[0]);
+               return pid;
+       } else if (pid == -1)
+               return -1;
+
+       ctx->ps_control_data_fd = data_fd[0];
+       close(data_fd[1]);
+       if (eloop_event_add(ctx->eloop, ctx->ps_control_data_fd,
+           ps_ctl_recv, ctx) == -1)
+               return -1;
+
+       ctx->ps_control = control_new(ctx,
+           listen_fd[0], 0);
+       close(listen_fd[1]);
+       if (ctx->ps_control == NULL)
+               return -1;
+       if (eloop_event_add(ctx->eloop, ctx->ps_control->fd,
+           ps_ctl_listen, ctx) == -1)
+               return -1;
+       return 0;
+}
+
+int
+ps_ctl_stop(struct dhcpcd_ctx *ctx)
+{
+
+       return ps_dostop(ctx, &ctx->ps_control_pid, &ctx->ps_control_fd);
+}
+
+ssize_t
+ps_ctl_sendargs(struct fd_list *fd, void *data, size_t len)
+{
+       struct dhcpcd_ctx *ctx = fd->ctx;
+
+       if (ctx->ps_control_client != NULL && ctx->ps_control_client != fd)
+               logerrx("%s: cannot deal with another client", __func__);
+       ctx->ps_control_client = fd;
+       return ps_sendcmd(ctx, ctx->ps_control_fd, PS_CTL,
+           fd->flags & FD_UNPRIV ? PS_CTL_UNPRIV : PS_CTL_PRIV,
+           data, len);
+}
+
+ssize_t
+ps_ctl_sendeof(struct fd_list *fd)
+{
+       struct dhcpcd_ctx *ctx = fd->ctx;
+
+       return ps_sendcmd(ctx, ctx->ps_control_fd, PS_CTL_EOF, 0, NULL, 0);
+}
diff --git a/src/privsep-control.h b/src/privsep-control.h
new file mode 100644 (file)
index 0000000..7f0e473
--- /dev/null
@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+/*
+ * Privilege Separation for dhcpcd
+ * Copyright (c) 2006-2020 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef PRIVSEP_CTL_H
+#define PRIVSEP_CTL_H
+
+#define IN_PRIVSEP_CONTROLLER(ctx) \
+    (IN_PRIVSEP((ctx)) && (ctx)->ps_control_pid == getpid())
+
+int ps_ctl_limitresources(struct dhcpcd_ctx *);
+pid_t ps_ctl_start(struct dhcpcd_ctx *);
+int ps_ctl_stop(struct dhcpcd_ctx *);
+ssize_t ps_ctl_handleargs(struct fd_list *, char *, size_t);
+ssize_t ps_ctl_sendargs(struct fd_list *, void *, size_t);
+ssize_t ps_ctl_sendeof(struct fd_list *fd);
+
+#endif
index 3b8368938a3373dd522ee824b55f4f5eb94766d7..a4b03fde255f7ef5f8f37c1b9664bcb63c4017c1 100644 (file)
@@ -39,7 +39,6 @@
  * this in a script or something.
  */
 
-#include <sys/resource.h>
 #include <sys/socket.h>
 #include <sys/stat.h>
 #include <sys/types.h>
@@ -129,36 +128,11 @@ ps_dropprivs(struct dhcpcd_ctx *ctx)
                return -1;
        }
 
-#if defined(HAVE_CAPSICUM) || defined(HAVE_PLEDGE)
-       /* Resource limits are not needed for these sandboxes */
-#else
-       struct rlimit rzero = { .rlim_cur = 0, .rlim_max = 0 };
-
-       /* We can't use RLIMIT_NOFILE because that breaks our control socket.
-        * XXX Offload to a new process? */
-#if 0
-#ifndef __linux__ /* breaks ppoll */
-       /* Prohibit new files, sockets, etc */
-       if (setrlimit(RLIMIT_NOFILE, &rzero) == -1) {
-               logerr("setrlimit RLIMIT_NOFILE");
-               return -1;
-       }
-#endif
-#endif
-
-       /* Prohibit large files */
-       if (setrlimit(RLIMIT_FSIZE, &rzero) == -1) {
-               logerr("setrlimit RLIMIT_FSIZE");
+#ifdef PRIVSEP_CONTROLLER
+       if (ps_ctl_limitresources(ctx) == -1)
                return -1;
-       }
-
-#ifdef RLIMIT_NPROC
-       /* Prohibit forks */
-       if (setrlimit(RLIMIT_NPROC, &rzero) == -1) {
-               logerr("setrlimit RLIMIT_NPROC");
-               return -1;
-       }
-#endif
+#elif !defined(HAVE_CAPSIUM) && !defined(HAVE_PLEDGE)
+#warning No sandbox support
 #endif
 
        return 0;
@@ -437,6 +411,18 @@ ps_start(struct dhcpcd_ctx *ctx)
        }
 
 started:
+#ifdef PRIVSEP_CONTROLLER
+       if (!(ctx->options & DHCPCD_TEST)) {
+               switch (pid = ps_ctl_start(ctx)) {
+               case -1:
+                       return -1;
+               case 0:
+                       return 0;
+               default:
+                       logdebugx("spawned controller on PID %d", pid);
+               }
+       }
+#endif
 
 #ifdef ARC4RANDOM_H
        /* Seed the random number generator early incase it needs /dev/urandom
@@ -491,6 +477,12 @@ ps_stop(struct dhcpcd_ctx *ctx)
            ctx->eloop == NULL)
                return 0;
 
+#ifdef PRIVSEP_CONTROLLER
+       r = ps_ctl_stop(ctx);
+       if (r != 0)
+               ret = r;
+#endif
+
        r = ps_inet_stop(ctx);
        if (r != 0)
                ret = r;
index 5d7b4ec027606adf7e4dde66c367c21457091717..7428adc786d1392cb75c4f886bfe0af232861c0e 100644 (file)
@@ -50,6 +50,8 @@
 #define        PS_WRITEFILE            0x0015
 #define        PS_FILEMTIME            0x0016
 #define        PS_AUTH_MONORDM         0x0017
+#define        PS_CTL                  0x0018
+#define        PS_CTL_EOF              0x0019
 
 /* BSD Commands */
 #define        PS_IOCTLLINK            0x0101
 #define        PS_DEV_IFREMOVED        0x0002
 #define        PS_DEV_IFUPDATED        0x0003
 
+/* Control Type (via flags) */
+#define        PS_CTL_PRIV             0x0301
+#define        PS_CTL_UNPRIV           0x0302
+
 /* Process commands */
 #define        PS_START                0x4000
 #define        PS_STOP                 0x8000
@@ -157,6 +163,9 @@ TAILQ_HEAD(ps_process_head, ps_process);
 #ifdef INET
 #include "privsep-bpf.h"
 #endif
+#ifdef PRIVSEP_CONTROLLER
+#include "privsep-control.h"
+#endif
 
 int ps_init(struct dhcpcd_ctx *);
 int ps_dropprivs(struct dhcpcd_ctx *);
index 282b5ac2a830a6b49a84f241389f26a382fcbf82..126d9249655cf78d8a21bd76867a3dcd8dd596d4 100644 (file)
@@ -560,7 +560,7 @@ send_interface1(struct fd_list *fd, const struct interface *ifp,
        len = make_env(ifp->ctx, ifp, reason);
        if (len == -1)
                return -1;
-       return control_queue(fd, ctx->script_buf, (size_t)len, 1);
+       return control_queue(fd, ctx->script_buf, (size_t)len);
 }
 
 int
@@ -752,8 +752,7 @@ send_listeners:
        TAILQ_FOREACH(fd, &ctx->control_fds, next) {
                if (!(fd->flags & FD_LISTEN))
                        continue;
-               if (control_queue(fd, ctx->script_buf, ctx->script_buflen,
-                   true) == -1)
+               if (control_queue(fd, ctx->script_buf, ctx->script_buflen)== -1)
                        logerr("%s: control_queue", __func__);
                else
                        status = 1;