]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
privsep: chroot the master process
authorRoy Marples <roy@marples.name>
Tue, 21 Jan 2020 16:17:18 +0000 (16:17 +0000)
committerRoy Marples <roy@marples.name>
Tue, 21 Jan 2020 16:17:18 +0000 (16:17 +0000)
This means that the privileged actioneer process needs to cleanup
sockets and pidfile.
It also has some reliance on how dhcpcd is started to create
a decent chroot area AND copy the configuration file to it.

src/control.c
src/dhcpcd.c
src/privsep-root.c
src/privsep-root.h
src/privsep.c
src/privsep.h

index eb43a93504d3a47492ba510b02db94fed71dfe63..2f10bc35e3939782b22cc1ccff20fa7ba3d84ced 100644 (file)
@@ -263,6 +263,24 @@ control_start(struct dhcpcd_ctx *ctx, const char *ifname)
        return ctx->control_fd;
 }
 
+static int
+control_unlink(struct dhcpcd_ctx *ctx, const char *file)
+{
+       int retval = 0;
+
+       errno = 0;
+#ifdef PRIVSEP
+       if (ctx->options & DHCPCD_PRIVSEP)
+               retval = (int)ps_root_unlink(ctx, file);
+       else
+#else
+               UNUSED(ctx);
+#endif
+               retval = unlink(file);
+
+       return retval == -1 && errno != ENOENT ? -1 : 0;
+}
+
 int
 control_stop(struct dhcpcd_ctx *ctx)
 {
@@ -276,7 +294,7 @@ control_stop(struct dhcpcd_ctx *ctx)
                eloop_event_delete(ctx->eloop, ctx->control_fd);
                close(ctx->control_fd);
                ctx->control_fd = -1;
-               if (unlink(ctx->control_sock) == -1 && errno != ENOENT)
+               if (control_unlink(ctx, ctx->control_sock) == -1)
                        retval = -1;
        }
 
@@ -284,7 +302,7 @@ control_stop(struct dhcpcd_ctx *ctx)
                eloop_event_delete(ctx->eloop, ctx->control_unpriv_fd);
                close(ctx->control_unpriv_fd);
                ctx->control_unpriv_fd = -1;
-               if (unlink(UNPRIVSOCKET) == -1 && errno != ENOENT)
+               if (control_unlink(ctx, UNPRIVSOCKET) == -1)
                        retval = -1;
        }
 
index f437977b70e5ebea8d054206908b597177ef0aed..ad2249c4608cd544f62932a397d4a58f49f30ccd 100644 (file)
@@ -2091,6 +2091,13 @@ printpidfile:
                goto exit_failure;
        }
 
+#ifdef PRIVSEP
+       if (ctx.options & DHCPCD_PRIVSEP && ps_dropprivs(&ctx) == -1) {
+               logerr("ps_dropprivs");
+               goto exit_failure;
+       }
+#endif
+
        setproctitle("%s%s%s",
            ctx.options & DHCPCD_MASTER ? "[master]" : argv[optind],
            ctx.options & DHCPCD_IPV4 ? " [ip4]" : "",
@@ -2223,13 +2230,13 @@ exit_failure:
        i = EXIT_FAILURE;
 
 exit1:
+       if (control_stop(&ctx) == -1)
+               logerr("%s: control_stop", __func__);
 #ifdef PRIVSEP
        ps_stop(&ctx);
 #endif
        if (ifaddrs != NULL)
                freeifaddrs(ifaddrs);
-       if (control_stop(&ctx) == -1)
-               logerr("%s: control_stop", __func__);
        /* Free memory and close fd's */
        if (ctx.ifaces) {
                while ((ifp = TAILQ_FIRST(ctx.ifaces))) {
index 4736362416236679eef831d55ad72bb0d5f48ffe..b6ba8af890268ed3af79bbb6d0872f5c6a9a0ca3 100644 (file)
@@ -198,6 +198,27 @@ ps_root_run_script(struct dhcpcd_ctx *ctx, const void *data, size_t len)
        return status;
 }
 
+static ssize_t
+ps_root_dounlink(void *data, size_t len)
+{
+       char *path = data;
+       size_t plen;
+
+       if (len < sizeof(plen)) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       memcpy(&plen, path, sizeof(plen));
+       path += sizeof(plen);
+       if (sizeof(plen) + plen > len) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       return (ssize_t)unlink(path);
+}
+
 static ssize_t
 ps_root_recvmsgcb(void *arg, struct ps_msghdr *psm, struct msghdr *msg)
 {
@@ -267,6 +288,9 @@ ps_root_recvmsgcb(void *arg, struct ps_msghdr *psm, struct msghdr *msg)
        case PS_SCRIPT:
                err = ps_root_run_script(ctx, data, len);
                break;
+       case PS_UNLINK:
+               err = ps_root_dounlink(data, len);
+               break;
        default:
                err = ps_root_os(psm, msg);
                break;
@@ -436,3 +460,24 @@ ps_root_ioctl(struct dhcpcd_ctx *ctx, ioctl_request_t req, void *data,
 #endif
        return ps_root_readerror(ctx);
 }
+
+ssize_t
+ps_root_unlink(struct dhcpcd_ctx *ctx, const char *path)
+{
+       char buf[PATH_MAX], *p = buf;
+       size_t plen = strlen(path) + 1;
+       size_t len = sizeof(plen) + plen;
+
+       if (len > sizeof(buf)) {
+               errno = ENOBUFS;
+               return -1;
+       }
+
+       memcpy(p, &plen, sizeof(plen));
+       p += sizeof(plen);
+       memcpy(p, path, plen);
+
+       if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_UNLINK, 0, buf, len) == -1)
+               return -1;
+       return ps_root_readerror(ctx);
+}
index 5ad19c96ed6de6d84a0910433500638b5e5f93b9..b4966bc913c87f9db9f27dd458abc5ab38979186 100644 (file)
@@ -36,6 +36,7 @@ int ps_root_stop(struct dhcpcd_ctx *ctx);
 
 ssize_t ps_root_readerror(struct dhcpcd_ctx *);
 ssize_t ps_root_ioctl(struct dhcpcd_ctx *, ioctl_request_t, void *, size_t);
+ssize_t ps_root_unlink(struct dhcpcd_ctx *, const char *);
 ssize_t ps_root_os(struct ps_msghdr *, struct msghdr *);
 #if defined(BSD) || defined(__sun)
 ssize_t ps_root_route(struct dhcpcd_ctx *, void *, size_t);
index 8fe7256a9fd2d44c42e262475ff2072e6be2f12f..e9ec25f21c2b0dd8fccb4b2f0623dee85f4087b1 100644 (file)
@@ -72,7 +72,6 @@ int
 ps_init(struct dhcpcd_ctx *ctx)
 {
        struct passwd *pw;
-       gid_t gid = (gid_t)-1;
 
        errno = 0;
        if ((pw = getpwnam(PRIVSEP_USER)) == NULL) {
@@ -86,16 +85,39 @@ ps_init(struct dhcpcd_ctx *ctx)
                return -1;
        }
 
+       ctx->options |= DHCPCD_PRIVSEP;
+       return 0;
+}
 
-       /* Change ownership of stuff we need to drop at exit. */
-       if (chown(ctx->pidfile, pw->pw_uid, gid) == -1)
-               logerr("chown `%s'", ctx->pidfile);
-       if (chown(DBDIR, pw->pw_uid, gid) == -1)
-               logerr("chown `%s'", DBDIR);
-       if (chown(RUNDIR, pw->pw_uid, gid) == -1)
-               logerr("chown `%s'", RUNDIR);
+int
+ps_dropprivs(struct dhcpcd_ctx *ctx)
+{
+       struct passwd *pw;
+
+       if ((pw = getpwnam(PRIVSEP_USER)) == NULL) {
+               if (errno == 0)
+                       logerrx("no such user %s", PRIVSEP_USER);
+               else
+                       logerr("getpwnam");
+               return -1;
+       }
+
+       if (!(ctx->options & DHCPCD_FORKED))
+               logdebugx("chrooting to `%s'", pw->pw_dir);
+
+       if (chroot(pw->pw_dir) == -1)
+               logerr("%s: chroot `%s'", __func__, pw->pw_dir);
+       if (chdir("/") == -1)
+               logerr("%s: chdir `/'", __func__);
+
+       if (setgroups(1, &pw->pw_gid) == -1 ||
+            setgid(pw->pw_gid) == -1 ||
+            setuid(pw->pw_uid) == -1)
+       {
+               logerr("failed to drop privileges");
+               return -1;
+       }
 
-       ctx->options |= DHCPCD_PRIVSEP;
        return 0;
 }
 
@@ -106,26 +128,10 @@ ps_dostart(struct dhcpcd_ctx *ctx,
     void *recv_ctx, int (*callback)(void *), void (*signal_cb)(int, void *),
     unsigned int flags)
 {
-       struct passwd *pw;
        int stype;
        int fd[2];
        pid_t pid;
 
-       if (flags & PSF_DROPPRIVS) {
-               errno = 0;
-               if ((pw = getpwnam(PRIVSEP_USER)) == NULL) {
-                       if (errno == 0)
-                               logerrx("no such user %s", PRIVSEP_USER);
-                       else
-                               logerr("getpwnam");
-                       return -1;
-               }
-       } else
-               pw = NULL;
-
-       if (priv_fd == NULL)
-               goto dropprivs;
-
        stype = SOCK_CLOEXEC | SOCK_NONBLOCK;
        if (socketpair(AF_UNIX, SOCK_DGRAM | stype, 0, fd) == -1) {
                logerr("socketpair");
@@ -199,25 +205,8 @@ ps_dostart(struct dhcpcd_ctx *ctx,
        freopen(_PATH_DEVNULL, "w", stdout);
        freopen(_PATH_DEVNULL, "w", stderr);
 
-       if (pw == NULL)
-               return 0;
-
-       if (!(flags & PSF_DROPPRIVS)) {
-               if (chdir(pw->pw_dir) == -1)
-                       logerr("%s: chdir `%s'", __func__, pw->pw_dir);
-               return 0;
-       }
-
-       if (chroot(pw->pw_dir) == -1)
-               logerr("%s: chroot `%s'", __func__, pw->pw_dir);
-       if (chdir("/") == -1)
-               logerr("%s: chdir `/'", __func__);
-
-dropprivs:
-       if (setgroups(1, &pw->pw_gid) == -1 ||
-           setgid(pw->pw_gid) == -1 ||
-           setuid(pw->pw_uid) == -1)
-               logerr("failed to drop privileges");
+       if (flags & PSF_DROPPRIVS)
+               ps_dropprivs(ctx);
 
        return 0;
 
@@ -294,7 +283,7 @@ ps_start(struct dhcpcd_ctx *ctx)
        /* No point in spawning the generic network listener if we're
         * not going to use it. */
        if (!(ctx->options & (DHCPCD_MASTER | DHCPCD_IPV6RS)))
-               goto dropprivs;
+               goto started;
 
        switch (pid = ps_inet_start(ctx)) {
        case -1:
@@ -307,11 +296,7 @@ ps_start(struct dhcpcd_ctx *ctx)
                logdebugx("spawned network proxy on PID %d", pid);
        }
 
-dropprivs:
-       /* Drop privs now. */
-       ps_dostart(ctx, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-           PSF_DROPPRIVS);
-
+started:
        return 1;
 }
 
@@ -320,12 +305,19 @@ ps_stop(struct dhcpcd_ctx *ctx)
 {
        int r, ret = 0;
 
-       if (ctx->options & DHCPCD_FORKED || ctx->eloop == NULL)
+       if (!(ctx->options & DHCPCD_PRIVSEP) ||
+           ctx->options & DHCPCD_FORKED ||
+           ctx->eloop == NULL)
                return 0;
 
        r = ps_inet_stop(ctx);
        if (r != 0)
                ret = r;
+
+       /* We've been chrooted, so we need to tell the
+        * priviledged actioneer to remove the pidfile. */
+       ps_root_unlink(ctx, ctx->pidfile);
+
        r = ps_root_stop(ctx);
        if (r != 0)
                ret = r;
index d277b22ff33a86529c8d9e95ae590fb8e9d6239c..babcb37c056e55503a63f10e3b06ad85ecfb7057 100644 (file)
 #define        PS_BPF_ARP_ADDR         0x06
 
 #define        PS_IOCTL                0x10
-#define        PS_SCRIPT               0x11
-#define        PS_IOCTLLINK            0x12
-#define        PS_IOCTL6               0x13
-#define        PS_ROUTE                0x14    /* Also used for NETLINK */
-#define        PS_WRITEPATHUINT        0x15
+#define        PS_ROUTE                0x11    /* Also used for NETLINK */
+#define        PS_SCRIPT               0x12
+#define        PS_UNLINK               0x13
+
+/* BSD Commands */
+#define        PS_IOCTLLINK            0x14
+#define        PS_IOCTL6               0x15
+
+/* Linux commands */
+#define        PS_WRITEPATHUINT        0x16
 
 #define        PS_DELETE               0x20
 #define        PS_START                0x40
@@ -132,6 +137,7 @@ TAILQ_HEAD(ps_process_head, ps_process);
 #endif
 
 int ps_init(struct dhcpcd_ctx *);
+int ps_dropprivs(struct dhcpcd_ctx *);
 int ps_start(struct dhcpcd_ctx *);
 int ps_stop(struct dhcpcd_ctx *);
 
@@ -153,7 +159,7 @@ pid_t ps_dostart(struct dhcpcd_ctx * ctx,
     pid_t *priv_pid, int *priv_fd,
     void (*recv_msg)(void *), void (*recv_unpriv_msg),
     void *recv_ctx, int (*callback)(void *), void (*)(int, void *),
-    unsigned int flags);
+    unsigned int);
 int ps_dostop(struct dhcpcd_ctx *ctx, pid_t *pid, int *fd);
 
 struct ps_process *ps_findprocess(struct dhcpcd_ctx *, struct ps_id *);