From: Roy Marples Date: Wed, 17 Mar 2021 15:03:52 +0000 (+0000) Subject: privsep: Implement some process management X-Git-Tag: v10.0.0~93 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=07a6574c2d0eeca0582efc63a554b52eb11ee760;p=thirdparty%2Fdhcpcd.git privsep: Implement some process management This also allows us to wait until all processes have exited to avoid a fast restart which complains addresses are in use. --- diff --git a/src/dhcpcd.c b/src/dhcpcd.c index eae6e20b..3f4d41fe 100644 --- a/src/dhcpcd.c +++ b/src/dhcpcd.c @@ -1409,7 +1409,7 @@ dhcpcd_renew(struct dhcpcd_ctx *ctx) #ifdef USE_SIGNALS #define sigmsg "received %s, %s" -static void +void dhcpcd_signal_cb(int sig, void *arg) { struct dhcpcd_ctx *ctx = arg; @@ -1468,8 +1468,12 @@ dhcpcd_signal_cb(int sig, void *arg) logerr("logopen"); return; case SIGCHLD: +#ifdef PRIVSEP + ps_root_signalcb(sig, ctx); +#else while (waitpid(-1, NULL, WNOHANG) > 0) ; +#endif return; default: logerrx("received signal %d but don't know what to do with it", @@ -1918,8 +1922,7 @@ main(int argc, char **argv, char **envp) ctx.dhcp6_wfd = -1; #endif #ifdef PRIVSEP - ctx.ps_root_fd = ctx.ps_log_fd = ctx.ps_data_fd = -1; - ctx.ps_inet_fd = ctx.ps_control_fd = -1; + ctx.ps_log_fd = -1; TAILQ_INIT(&ctx.ps_processes); #endif @@ -2307,9 +2310,9 @@ printpidfile: if (!(ctx.options & DHCPCD_DAEMONISE)) goto start_manager; - if (xsocketpair(AF_UNIX, SOCK_DGRAM | SOCK_CXNB, 0, fork_fd) == -1 || + if (xsocketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CXNB, 0, fork_fd) == -1 || (ctx.stderr_valid && - xsocketpair(AF_UNIX, SOCK_DGRAM | SOCK_CXNB, 0, stderr_fd) == -1)) + xsocketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CXNB, 0, stderr_fd) == -1)) { logerr("socketpair"); goto exit_failure; @@ -2639,10 +2642,22 @@ exit1: #endif if (ctx.script != dhcpcd_default_script) free(ctx.script); - if (ctx.options & DHCPCD_STARTED && !(ctx.options & DHCPCD_FORKED)) +#ifdef PRIVSEP + if (ps_stopwait(&ctx) != EXIT_SUCCESS) + i = EXIT_FAILURE; +#endif + if (ctx.options & DHCPCD_STARTED && !(ctx.options & DHCPCD_FORKED)) { loginfox(PACKAGE " exited"); + +#ifdef PRIVSEP + /* Sleep some for the exited log entry to be written. */ + struct timespec ts = { .tv_nsec = 10 }; + nanosleep(&ts, NULL); +#endif + } #ifdef PRIVSEP - ps_root_stop(&ctx); + if (ps_root_stop(&ctx) == -1) + i = EXIT_FAILURE; eloop_free(ctx.ps_eloop); #endif eloop_free(ctx.eloop); diff --git a/src/dhcpcd.h b/src/dhcpcd.h index d7fb8164..1c4a401f 100644 --- a/src/dhcpcd.h +++ b/src/dhcpcd.h @@ -198,17 +198,14 @@ struct dhcpcd_ctx { #ifdef PRIVSEP struct passwd *ps_user; /* struct passwd for privsep user */ - pid_t ps_root_pid; - int ps_root_fd; /* Privileged Proxy commands */ + struct ps_process_head ps_processes; /* List of spawned processes */ + struct ps_process *ps_root; + struct ps_process *ps_inet; + struct ps_process *ps_ctl; + int ps_data_fd; /* data returned from processes */ int ps_log_fd; /* chroot logging */ - int ps_data_fd; /* Data from root spawned processes */ + int ps_log_root_fd; /* outside chroot log reader */ struct eloop *ps_eloop; /* eloop for polling root data */ - struct ps_process_head ps_processes; /* List of spawned processes */ - pid_t ps_inet_pid; - int ps_inet_fd; /* Network Proxy commands and data */ - pid_t ps_control_pid; - int ps_control_fd; /* Control Proxy - generic listener */ - int ps_control_data_fd; /* Control Proxy - data query */ struct fd_list *ps_control; /* Queue for the above */ struct fd_list *ps_control_client; /* Queue for the above */ #endif @@ -270,6 +267,8 @@ int dhcpcd_ifafwaiting(const struct interface *); int dhcpcd_afwaiting(const struct dhcpcd_ctx *); void dhcpcd_daemonise(struct dhcpcd_ctx *); +void dhcpcd_signal_cb(int, void *); + void dhcpcd_linkoverflow(struct dhcpcd_ctx *); int dhcpcd_handleargs(struct dhcpcd_ctx *, struct fd_list *, int, char **); void dhcpcd_handlecarrier(struct interface *, int, unsigned int); diff --git a/src/if-bsd.c b/src/if-bsd.c index e5ffe500..17802b38 100644 --- a/src/if-bsd.c +++ b/src/if-bsd.c @@ -1246,8 +1246,8 @@ if_rtm(struct dhcpcd_ctx *ctx, const struct rt_msghdr *rtm) /* Ignore messages from ourself. */ #ifdef PRIVSEP - if (ctx->ps_root_pid != 0) { - if (rtm->rtm_pid == ctx->ps_root_pid) + if (ctx->ps_root != NULL) { + if (rtm->rtm_pid == ctx->ps_root->psp_pid) return 0; } #endif @@ -1298,8 +1298,8 @@ if_ifa(struct dhcpcd_ctx *ctx, const struct ifa_msghdr *ifam) * We need to process address flag changes though. */ if (ifam->ifam_type == RTM_DELADDR) { #ifdef PRIVSEP - if (ctx->ps_root_pid != 0) { - if (ifam->ifam_pid == ctx->ps_root_pid) + if (ctx->ps_root != NULL) { + if (ifam->ifam_pid == ctx->ps_root->psp_pid) return 0; } else #endif diff --git a/src/privsep-bpf.c b/src/privsep-bpf.c index f0088568..b513d49e 100644 --- a/src/privsep-bpf.c +++ b/src/privsep-bpf.c @@ -144,9 +144,8 @@ ps_bpf_recvmsg(void *arg, unsigned short events) } static int -ps_bpf_start_bpf(void *arg) +ps_bpf_start_bpf(struct ps_process *psp) { - struct ps_process *psp = arg; struct dhcpcd_ctx *ctx = psp->psp_ctx; char *addr; struct in_addr *ia = &psp->psp_id.psi_addr.psa_in_addr; @@ -187,6 +186,8 @@ ps_bpf_cmd(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg) pid_t start; struct iovec *iov = msg->msg_iov; struct interface *ifp; + struct in_addr *ia = &psm->ps_id.psi_addr.psa_in_addr; + const char *addr; cmd = (uint16_t)(psm->ps_cmd & ~(PS_START | PS_STOP)); psp = ps_findprocess(ctx, &psm->ps_id); @@ -244,11 +245,16 @@ ps_bpf_cmd(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg) break; } - start = ps_dostart(ctx, - &psp->psp_pid, &psp->psp_fd, - ps_bpf_recvmsg, NULL, psp, - ps_bpf_start_bpf, NULL, - PSF_DROPPRIVS); + if (ia->s_addr == INADDR_ANY) + addr = NULL; + else + addr = inet_ntoa(*ia); + snprintf(psp->psp_name, sizeof(psp->psp_name), "BPF %s%s%s", + psp->psp_protostr, + addr != NULL ? " " : "", addr != NULL ? addr : ""); + + start = ps_startprocess(psp, ps_bpf_recvmsg, NULL, + ps_bpf_start_bpf, NULL, PSF_DROPPRIVS); switch (start) { case -1: ps_freeprocess(psp); @@ -257,8 +263,8 @@ ps_bpf_cmd(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg) ps_entersandbox("stdio", NULL); break; default: - logdebugx("%s: spawned BPF %s on PID %d", - psp->psp_ifname, psp->psp_protostr, start); + logdebugx("%s: spawned %s on PID %d", + psp->psp_ifname, psp->psp_name, psp->psp_pid); break; } return start; @@ -322,7 +328,7 @@ ps_bpf_send(const struct interface *ifp, const struct in_addr *ia, if (ia != NULL) psm.ps_id.psi_addr.psa_in_addr = *ia; - return ps_sendpsmdata(ctx, ctx->ps_root_fd, &psm, data, len); + return ps_sendpsmdata(ctx, ctx->ps_root->psp_fd, &psm, data, len); } #ifdef ARP diff --git a/src/privsep-bsd.c b/src/privsep-bsd.c index 91c54488..2ca26a53 100644 --- a/src/privsep-bsd.c +++ b/src/privsep-bsd.c @@ -205,7 +205,7 @@ ps_root_ioctldom(struct dhcpcd_ctx *ctx, uint16_t domain, unsigned long request, void *data, size_t len) { - if (ps_sendcmd(ctx, ctx->ps_root_fd, domain, + if (ps_sendcmd(ctx, ctx->ps_root->psp_fd, domain, request, data, len) == -1) return -1; return ps_root_readerror(ctx, data, len); @@ -231,7 +231,7 @@ ssize_t ps_root_route(struct dhcpcd_ctx *ctx, void *data, size_t len) { - if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_ROUTE, 0, data, len) == -1) + if (ps_sendcmd(ctx, ctx->ps_root->psp_fd, PS_ROUTE, 0, data, len) == -1) return -1; return ps_root_readerror(ctx, data, len); } diff --git a/src/privsep-control.c b/src/privsep-control.c index f216609b..0e50e9ea 100644 --- a/src/privsep-control.c +++ b/src/privsep-control.c @@ -37,9 +37,9 @@ #include "privsep.h" static int -ps_ctl_startcb(void *arg) +ps_ctl_startcb(struct ps_process *psp) { - struct dhcpcd_ctx *ctx = arg; + struct dhcpcd_ctx *ctx = psp->psp_ctx; sa_family_t af; if (ctx->options & DHCPCD_MANAGER) { @@ -60,8 +60,6 @@ ps_ctl_startcb(void *arg) af = AF_UNSPEC; } - ctx->ps_control_pid = getpid(); - return control_start(ctx, ctx->options & DHCPCD_MANAGER ? NULL : *ctx->ifv, af); } @@ -76,18 +74,17 @@ ps_ctl_recvmsgcb(void *arg, struct ps_msghdr *psm, __unused struct msghdr *msg) return -1; } - if (ctx->ps_control_client != NULL) - ctx->ps_control_client = NULL; + ctx->ps_control_client = NULL; return 0; } static void ps_ctl_recvmsg(void *arg, unsigned short events) { - struct dhcpcd_ctx *ctx = arg; + struct ps_process *psp = arg; - if (ps_recvpsmsg(ctx, ctx->ps_control_fd, events, - ps_ctl_recvmsgcb, ctx) == -1) + if (ps_recvpsmsg(psp->psp_ctx, psp->psp_fd, events, + ps_ctl_recvmsgcb, psp->psp_ctx) == -1) logerr(__func__); } @@ -145,14 +142,14 @@ ps_ctl_dispatch(void *arg, struct ps_msghdr *psm, struct msghdr *msg) logerrx("%s: cannot handle another client", __func__); return 0; } - fd = control_new(ctx, ctx->ps_control_data_fd, fd_flags); + fd = control_new(ctx, ctx->ps_ctl->psp_work_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: - control_free(ctx->ps_control_client); + ctx->ps_control_client = NULL; break; default: errno = ENOTSUP; @@ -164,10 +161,10 @@ ps_ctl_dispatch(void *arg, struct ps_msghdr *psm, struct msghdr *msg) static void ps_ctl_dodispatch(void *arg, unsigned short events) { - struct dhcpcd_ctx *ctx = arg; + struct ps_process *psp = arg; - if (ps_recvpsmsg(ctx, ctx->ps_control_fd, events, - ps_ctl_dispatch, ctx) == -1) + if (ps_recvpsmsg(psp->psp_ctx, psp->psp_fd, events, + ps_ctl_dispatch, psp->psp_ctx) == -1) logerr(__func__); } @@ -181,8 +178,9 @@ ps_ctl_recv(void *arg, unsigned short events) if (!(events & ELE_READ)) logerrx("%s: unexpected event 0x%04x", __func__, events); - errno = 0; - len = read(ctx->ps_control_data_fd, buf, sizeof(buf)); + len = read(ctx->ps_ctl->psp_work_fd, buf, sizeof(buf)); + if (len == 0) + return; if (len == -1) { logerr("%s: read", __func__); eloop_exit(ctx->eloop, EXIT_FAILURE); @@ -227,6 +225,11 @@ ps_ctl_listen(void *arg, unsigned short events) pid_t ps_ctl_start(struct dhcpcd_ctx *ctx) { + struct ps_id id = { + .psi_ifindex = 0, + .psi_cmd = PS_CTL, + }; + struct ps_process *psp; int data_fd[2], listen_fd[2]; pid_t pid; @@ -239,15 +242,15 @@ ps_ctl_start(struct dhcpcd_ctx *ctx) 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, NULL, - PSF_DROPPRIVS); + psp = ctx->ps_ctl = ps_newprocess(ctx, &id); + strlcpy(psp->psp_name, "control proxy", sizeof(psp->psp_name)); + pid = ps_startprocess(psp, ps_ctl_recvmsg, ps_ctl_dodispatch, + ps_ctl_startcb, NULL, PSF_DROPPRIVS); if (pid == -1) return -1; else if (pid != 0) { - ctx->ps_control_data_fd = data_fd[1]; + psp->psp_work_fd = data_fd[1]; close(data_fd[0]); ctx->ps_control = control_new(ctx, listen_fd[1], FD_SENDLEN | FD_LISTEN); @@ -257,14 +260,13 @@ ps_ctl_start(struct dhcpcd_ctx *ctx) return pid; } - ctx->ps_control_data_fd = data_fd[0]; + psp->psp_work_fd = data_fd[0]; close(data_fd[1]); - if (eloop_event_add(ctx->eloop, ctx->ps_control_data_fd, ELE_READ, + if (eloop_event_add(ctx->eloop, psp->psp_work_fd, ELE_READ, ps_ctl_recv, ctx) == -1) return -1; - ctx->ps_control = control_new(ctx, - listen_fd[0], 0); + ctx->ps_control = control_new(ctx, listen_fd[0], 0); close(listen_fd[1]); if (ctx->ps_control == NULL) return -1; @@ -280,7 +282,7 @@ int ps_ctl_stop(struct dhcpcd_ctx *ctx) { - return ps_dostop(ctx, &ctx->ps_control_pid, &ctx->ps_control_fd); + return ps_stopprocess(ctx->ps_ctl); } ssize_t @@ -291,7 +293,7 @@ ps_ctl_sendargs(struct fd_list *fd, void *data, size_t len) 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, + return ps_sendcmd(ctx, ctx->ps_ctl->psp_fd, PS_CTL, fd->flags & FD_UNPRIV ? PS_CTL_UNPRIV : PS_CTL_PRIV, data, len); } @@ -301,5 +303,5 @@ 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); + return ps_sendcmd(ctx, ctx->ps_ctl->psp_fd, PS_CTL_EOF, 0, NULL, 0); } diff --git a/src/privsep-inet.c b/src/privsep-inet.c index fc7da095..2ad2ab18 100644 --- a/src/privsep-inet.c +++ b/src/privsep-inet.c @@ -54,7 +54,7 @@ ps_inet_recvbootp(void *arg, unsigned short events) struct dhcpcd_ctx *ctx = arg; if (ps_recvmsg(ctx, ctx->udp_rfd, events, - PS_BOOTP, ctx->ps_inet_fd) == -1) + PS_BOOTP, ctx->ps_inet->psp_fd) == -1) logerr(__func__); } #endif @@ -69,13 +69,13 @@ ps_inet_recvra(void *arg, unsigned short events) struct dhcpcd_ctx *ctx = ifp->ctx; if (ps_recvmsg(ctx, state->nd_fd, events, - PS_ND, ctx->ps_inet_fd) == -1) + PS_ND, ctx->ps_inet->psp_fd) == -1) logerr(__func__); #else struct dhcpcd_ctx *ctx = arg; if (ps_recvmsg(ctx, ctx->nd_fd, events, - PS_ND, ctx->ps_inet_fd) == -1) + PS_ND, ctx->ps_inet->psp_fd) == -1) logerr(__func__); #endif } @@ -88,7 +88,7 @@ ps_inet_recvdhcp6(void *arg, unsigned short events) struct dhcpcd_ctx *ctx = arg; if (ps_recvmsg(ctx, ctx->dhcp6_rfd, events, - PS_DHCP6, ctx->ps_inet_fd) == -1) + PS_DHCP6, ctx->ps_inet->psp_fd) == -1) logerr(__func__); } #endif @@ -116,16 +116,16 @@ ps_inet_canstart(const struct dhcpcd_ctx *ctx) } static int -ps_inet_startcb(void *arg) +ps_inet_startcb(struct ps_process *psp) { - struct dhcpcd_ctx *ctx = arg; + struct dhcpcd_ctx *ctx = psp->psp_ctx; int ret = 0; if (ctx->options & DHCPCD_MANAGER) setproctitle("[network proxy]"); else setproctitle("[network proxy] %s%s%s", - ctx->ifv[0], + ctx->ifc != 0 ? ctx->ifv[0] : "", ctx->options & DHCPCD_IPV4 ? " [ip4]" : "", ctx->options & DHCPCD_IPV6 ? " [ip6]" : ""); @@ -305,10 +305,10 @@ dosend: static void ps_inet_recvmsg(void *arg, unsigned short events) { - struct dhcpcd_ctx *ctx = arg; + struct ps_process *psp = arg; /* Receive shutdown */ - if (ps_recvpsmsg(ctx, ctx->ps_inet_fd, events, NULL, NULL) == -1) + if (ps_recvpsmsg(psp->psp_ctx, psp->psp_fd, events, NULL, NULL) == -1) logerr(__func__); } @@ -343,22 +343,30 @@ ps_inet_dispatch(void *arg, struct ps_msghdr *psm, struct msghdr *msg) static void ps_inet_dodispatch(void *arg, unsigned short events) { - struct dhcpcd_ctx *ctx = arg; + struct ps_process *psp = arg; - if (ps_recvpsmsg(ctx, ctx->ps_inet_fd, events, - ps_inet_dispatch, ctx) == -1) + if (ps_recvpsmsg(psp->psp_ctx, psp->psp_fd, events, + ps_inet_dispatch, psp->psp_ctx) == -1) logerr(__func__); } pid_t ps_inet_start(struct dhcpcd_ctx *ctx) { + struct ps_id id = { + .psi_ifindex = 0, + .psi_cmd = PS_INET, + }; + struct ps_process *psp; pid_t pid; - pid = ps_dostart(ctx, &ctx->ps_inet_pid, &ctx->ps_inet_fd, - ps_inet_recvmsg, ps_inet_dodispatch, ctx, - ps_inet_startcb, NULL, - PSF_DROPPRIVS); + psp = ctx->ps_inet = ps_newprocess(ctx, &id); + if (psp == NULL) + return -1; + + strlcpy(psp->psp_name, "network proxy", sizeof(psp->psp_name)); + pid = ps_startprocess(psp, ps_inet_recvmsg, ps_inet_dodispatch, + ps_inet_startcb, NULL, PSF_DROPPRIVS); if (pid == 0) ps_entersandbox("stdio", NULL); @@ -370,7 +378,7 @@ int ps_inet_stop(struct dhcpcd_ctx *ctx) { - return ps_dostop(ctx, &ctx->ps_inet_pid, &ctx->ps_inet_fd); + return ps_stopprocess(ctx->ps_inet); } #ifdef INET @@ -385,14 +393,13 @@ ps_inet_recvinbootp(void *arg, unsigned short events) } static int -ps_inet_listenin(void *arg) +ps_inet_listenin(struct ps_process *psp) { - struct ps_process *psp = arg; struct in_addr *ia = &psp->psp_id.psi_addr.psa_in_addr; char buf[INET_ADDRSTRLEN]; inet_ntop(AF_INET, ia, buf, sizeof(buf)); - setproctitle("[network proxy] %s", buf); + setproctitle("[%s proxy] %s", psp->psp_protostr, buf); psp->psp_work_fd = dhcp_openudp(ia); if (psp->psp_work_fd == -1) { @@ -413,8 +420,6 @@ ps_inet_listenin(void *arg) logerr("%s: eloop_event_add DHCP", __func__); return -1; } - - logdebugx("spawned listener %s on PID %d", buf, getpid()); return 0; } #endif @@ -431,9 +436,8 @@ ps_inet_recvin6nd(void *arg) } static int -ps_inet_listennd(void *arg) +ps_inet_listennd(struct ps_process *psp) { - struct ps_process *psp = arg; setproctitle("[ND network proxy]"); @@ -456,8 +460,6 @@ ps_inet_listennd(void *arg) logerr(__func__); return -1; } - - logdebugx("spawned ND listener on PID %d", getpid()); return 0; } #endif @@ -474,14 +476,13 @@ ps_inet_recvin6dhcp6(void *arg, unsigned short events) } static int -ps_inet_listenin6(void *arg) +ps_inet_listenin6(struct ps_process *psp) { - struct ps_process *psp = arg; struct in6_addr *ia = &psp->psp_id.psi_addr.psa_in6_addr; char buf[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, ia, buf, sizeof(buf)); - setproctitle("[network proxy] %s", buf); + setproctitle("[%s proxy] %s", psp->psp_protostr, buf); psp->psp_work_fd = dhcp6_openudp(psp->psp_id.psi_ifindex, ia); if (psp->psp_work_fd == -1) { @@ -502,8 +503,6 @@ ps_inet_listenin6(void *arg) logerr("%s: eloop_event_add DHCP", __func__); return -1; } - - logdebugx("spawned listener %s on PID %d", buf, getpid()); return 0; } #endif @@ -523,8 +522,11 @@ ps_inet_cmd(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg) { uint16_t cmd; struct ps_process *psp; - int (*start_func)(void *); + int (*start_func)(struct ps_process *); pid_t start; + struct ps_addr *psa = &psm->ps_id.psi_addr; + void *ia; + char buf[INET_MAX_ADDRSTRLEN]; cmd = (uint16_t)(psm->ps_cmd & ~(PS_START | PS_STOP)); if (cmd == psm->ps_cmd) @@ -541,21 +543,40 @@ ps_inet_cmd(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg) return 0; } + if (!(psm->ps_cmd & PS_START)) { + errno = EINVAL; + return -1; + } + + if (psp != NULL) + return 1; + + psp = ps_newprocess(ctx, &psm->ps_id); + if (psp == NULL) + return -1; + + switch (cmd) { #ifdef INET case PS_BOOTP: start_func = ps_inet_listenin; + psp->psp_protostr = "BOOTP"; + ia = &psa->psa_in_addr; break; #endif #ifdef INET6 #ifdef __sun case PS_ND: start_func = ps_inet_listennd; + psp->psp_protostr = "ND"; + ia = &psa->psa_in6_addr; break; #endif #ifdef DHCP6 case PS_DHCP6: start_func = ps_inet_listenin6; + psp->psp_protostr = "DHCP6"; + ia = &psa->psa_in6_addr; break; #endif #endif @@ -565,23 +586,11 @@ ps_inet_cmd(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg) return -1; } - if (!(psm->ps_cmd & PS_START)) { - errno = EINVAL; - return -1; - } - - if (psp != NULL) - return 1; - - psp = ps_newprocess(ctx, &psm->ps_id); - if (psp == NULL) - return -1; - - start = ps_dostart(ctx, - &psp->psp_pid, &psp->psp_fd, - ps_inet_recvmsgpsp, NULL, psp, - start_func, NULL, - PSF_DROPPRIVS); + snprintf(psp->psp_name, sizeof(psp->psp_name), + "%s proxy %s", psp->psp_protostr, + inet_ntop(psa->psa_family, ia, buf, sizeof(buf))); + start = ps_startprocess(psp, ps_inet_recvmsgpsp, NULL, + start_func, NULL, PSF_DROPPRIVS); switch (start) { case -1: ps_freeprocess(psp); @@ -590,6 +599,8 @@ ps_inet_cmd(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg) ps_entersandbox("stdio", NULL); break; default: + logdebugx("%s: spawned %s on PID %d", + psp->psp_ifname, psp->psp_name, psp->psp_pid); break; } return start; @@ -606,11 +617,12 @@ ps_inet_in_docmd(struct ipv4_addr *ia, uint16_t cmd, const struct msghdr *msg) .ps_id = { .psi_cmd = (uint8_t)(cmd & ~(PS_START | PS_STOP)), .psi_ifindex = ia->iface->index, + .psi_addr.psa_family = AF_INET, .psi_addr.psa_in_addr = ia->addr, }, }; - return ps_sendpsmmsg(ctx, ctx->ps_root_fd, &psm, msg); + return ps_sendpsmmsg(ctx, ctx->ps_root->psp_fd, &psm, msg); } ssize_t @@ -630,8 +642,9 @@ ps_inet_closebootp(struct ipv4_addr *ia) ssize_t ps_inet_sendbootp(struct interface *ifp, const struct msghdr *msg) { + struct dhcpcd_ctx *ctx = ifp->ctx; - return ps_sendmsg(ifp->ctx, ifp->ctx->ps_root_fd, PS_BOOTP, 0, msg); + return ps_sendmsg(ctx, ctx->ps_root->psp_fd, PS_BOOTP, 0, msg); } #endif /* INET */ @@ -646,10 +659,11 @@ ps_inet_ifp_docmd(struct interface *ifp, uint16_t cmd, const struct msghdr *msg) .ps_id = { .psi_cmd = (uint8_t)(cmd & ~(PS_START | PS_STOP)), .psi_ifindex = ifp->index, + .psi_addr.psa_family = AF_INET6, }, }; - return ps_sendpsmmsg(ctx, ctx->ps_root_fd, &psm, msg); + return ps_sendpsmmsg(ctx, ctx->ps_root->psp_fd, &psm, msg); } ssize_t @@ -676,8 +690,9 @@ ps_inet_sendnd(struct interface *ifp, const struct msghdr *msg) ssize_t ps_inet_sendnd(struct interface *ifp, const struct msghdr *msg) { + struct dhcpcd_ctx *ctx = ifp->ctx; - return ps_sendmsg(ifp->ctx, ifp->ctx->ps_root_fd, PS_ND, 0, msg); + return ps_sendmsg(ctx, ctx->ps_root->psp_fd, PS_ND, 0, msg); } #endif @@ -691,11 +706,12 @@ ps_inet_in6_docmd(struct ipv6_addr *ia, uint16_t cmd, const struct msghdr *msg) .ps_id = { .psi_cmd = (uint8_t)(cmd & ~(PS_START | PS_STOP)), .psi_ifindex = ia->iface->index, + .psi_addr.psa_family = AF_INET6, .psi_addr.psa_in6_addr = ia->addr, }, }; - return ps_sendpsmmsg(ctx, ctx->ps_root_fd, &psm, msg); + return ps_sendpsmmsg(ctx, ctx->ps_root->psp_fd, &psm, msg); } ssize_t @@ -715,8 +731,9 @@ ps_inet_closedhcp6(struct ipv6_addr *ia) ssize_t ps_inet_senddhcp6(struct interface *ifp, const struct msghdr *msg) { + struct dhcpcd_ctx *ctx = ifp->ctx; - return ps_sendmsg(ifp->ctx, ifp->ctx->ps_root_fd, PS_DHCP6, 0, msg); + return ps_sendmsg(ctx, ctx->ps_root->psp_fd, PS_DHCP6, 0, msg); } #endif /* DHCP6 */ #endif /* INET6 */ diff --git a/src/privsep-root.c b/src/privsep-root.c index c04cf221..b7c737e3 100644 --- a/src/privsep-root.c +++ b/src/privsep-root.c @@ -97,7 +97,7 @@ ps_root_readerrorcb(void *arg, unsigned short events) goto out; \ } while (0 /* CONSTCOND */) - len = readv(ctx->ps_root_fd, iov, __arraycount(iov)); + len = readv(ctx->ps_root->psp_fd, iov, __arraycount(iov)); if (len == -1) PSR_ERROR(errno); else if ((size_t)len < sizeof(*psr_error)) @@ -116,12 +116,13 @@ ps_root_readerror(struct dhcpcd_ctx *ctx, void *data, size_t len) .psr_data = data, .psr_datalen = len, }; - if (eloop_event_add(ctx->ps_eloop, ctx->ps_root_fd, ELE_READ, + if (eloop_event_add(ctx->ps_eloop, ctx->ps_root->psp_fd, ELE_READ, ps_root_readerrorcb, &psr_ctx) == -1) return -1; eloop_enter(ctx->ps_eloop); eloop_start(ctx->ps_eloop, &ctx->sigset); + eloop_event_delete(ctx->ps_eloop, ctx->ps_root->psp_fd); errno = psr_ctx.psr_error.psr_errno; return psr_ctx.psr_error.psr_result; @@ -144,7 +145,8 @@ ps_root_mreaderrorcb(void *arg, unsigned short events) if (events != ELE_READ) logerrx("%s: unexpected event 0x%04x", __func__, events); - len = recv(ctx->ps_root_fd, psr_error, sizeof(*psr_error), MSG_PEEK); + len = recv(ctx->ps_root->psp_fd, + psr_error, sizeof(*psr_error), MSG_PEEK); if (len == -1) PSR_ERROR(errno); else if ((size_t)len < sizeof(*psr_error)) @@ -161,7 +163,7 @@ ps_root_mreaderrorcb(void *arg, unsigned short events) iov[1].iov_len = psr_ctx->psr_datalen; } - len = readv(ctx->ps_root_fd, iov, __arraycount(iov)); + len = readv(ctx->ps_root->psp_fd, iov, __arraycount(iov)); if (len == -1) PSR_ERROR(errno); else if ((size_t)len != sizeof(*psr_error) + psr_ctx->psr_datalen) @@ -185,6 +187,7 @@ ps_root_mreaderror(struct dhcpcd_ctx *ctx, void **data, size_t *len) eloop_enter(ctx->ps_eloop); eloop_start(ctx->ps_eloop, &ctx->sigset); + eloop_event_delete(ctx->ps_eloop, ctx->ps_root->psp_fd); errno = psr_ctx.psr_error.psr_errno; *data = psr_ctx.psr_data; @@ -211,7 +214,7 @@ ps_root_writeerror(struct dhcpcd_ctx *ctx, ssize_t result, logdebugx("%s: result %zd errno %d", __func__, result, errno); #endif - return writev(ctx->ps_root_fd, iov, __arraycount(iov)); + return writev(ctx->ps_root->psp_fd, iov, __arraycount(iov)); } static ssize_t @@ -276,6 +279,7 @@ ps_root_run_script(struct dhcpcd_ctx *ctx, const void *data, size_t len) pid = script_exec(argv, ctx->script_env); if (pid == -1) return -1; + /* Wait for the script to finish */ while (waitpid(pid, &status, 0) == -1) { if (errno != EINTR) { @@ -475,12 +479,13 @@ ps_root_recvmsgcb(void *arg, struct ps_msghdr *psm, struct msghdr *msg) if (psp != NULL) { if (psm->ps_cmd & PS_STOP) { - int ret = ps_dostop(ctx, &psp->psp_pid, &psp->psp_fd); - - ps_freeprocess(psp); - return ret; + return ps_stopprocess(psp); } else if (psm->ps_cmd & PS_START) { /* Process has already started .... */ + logdebugx("%s%sprocess %s already started on pid %d", + psp->psp_ifname, + psp->psp_ifname[0] != '\0' ? ": " : "", + psp->psp_name, psp->psp_pid); return 0; } @@ -488,9 +493,6 @@ ps_root_recvmsgcb(void *arg, struct ps_msghdr *psm, struct msghdr *msg) if (err == -1) { logerr("%s: failed to send message to pid %d", __func__, psp->psp_pid); - shutdown(psp->psp_fd, SHUT_RDWR); - close(psp->psp_fd); - psp->psp_fd = -1; ps_freeprocess(psp); } return 0; @@ -538,6 +540,14 @@ 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_STOPPROCS: + ctx->options |= DHCPCD_EXITING; + TAILQ_FOREACH(psp, &ctx->ps_processes, next) { + if (psp != ctx->ps_root) + ps_stopprocess(psp); + } + err = ps_stopwait(ctx); + break; case PS_UNLINK: if (!ps_root_validpath(ctx, psm->ps_cmd, data)) { err = -1; @@ -613,10 +623,10 @@ ps_root_recvmsgcb(void *arg, struct ps_msghdr *psm, struct msghdr *msg) static void ps_root_recvmsg(void *arg, unsigned short events) { - struct dhcpcd_ctx *ctx = arg; + struct ps_process *psp = arg; - if (ps_recvpsmsg(ctx, ctx->ps_root_fd, events, - ps_root_recvmsgcb, ctx) == -1) + if (ps_recvpsmsg(psp->psp_ctx, psp->psp_fd, events, + ps_root_recvmsgcb, psp->psp_ctx) == -1) logerr(__func__); } @@ -638,15 +648,15 @@ ps_root_handleinterface(void *arg, int action, const char *ifname) return -1; } - return (int)ps_sendcmd(ctx, ctx->ps_data_fd, PS_DEV_IFCMD, flag, - ifname, strlen(ifname) + 1); + return (int)ps_sendcmd(ctx, ctx->ps_data_fd, PS_DEV_IFCMD, + flag, ifname, strlen(ifname) + 1); } #endif static int -ps_root_startcb(void *arg) +ps_root_startcb(struct ps_process *psp) { - struct dhcpcd_ctx *ctx = arg; + struct dhcpcd_ctx *ctx = psp->psp_ctx; if (ctx->options & DHCPCD_MANAGER) setproctitle("[privileged proxy]"); @@ -655,7 +665,6 @@ ps_root_startcb(void *arg) ctx->ifv[0], ctx->options & DHCPCD_IPV4 ? " [ip4]" : "", ctx->options & DHCPCD_IPV6 ? " [ip6]" : ""); - ctx->ps_root_pid = getpid(); ctx->options |= DHCPCD_PRIVSEPROOT; /* Open network sockets for sending. @@ -663,7 +672,7 @@ ps_root_startcb(void *arg) * but makes life very easy for unicasting DHCPv6 in non manager * mode as we no longer care about address selection. * We can't call shutdown SHUT_RD on the socket because it's - * not connectd. All we can do is try and set a zero sized + * not connected. All we can do is try and set a zero sized * receive buffer and just let it overflow. * Reading from it just to drain it is a waste of CPU time. */ #ifdef INET @@ -711,19 +720,57 @@ ps_root_startcb(void *arg) (DHCPCD_MANAGER | DHCPCD_DEV)) dev_start(ctx, ps_root_handleinterface); #endif - return 0; } -static void -ps_root_signalcb(int sig, __unused void *arg) +void +ps_root_signalcb(int sig, void *arg) { + struct dhcpcd_ctx *ctx = arg; + int status; + pid_t pid; + const char *ifname, *name; + struct ps_process *psp; - if (sig == SIGCHLD) { - while (waitpid(-1, NULL, WNOHANG) > 0) - ; + if (sig != SIGCHLD) return; + + while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { + psp = ps_findprocesspid(ctx, pid); + if (psp != NULL) { + ifname = psp->psp_ifname; + name = psp->psp_name; + } else { + /* Ignore logging the double fork */ + if (ctx->options & DHCPCD_LAUNCHER) + continue; + ifname = ""; + name = "unknown process"; + } + + if (WIFEXITED(status) && WEXITSTATUS(status) != 0) + logerrx("%s%s%s exited unexpectedly from PID %d," + " code=%d", + ifname, ifname[0] != '\0' ? ": " : "", + name, pid, WEXITSTATUS(status)); + else if (WIFSIGNALED(status)) + logerrx("%s%s%s exited unexpectedly from PID %d," + " signal=%s", + ifname, ifname[0] != '\0' ? ": " : "", + name, pid, strsignal(WTERMSIG(status))); + else + logdebugx("%s%s%s exited from PID %d", + ifname, ifname[0] != '\0' ? ": " : "", + name, pid); + + if (psp != NULL) + ps_freeprocess(psp); } + + if (!(ctx->options & DHCPCD_EXITING)) + return; + if (!(PS_WAITING_FOR_PROCESSES(ctx))) + eloop_exit(ctx->ps_eloop, EXIT_SUCCESS); } int (*handle_interface)(void *, int, const char *); @@ -799,25 +846,29 @@ ps_root_log(void *arg, unsigned short events) if (events != ELE_READ) logerrx("%s: unexpected event 0x%04x", __func__, events); - /* OpenBSD reports connection reset when dhcpcd exits ... */ - if (logreadfd(ctx->ps_log_fd) == -1 && errno != ECONNRESET) + if (logreadfd(ctx->ps_log_root_fd) == -1) logerr(__func__); } pid_t ps_root_start(struct dhcpcd_ctx *ctx) { + struct ps_id id = { + .psi_ifindex = 0, + .psi_cmd = PS_ROOT, + }; + struct ps_process *psp; int logfd[2], datafd[2]; pid_t pid; - if (xsocketpair(AF_UNIX, SOCK_DGRAM | SOCK_CXNB, 0, logfd) == -1) + if (xsocketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CXNB, 0, logfd) == -1) return -1; #ifdef PRIVSEP_RIGHTS if (ps_rights_limit_fdpair(logfd) == -1) return -1; #endif - if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_CXNB, 0, datafd) == -1) + if (socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CXNB, 0, datafd) == -1) return -1; if (ps_setbuf_fdpair(datafd) == -1) return -1; @@ -826,16 +877,17 @@ ps_root_start(struct dhcpcd_ctx *ctx) return -1; #endif - pid = ps_dostart(ctx, &ctx->ps_root_pid, &ctx->ps_root_fd, - ps_root_recvmsg, NULL, ctx, - ps_root_startcb, ps_root_signalcb, 0); + psp = ctx->ps_root = ps_newprocess(ctx, &id); + strlcpy(psp->psp_name, "privileged proxy", sizeof(psp->psp_name)); + pid = ps_startprocess(psp, ps_root_recvmsg, NULL, + ps_root_startcb, ps_root_signalcb, PSF_ELOOP); if (pid == 0) { - ctx->ps_log_fd = logfd[1]; - if (eloop_event_add(ctx->eloop, ctx->ps_log_fd, ELE_READ, + ctx->ps_log_fd = logfd[0]; /* Keep open to pass to processes */ + ctx->ps_log_root_fd = logfd[1]; + if (eloop_event_add(ctx->eloop, ctx->ps_log_root_fd, ELE_READ, ps_root_log, ctx) == -1) return -1; - close(logfd[0]); ctx->ps_data_fd = datafd[1]; close(datafd[0]); return 0; @@ -849,14 +901,7 @@ ps_root_start(struct dhcpcd_ctx *ctx) close(datafd[1]); if (eloop_event_add(ctx->eloop, ctx->ps_data_fd, ELE_READ, ps_root_dispatch, ctx) == -1) - return -1; - - if ((ctx->ps_eloop = eloop_new()) == NULL) - return -1; - - eloop_signal_set_cb(ctx->ps_eloop, - dhcpcd_signals, dhcpcd_signals_len, - ps_root_signalcb, ctx); + return 1; return pid; } @@ -865,19 +910,43 @@ int ps_root_stop(struct dhcpcd_ctx *ctx) { + /* If we are the root process, ensure the log fd is fully drained. */ + if (ctx->options & DHCPCD_PRIVSEPROOT && ctx->ps_log_fd != -1) { + do { + ; + } while (logreadfd(ctx->ps_log_fd) != -1); + } + if (!(ctx->options & DHCPCD_PRIVSEP) || ctx->options & DHCPCD_FORKED || ctx->eloop == NULL) return 0; - return ps_dostop(ctx, &ctx->ps_root_pid, &ctx->ps_root_fd); + if (ps_stopprocess(ctx->ps_root) == -1) + return -1; + ctx->ps_root = NULL; + return ps_stopwait(ctx); +} + +ssize_t +ps_root_stopprocesses(struct dhcpcd_ctx *ctx) +{ + + if (!(IN_PRIVSEP_SE(ctx))) + return 0; + + if (ps_sendcmd(ctx, ctx->ps_root->psp_fd, PS_STOPPROCS, 0, + NULL, 0) == -1) + return -1; + return ps_root_readerror(ctx, NULL, 0); } ssize_t ps_root_script(struct dhcpcd_ctx *ctx, const void *data, size_t len) { - if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_SCRIPT, 0, data, len) == -1) + if (ps_sendcmd(ctx, ctx->ps_root->psp_fd, PS_SCRIPT, + 0, data, len) == -1) return -1; return ps_root_readerror(ctx, NULL, 0); } @@ -886,14 +955,15 @@ ssize_t ps_root_ioctl(struct dhcpcd_ctx *ctx, ioctl_request_t req, void *data, size_t len) { + int fd = ctx->ps_root->psp_fd; #ifdef IOCTL_REQUEST_TYPE unsigned long ulreq = 0; memcpy(&ulreq, &req, sizeof(req)); - if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_IOCTL, ulreq, data, len) == -1) + if (ps_sendcmd(ctx, fd, PS_IOCTL, ulreq, data, len) == -1) return -1; #else - if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_IOCTL, req, data, len) == -1) + if (ps_sendcmd(ctx, fd, PS_IOCTL, req, data, len) == -1) return -1; #endif return ps_root_readerror(ctx, data, len); @@ -903,7 +973,7 @@ ssize_t ps_root_unlink(struct dhcpcd_ctx *ctx, const char *file) { - if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_UNLINK, 0, + if (ps_sendcmd(ctx, ctx->ps_root->psp_fd, PS_UNLINK, 0, file, strlen(file) + 1) == -1) return -1; return ps_root_readerror(ctx, NULL, 0); @@ -913,7 +983,7 @@ ssize_t ps_root_readfile(struct dhcpcd_ctx *ctx, const char *file, void *data, size_t len) { - if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_READFILE, 0, + if (ps_sendcmd(ctx, ctx->ps_root->psp_fd, PS_READFILE, 0, file, strlen(file) + 1) == -1) return -1; return ps_root_readerror(ctx, data, len); @@ -934,7 +1004,7 @@ ps_root_writefile(struct dhcpcd_ctx *ctx, const char *file, mode_t mode, } memcpy(buf + flen, data, len); - if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_WRITEFILE, mode, + if (ps_sendcmd(ctx, ctx->ps_root->psp_fd, PS_WRITEFILE, mode, buf, flen + len) == -1) return -1; return ps_root_readerror(ctx, NULL, 0); @@ -944,7 +1014,7 @@ ssize_t ps_root_filemtime(struct dhcpcd_ctx *ctx, const char *file, time_t *time) { - if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_FILEMTIME, 0, + if (ps_sendcmd(ctx, ctx->ps_root->psp_fd, PS_FILEMTIME, 0, file, strlen(file) + 1) == -1) return -1; return ps_root_readerror(ctx, time, sizeof(*time)); @@ -954,7 +1024,8 @@ ssize_t ps_root_logreopen(struct dhcpcd_ctx *ctx) { - if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_LOGREOPEN, 0, NULL, 0) == -1) + if (ps_sendcmd(ctx, ctx->ps_root->psp_fd, PS_LOGREOPEN, 0, + NULL, 0) == -1) return -1; return ps_root_readerror(ctx, NULL, 0); } @@ -970,7 +1041,7 @@ ps_root_getifaddrs(struct dhcpcd_ctx *ctx, struct ifaddrs **ifahead) size_t len; ssize_t err; - if (ps_sendcmd(ctx, ctx->ps_root_fd, + if (ps_sendcmd(ctx, ctx->ps_root->psp_fd, PS_GETIFADDRS, 0, NULL, 0) == -1) return -1; err = ps_root_mreaderror(ctx, &buf, &len); @@ -1045,7 +1116,7 @@ ssize_t ps_root_ip6forwarding(struct dhcpcd_ctx *ctx, const char *ifname) { - if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_IP6FORWARDING, 0, + if (ps_sendcmd(ctx, ctx->ps_root->psp_fd, PS_IP6FORWARDING, 0, ifname, ifname != NULL ? strlen(ifname) + 1 : 0) == -1) return -1; return ps_root_readerror(ctx, NULL, 0); @@ -1057,7 +1128,7 @@ int ps_root_getauthrdm(struct dhcpcd_ctx *ctx, uint64_t *rdm) { - if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_AUTH_MONORDM, 0, + if (ps_sendcmd(ctx, ctx->ps_root->psp_fd, PS_AUTH_MONORDM, 0, rdm, sizeof(*rdm))== -1) return -1; return (int)ps_root_readerror(ctx, rdm, sizeof(*rdm)); @@ -1069,7 +1140,7 @@ int ps_root_dev_initialised(struct dhcpcd_ctx *ctx, const char *ifname) { - if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_DEV_INITTED, 0, + if (ps_sendcmd(ctx, ctx->ps_root->psp_fd, PS_DEV_INITTED, 0, ifname, strlen(ifname) + 1)== -1) return -1; return (int)ps_root_readerror(ctx, NULL, 0); @@ -1079,7 +1150,8 @@ int ps_root_dev_listening(struct dhcpcd_ctx * ctx) { - if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_DEV_LISTENING, 0, NULL, 0)== -1) + if (ps_sendcmd(ctx, ctx->ps_root->psp_fd, PS_DEV_LISTENING, + 0, NULL, 0) == -1) return -1; return (int)ps_root_readerror(ctx, NULL, 0); } diff --git a/src/privsep-root.h b/src/privsep-root.h index 7fdd9f69..a54696c3 100644 --- a/src/privsep-root.h +++ b/src/privsep-root.h @@ -37,6 +37,7 @@ pid_t ps_root_start(struct dhcpcd_ctx *ctx); int ps_root_stop(struct dhcpcd_ctx *ctx); +void ps_root_signalcb(int, void *); ssize_t ps_root_readerror(struct dhcpcd_ctx *, void *, size_t); ssize_t ps_root_mreaderror(struct dhcpcd_ctx *, void **, size_t *); @@ -49,6 +50,7 @@ ssize_t ps_root_writefile(struct dhcpcd_ctx *, const char *, mode_t, const void *, size_t); ssize_t ps_root_logreopen(struct dhcpcd_ctx *); ssize_t ps_root_script(struct dhcpcd_ctx *, const void *, size_t); +ssize_t ps_root_stopprocesses(struct dhcpcd_ctx *); int ps_root_getauthrdm(struct dhcpcd_ctx *, uint64_t *); #ifdef PRIVSEP_GETIFADDRS int ps_root_getifaddrs(struct dhcpcd_ctx *, struct ifaddrs **); diff --git a/src/privsep.c b/src/privsep.c index 0a9f93bf..25c5fa42 100644 --- a/src/privsep.c +++ b/src/privsep.c @@ -114,6 +114,7 @@ ps_init(struct dhcpcd_ctx *ctx) static int ps_dropprivs(struct dhcpcd_ctx *ctx) { + return 0; struct passwd *pw = ctx->ps_user; if (ctx->options & DHCPCD_LAUNCHER) @@ -135,7 +136,7 @@ ps_dropprivs(struct dhcpcd_ctx *ctx) struct rlimit rzero = { .rlim_cur = 0, .rlim_max = 0 }; - if (ctx->ps_control_pid != getpid()) { + if (ctx->ps_ctl == NULL || ctx->ps_ctl->psp_pid != getpid()) { /* Prohibit new files, sockets, etc */ #if (defined(__linux__) || defined(__sun) || defined(__OpenBSD__)) && \ !defined(HAVE_KQUEUE) && !defined(HAVE_EPOLL) @@ -318,17 +319,17 @@ ps_rights_limit_stdio(struct dhcpcd_ctx *ctx) #endif pid_t -ps_dostart(struct dhcpcd_ctx *ctx, - pid_t *priv_pid, int *priv_fd, +ps_startprocess(struct ps_process *psp, void (*recv_msg)(void *, unsigned short), void (*recv_unpriv_msg)(void *, unsigned short), - void *recv_ctx, int (*callback)(void *), void (*signal_cb)(int, void *), + int (*callback)(struct ps_process *), void (*signal_cb)(int, void *), unsigned int flags) { + struct dhcpcd_ctx *ctx = psp->psp_ctx; int fd[2]; pid_t pid; - if (xsocketpair(AF_UNIX, SOCK_DGRAM | SOCK_CXNB, 0, fd) == -1) { + if (xsocketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CXNB, 0, fd) == -1) { logerr("%s: socketpair", __func__); return -1; } @@ -348,32 +349,30 @@ ps_dostart(struct dhcpcd_ctx *ctx, logerr("fork"); return -1; case 0: - *priv_fd = fd[1]; + psp->psp_pid = getpid(); + psp->psp_fd = fd[1]; close(fd[0]); break; default: - *priv_pid = pid; - *priv_fd = fd[0]; + psp->psp_pid = pid; + psp->psp_fd = fd[0]; close(fd[1]); if (recv_unpriv_msg == NULL) ; - else if (eloop_event_add(ctx->eloop, *priv_fd, ELE_READ, - recv_unpriv_msg, recv_ctx) == -1) + else if (eloop_event_add(ctx->eloop, psp->psp_fd, ELE_READ, + recv_unpriv_msg, psp) == -1) { - logerr("%s: eloop_event_add", __func__); + logerr("%s: eloop_event_add fd %d", + __func__, psp->psp_fd); return -1; } return pid; } ctx->options |= DHCPCD_FORKED; - if (ctx->fork_fd != -1) { - close(ctx->fork_fd); - ctx->fork_fd = -1; - } - pidfile_clean(); - - eloop_clear(ctx->eloop, loggetfd(), -1); + if (ctx->ps_log_fd != -1) + logsetfd(ctx->ps_log_fd); + eloop_clear(ctx->eloop, -1); eloop_forked(ctx->eloop); eloop_signal_set_cb(ctx->eloop, dhcpcd_signals, dhcpcd_signals_len, signal_cb, ctx); @@ -383,16 +382,31 @@ ps_dostart(struct dhcpcd_ctx *ctx, goto errexit; } - /* We are not root */ - if (priv_fd != &ctx->ps_root_fd) { - ps_freeprocesses(ctx, recv_ctx); - if (ctx->ps_root_fd != -1) { - close(ctx->ps_root_fd); - ctx->ps_root_fd = -1; - } + if (ctx->fork_fd != -1) { + /* Already removed from eloop thanks to above clear. */ + close(ctx->fork_fd); + ctx->fork_fd = -1; + } + /* This process has no need of the blocking inner eloop. */ + if (!(flags & PSF_ELOOP)) { + eloop_free(ctx->ps_eloop); + ctx->ps_eloop = NULL; + } else + eloop_forked(ctx->ps_eloop); + + pidfile_clean(); + ps_freeprocesses(ctx, psp); + + if (ctx->ps_root != psp) { + ctx->options &= ~DHCPCD_PRIVSEPROOT; + ctx->ps_root = NULL; + if (ctx->ps_log_root_fd != -1) { + /* Already removed from eloop thanks to above clear. */ + close(ctx->ps_log_root_fd); + ctx->ps_log_root_fd = -1; + } #ifdef PRIVSEP_RIGHTS - /* We cannot limit the root process in any way. */ if (ps_rights_limit_stdio(ctx) == -1) { logerr("ps_rights_limit_stdio"); goto errexit; @@ -400,19 +414,26 @@ ps_dostart(struct dhcpcd_ctx *ctx, #endif } - if (priv_fd != &ctx->ps_inet_fd && ctx->ps_inet_fd != -1) { - close(ctx->ps_inet_fd); - ctx->ps_inet_fd = -1; - } + if (ctx->ps_inet != psp) + ctx->ps_inet = NULL; + if (ctx->ps_ctl != psp) + ctx->ps_ctl = NULL; + +#if 0 + char buf[1024]; + errno = 0; + ssize_t xx = recv(psp->psp_fd, buf, sizeof(buf), MSG_PEEK); + logerr("pid %d test fd %d recv peek %zd", getpid(), psp->psp_fd, xx); +#endif - if (eloop_event_add(ctx->eloop, *priv_fd, ELE_READ, - recv_msg, recv_ctx) == -1) + if (eloop_event_add(ctx->eloop, psp->psp_fd, ELE_READ, + recv_msg, psp) == -1) { - logerr("%s: eloop_event_add", __func__); + logerr("%d %s: eloop_event_add XX fd %d", getpid(), __func__, psp->psp_fd); goto errexit; } - if (callback(recv_ctx) == -1) + if (callback(psp) == -1) goto errexit; if (flags & PSF_DROPPRIVS) @@ -421,38 +442,57 @@ ps_dostart(struct dhcpcd_ctx *ctx, return 0; errexit: - /* Failure to start root or inet processes is fatal. */ - if (priv_fd == &ctx->ps_root_fd || priv_fd == &ctx->ps_inet_fd) - (void)ps_sendcmd(ctx, *priv_fd, PS_STOP, 0, NULL, 0); - shutdown(*priv_fd, SHUT_RDWR); - *priv_fd = -1; + if (psp->psp_fd != -1) { + close(psp->psp_fd); + psp->psp_fd = -1; + } eloop_exit(ctx->eloop, EXIT_FAILURE); return -1; } +void +ps_process_timeout(void *arg) +{ + struct dhcpcd_ctx *ctx = arg; + + logerrx("%s: timed out", __func__); + eloop_exit(ctx->eloop, EXIT_FAILURE); +} + int -ps_dostop(struct dhcpcd_ctx *ctx, pid_t *pid, int *fd) +ps_stopprocess(struct ps_process *psp) { int err = 0; + if (psp == NULL) + return 0; + #ifdef PRIVSEP_DEBUG - logdebugx("%s: pid=%d fd=%d", __func__, *pid, *fd); + logdebugx("%s: me=%d pid=%d fd=%d %s", __func__, + getpid(), psp->psp_pid, psp->psp_fd, psp->psp_name); #endif - if (*fd != -1) { - eloop_event_delete(ctx->eloop, *fd); - if (ps_sendcmd(ctx, *fd, PS_STOP, 0, NULL, 0) == -1) { + if (psp->psp_fd != -1) { + eloop_event_delete(psp->psp_ctx->eloop, psp->psp_fd); +#if 0 + if (ps_sendcmd(psp->psp_ctx, psp->psp_fd, PS_STOP, 0, + NULL, 0) == -1) + { + logerr("%d %d %s %s", getpid(), psp->psp_pid, psp->psp_name, __func__); + err = -1; + } + shutdown(psp->psp_fd, SHUT_WR); +#else + if (shutdown(psp->psp_fd, SHUT_WR) == -1) { logerr(__func__); err = -1; } - (void)shutdown(*fd, SHUT_RDWR); - close(*fd); - *fd = -1; +#endif + psp->psp_fd = -1; } /* Don't wait for the process as it may not respond to the shutdown * request. We'll reap the process on receipt of SIGCHLD. */ - *pid = 0; return err; } @@ -463,6 +503,13 @@ ps_start(struct dhcpcd_ctx *ctx) TAILQ_INIT(&ctx->ps_processes); + /* We need an inner eloop to block with. */ + if ((ctx->ps_eloop = eloop_new()) == NULL) + return -1; + eloop_signal_set_cb(ctx->ps_eloop, + dhcpcd_signals, dhcpcd_signals_len, + dhcpcd_signal_cb, ctx); + switch (pid = ps_root_start(ctx)) { case -1: logerr("ps_root_start"); @@ -553,7 +600,7 @@ ps_managersandbox(struct dhcpcd_ctx *ctx, const char *_pledge) * If it cannot be opened before chrooting then syslog(3) will fail. * openlog(3) does not return an error which doubly sucks. */ - if (ctx->ps_root_fd == -1) { + if (ctx->ps_root == NULL) { unsigned int logopts = loggetopts(); logopts &= ~LOGERR_LOG; @@ -602,35 +649,85 @@ ps_stop(struct dhcpcd_ctx *ctx) ctx->eloop == NULL) return 0; - r = ps_ctl_stop(ctx); - if (r != 0) - ret = r; + if (ctx->ps_ctl != NULL) { + r = ps_ctl_stop(ctx); + if (r != 0) + ret = r; + } - r = ps_inet_stop(ctx); - if (r != 0) - ret = r; + if (ctx->ps_inet != NULL) { + r = ps_inet_stop(ctx); + if (r != 0) + ret = r; + } - /* We've been chrooted, so we need to tell the - * privileged proxy to remove the pidfile. */ - if (ps_root_unlink(ctx, ctx->pidfile) == -1) - ret = -1; + if (ctx->ps_root != NULL) { + if (ps_root_stopprocesses(ctx) == -1) + ret = -1; + } return ret; } +int +ps_stopwait(struct dhcpcd_ctx *ctx) +{ + int error = EXIT_SUCCESS; + + if (ctx->ps_eloop != NULL && PS_WAITING_FOR_PROCESSES(ctx)) { + int waited; + + ctx->options |= DHCPCD_EXITING; + if (eloop_timeout_add_sec(ctx->ps_eloop, PS_PROCESS_TIMEOUT, + ps_process_timeout, ctx) == -1) + logerr("%s: eloop_timeout_add_sec", __func__); + eloop_enter(ctx->ps_eloop); + waited = eloop_start(ctx->ps_eloop, &ctx->sigset); + if (waited != EXIT_SUCCESS) { + logerr("%s: eloop_start", __func__); + error = waited; + } + eloop_timeout_delete(ctx->ps_eloop, ps_process_timeout, ctx); + } + + if (IN_PRIVSEP_SE(ctx) && ctx->ps_root != NULL) { + struct ps_process *psp = ctx->ps_root; + + if (ps_root_unlink(ctx, ctx->pidfile) == -1) + logerr("%s: ps_root_unlink", __func__); + + /* We cannot log the root process exited before we + * log dhcpcd exits because the latter requires the former. + * So we just log the intent to exit. */ + logdebugx("%s%s%s will exit from PID %d", + psp->psp_ifname, + psp->psp_ifname[0] != '\0' ? ": " : "", + psp->psp_name, psp->psp_pid); + } + + return error; +} + void ps_freeprocess(struct ps_process *psp) { + struct dhcpcd_ctx *ctx = psp->psp_ctx; - TAILQ_REMOVE(&psp->psp_ctx->ps_processes, psp, next); + TAILQ_REMOVE(&ctx->ps_processes, psp, next); if (psp->psp_fd != -1) { - eloop_event_delete(psp->psp_ctx->eloop, psp->psp_fd); + eloop_event_delete(ctx->eloop, psp->psp_fd); close(psp->psp_fd); } if (psp->psp_work_fd != -1) { - eloop_event_delete(psp->psp_ctx->eloop, psp->psp_work_fd); + eloop_event_delete(ctx->eloop, psp->psp_work_fd); close(psp->psp_work_fd); } + if (ctx->ps_root == psp) + ctx->ps_root = NULL; + if (ctx->ps_inet == psp) + ctx->ps_inet = NULL; + if (ctx->ps_ctl == psp) + ctx->ps_ctl = NULL; #ifdef INET if (psp->psp_bpf != NULL) bpf_close(psp->psp_bpf); @@ -641,12 +738,23 @@ ps_freeprocess(struct ps_process *psp) static void ps_free(struct dhcpcd_ctx *ctx) { - struct ps_process *psp; - bool stop = ctx->ps_root_pid == getpid(); + struct ps_process *ppsp, *psp; + bool stop; + + if (ctx->ps_root != NULL) + ppsp = ctx->ps_root; + else if (ctx->ps_ctl != NULL) + ppsp = ctx->ps_ctl; + else + ppsp = NULL; + if (ppsp != NULL) + stop = ppsp->psp_pid == getpid(); + else + stop = false; while ((psp = TAILQ_FIRST(&ctx->ps_processes)) != NULL) { - if (stop) - ps_dostop(ctx, &psp->psp_pid, &psp->psp_fd); + if (stop && psp != ppsp) + ps_stopprocess(psp); ps_freeprocess(psp); } } @@ -739,7 +847,6 @@ ps_sendpsmmsg(struct dhcpcd_ctx *ctx, int fd, len = writev(fd, iov, iovlen); if (len == -1) { - logerr(__func__); if (ctx->options & DHCPCD_FORKED && !(ctx->options & DHCPCD_PRIVSEPROOT)) eloop_exit(ctx->eloop, EXIT_FAILURE); @@ -883,27 +990,24 @@ ps_recvmsg(struct dhcpcd_ctx *ctx, int rfd, unsigned short events, }; ssize_t len; - if (events != ELE_READ) + if (!(events & ELE_READ)) logerrx("%s: unexpected event 0x%04x", __func__, events); len = recvmsg(rfd, &msg, 0); - if (len == -1) logerr("%s: recvmsg", __func__); if (len == -1 || len == 0) { - if (ctx->options & DHCPCD_FORKED && - !(ctx->options & DHCPCD_PRIVSEPROOT)) + if (ctx->options & DHCPCD_FORKED) eloop_exit(ctx->eloop, - len == 0 ? EXIT_SUCCESS : EXIT_FAILURE); + len != -1 ? EXIT_SUCCESS : EXIT_FAILURE); return len; } iov[0].iov_len = (size_t)len; len = ps_sendcmdmsg(wfd, cmd, &msg); if (len == -1) { - logerr("ps_sendcmdmsg"); - if (ctx->options & DHCPCD_FORKED && - !(ctx->options & DHCPCD_PRIVSEPROOT)) + logerr("%s: ps_sendcmdmsg", __func__); + if (ctx->options & DHCPCD_FORKED) eloop_exit(ctx->eloop, EXIT_FAILURE); } return len; @@ -921,7 +1025,7 @@ ps_recvpsmsg(struct dhcpcd_ctx *ctx, int fd, unsigned short events, struct msghdr msg = { .msg_iov = iov, .msg_iovlen = 1 }; bool stop = false; - if (events != ELE_READ) + if (!(events & ELE_READ)) logerrx("%s: unexpected event 0x%04x", __func__, events); len = read(fd, &psm, sizeof(psm)); @@ -945,6 +1049,7 @@ ps_recvpsmsg(struct dhcpcd_ctx *ctx, int fd, unsigned short events, } if (stop) { + ctx->options |= DHCPCD_EXITING; #ifdef PRIVSEP_DEBUG logdebugx("process %d stopping", getpid()); #endif @@ -980,6 +1085,19 @@ ps_findprocess(struct dhcpcd_ctx *ctx, struct ps_id *psid) return NULL; } +struct ps_process * +ps_findprocesspid(struct dhcpcd_ctx *ctx, pid_t pid) +{ + struct ps_process *psp; + + TAILQ_FOREACH(psp, &ctx->ps_processes, next) { + if (psp->psp_pid == pid) + return psp; + } + errno = ESRCH; + return NULL; +} + struct ps_process * ps_newprocess(struct dhcpcd_ctx *ctx, struct ps_id *psid) { @@ -991,6 +1109,8 @@ ps_newprocess(struct dhcpcd_ctx *ctx, struct ps_id *psid) psp->psp_ctx = ctx; memcpy(&psp->psp_id, psid, sizeof(psp->psp_id)); psp->psp_work_fd = -1; + if (!(ctx->options & DHCPCD_MANAGER)) + strlcpy(psp->psp_ifname, ctx->ifv[0], sizeof(psp->psp_name)); TAILQ_INSERT_TAIL(&ctx->ps_processes, psp, next); return psp; } diff --git a/src/privsep.h b/src/privsep.h index 57fa7515..57a562e4 100644 --- a/src/privsep.h +++ b/src/privsep.h @@ -33,6 +33,7 @@ /* Start flags */ #define PSF_DROPPRIVS 0x01 +#define PSF_ELOOP 0x02 /* Protocols */ #define PS_BOOTP 0x0001 @@ -53,14 +54,20 @@ #define PS_CTL 0x0018 #define PS_CTL_EOF 0x0019 #define PS_LOGREOPEN 0x0020 +#define PS_STOPPROCS 0x0021 + +/* Domains */ +#define PS_ROOT 0x0101 +#define PS_INET 0x0102 +#define PS_CONTROL 0x0103 /* BSD Commands */ -#define PS_IOCTLLINK 0x0101 -#define PS_IOCTL6 0x0102 -#define PS_IOCTLINDIRECT 0x0103 -#define PS_IP6FORWARDING 0x0104 -#define PS_GETIFADDRS 0x0105 -#define PS_IFIGNOREGRP 0x0106 +#define PS_IOCTLLINK 0x0201 +#define PS_IOCTL6 0x0202 +#define PS_IOCTLINDIRECT 0x0203 +#define PS_IP6FORWARDING 0x0204 +#define PS_GETIFADDRS 0x0205 +#define PS_IFIGNOREGRP 0x0206 /* Dev Commands */ #define PS_DEV_LISTENING 0x1001 @@ -87,12 +94,24 @@ CMSG_SPACE(sizeof(struct in6_pktinfo) + \ sizeof(int))) +#define PSP_NAMESIZE 16 + INET_MAX_ADDRSTRLEN + /* Handy macro to work out if in the privsep engine or not. */ #define IN_PRIVSEP(ctx) \ ((ctx)->options & DHCPCD_PRIVSEP) #define IN_PRIVSEP_SE(ctx) \ (((ctx)->options & (DHCPCD_PRIVSEP | DHCPCD_FORKED)) == DHCPCD_PRIVSEP) +#define PS_PROCESS_TIMEOUT 5 /* seconds to stop all processes */ + +/* We always have ourself as a process */ +#define PS_WAITING_FOR_PROCESSES(_ctx) \ + ((IN_PRIVSEP_SE((_ctx)) && \ + TAILQ_LAST(&(_ctx)->ps_processes, ps_process_head) != (_ctx)->ps_root) || \ + (!IN_PRIVSEP_SE((_ctx)) && \ + TAILQ_FIRST(&(_ctx)->ps_processes) != \ + TAILQ_LAST(&(_ctx)->ps_processes, ps_process_head))) + #if defined(PRIVSEP) && defined(HAVE_CAPSICUM) #define PRIVSEP_RIGHTS #endif @@ -154,6 +173,7 @@ struct ps_process { int psp_work_fd; unsigned int psp_ifindex; char psp_ifname[IF_NAMESIZE]; + char psp_name[PSP_NAMESIZE]; uint16_t psp_proto; const char *psp_protostr; @@ -175,6 +195,7 @@ TAILQ_HEAD(ps_process_head, ps_process); int ps_init(struct dhcpcd_ctx *); int ps_start(struct dhcpcd_ctx *); int ps_stop(struct dhcpcd_ctx *); +int ps_stopwait(struct dhcpcd_ctx *); int ps_entersandbox(const char *, const char **); int ps_managersandbox(struct dhcpcd_ctx *, const char *); @@ -207,16 +228,16 @@ int ps_rights_limit_fdpair(int []); int ps_seccomp_enter(void); #endif -pid_t ps_dostart(struct dhcpcd_ctx * ctx, - pid_t *priv_pid, int *priv_fd, +pid_t ps_startprocess(struct ps_process *, void (*recv_msg)(void *, unsigned short), void (*recv_unpriv_msg)(void *, unsigned short), - void *recv_ctx, int (*callback)(void *), void (*)(int, void *), + int (*callback)(struct ps_process *), void (*)(int, void *), unsigned int); -int ps_dostop(struct dhcpcd_ctx *ctx, pid_t *pid, int *fd); - +int ps_stopprocess(struct ps_process *); struct ps_process *ps_findprocess(struct dhcpcd_ctx *, struct ps_id *); +struct ps_process *ps_findprocesspid(struct dhcpcd_ctx *, pid_t); struct ps_process *ps_newprocess(struct dhcpcd_ctx *, struct ps_id *); +void ps_process_timeout(void *); void ps_freeprocess(struct ps_process *); void ps_freeprocesses(struct dhcpcd_ctx *, struct ps_process *); #endif