From: Roy Marples Date: Tue, 21 Jan 2020 16:17:18 +0000 (+0000) Subject: privsep: chroot the master process X-Git-Tag: v9.0.0~94 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d70199801ff5663bcfe0ee37a6aaae0b8dd7f9dc;p=thirdparty%2Fdhcpcd.git privsep: chroot the master process 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. --- diff --git a/src/control.c b/src/control.c index eb43a935..2f10bc35 100644 --- a/src/control.c +++ b/src/control.c @@ -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; } diff --git a/src/dhcpcd.c b/src/dhcpcd.c index f437977b..ad2249c4 100644 --- a/src/dhcpcd.c +++ b/src/dhcpcd.c @@ -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))) { diff --git a/src/privsep-root.c b/src/privsep-root.c index 47363624..b6ba8af8 100644 --- a/src/privsep-root.c +++ b/src/privsep-root.c @@ -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); +} diff --git a/src/privsep-root.h b/src/privsep-root.h index 5ad19c96..b4966bc9 100644 --- a/src/privsep-root.h +++ b/src/privsep-root.h @@ -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); diff --git a/src/privsep.c b/src/privsep.c index 8fe7256a..e9ec25f2 100644 --- a/src/privsep.c +++ b/src/privsep.c @@ -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; diff --git a/src/privsep.h b/src/privsep.h index d277b22f..babcb37c 100644 --- a/src/privsep.h +++ b/src/privsep.h @@ -43,11 +43,16 @@ #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 *);