From: Roy Marples Date: Thu, 7 May 2020 19:57:22 +0000 (+0100) Subject: privsep: Enable capsicum for network facing processes X-Git-Tag: v9.1.0~86 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=08958eaee78ef439774778c4d2b6339ade2ef7bf;p=thirdparty%2Fdhcpcd.git privsep: Enable capsicum for network facing processes All fd's in network facing processes are fully limited. Capability mode is only enabled for BPF processes because it's too restrictive otherwise - the reasons are noted in the commit. --- diff --git a/configure b/configure index ebf104c4..b38fe6c1 100755 --- a/configure +++ b/configure @@ -648,6 +648,23 @@ else echo "EMBEDDEDINSTALL= _embeddedinstall" >>$CONFIG_MK fi +if [ "$PRIVSEP" = yes ]; then + printf "Testing for capsicum ... " + cat <_capsicum.c +#include +int main(void) { + return cap_enter(); +} +EOF + if $XCC _capsicum.c -o _capsicum 2>&3; then + echo "yes" + echo "#define HAVE_CAPSICUM" >>$CONFIG_H + else + echo "no" + fi + rm -f _capsicum.c _capsicum +fi + if [ "$OS" = linux ]; then printf "Testing for nl80211 ... " cat <_nl80211.c diff --git a/src/dhcpcd.c b/src/dhcpcd.c index 060f2e93..4c61c6f0 100644 --- a/src/dhcpcd.c +++ b/src/dhcpcd.c @@ -2236,9 +2236,16 @@ printpidfile: dev_start(&ctx); #ifdef PRIVSEP - if (ctx.options & DHCPCD_PRIVSEP && ps_dropprivs(&ctx) == -1) { - logerr("ps_dropprivs"); - goto exit_failure; + if (ctx.options & DHCPCD_PRIVSEP) { + /* + * PSF_CAP_ENTER is not set because the following functions + * won't work in it: + * getifaddrs(3), gethostname(3), uname(3). + */ + if (ps_dropprivs(&ctx, 0) == -1) { + logerr("ps_dropprivs"); + goto exit_failure; + } } #endif diff --git a/src/privsep-bpf.c b/src/privsep-bpf.c index 28394ea9..b1ed821d 100644 --- a/src/privsep-bpf.c +++ b/src/privsep-bpf.c @@ -54,6 +54,10 @@ #include "logerr.h" #include "privsep.h" +#ifdef HAVE_CAPSICUM +#include +#endif + static void ps_bpf_recvbpf(void *arg) { @@ -152,6 +156,13 @@ ps_bpf_start_bpf(void *arg) { struct ps_process *psp = arg; struct dhcpcd_ctx *ctx = psp->psp_ctx; +#ifdef HAVE_CAPSICUM + cap_rights_t rights; + + /* We need CAP_IOCTL so we can change the BPF filter when we + * need to. */ + cap_rights_init(&rights, CAP_READ, CAP_WRITE, CAP_EVENT, CAP_IOCTL); +#endif setproctitle("[BPF %s] %s", psp->psp_protostr, psp->psp_ifname); @@ -160,6 +171,11 @@ ps_bpf_start_bpf(void *arg) psp->psp_work_fd = bpf_open(&psp->psp_ifp, psp->psp_filter); if (psp->psp_work_fd == -1) logerr("%s: bpf_open",__func__); +#ifdef HAVE_CAPSICUM + else if (cap_rights_limit(psp->psp_work_fd, &rights) == -1 && + errno != ENOSYS) + logerr("%s: cap_rights_limit", __func__); +#endif else if (eloop_event_add(ctx->eloop, psp->psp_work_fd, ps_bpf_recvbpf, psp) == -1) logerr("%s: eloop_event_add", __func__); @@ -252,7 +268,8 @@ ps_bpf_cmd(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg) start = ps_dostart(ctx, &psp->psp_pid, &psp->psp_fd, ps_bpf_recvmsg, NULL, psp, - ps_bpf_start_bpf, ps_bpf_signal_bpfcb, PSF_DROPPRIVS); + ps_bpf_start_bpf, ps_bpf_signal_bpfcb, + PSF_DROPPRIVS | PSF_CAP_ENTER); switch (start) { case -1: ps_freeprocess(psp); diff --git a/src/privsep-inet.c b/src/privsep-inet.c index 0191ccd7..f0dc79a2 100644 --- a/src/privsep-inet.c +++ b/src/privsep-inet.c @@ -45,6 +45,13 @@ #include "logerr.h" #include "privsep.h" +#ifdef HAVE_CAPSICUM +/* We never call ps_dostart with PSF_CAP_ENTER because + * our sockets require the use of CAP_CONNECT which does not + * work in capabilities mode according to rights(4). */ +#include +#endif + #ifdef INET static void ps_inet_recvbootp(void *arg) @@ -92,6 +99,11 @@ ps_inet_startcb(void *arg) { struct dhcpcd_ctx *ctx = arg; int ret = 0; +#ifdef HAVE_CAPSICUM + cap_rights_t rights; + + cap_rights_init(&rights, CAP_RECV, CAP_CONNECT, CAP_SEND, CAP_EVENT); +#endif if (ctx->options & DHCPCD_MASTER) setproctitle("[network proxy]"); @@ -114,6 +126,15 @@ ps_inet_startcb(void *arg) ctx->udp_fd = dhcp_openudp(NULL); if (ctx->udp_fd == -1) logerr("%s: dhcp_open", __func__); +#ifdef HAVE_CAPSICUM + else if (cap_rights_limit(ctx->udp_fd, &rights) == -1 + && errno != ENOSYS) + { + logerr("%s: cap_rights_limit", __func__); + close(ctx->udp_fd); + ctx->udp_fd = -1; + } +#endif else if (eloop_event_add(ctx->eloop, ctx->udp_fd, ps_inet_recvbootp, ctx) == -1) { @@ -128,6 +149,15 @@ ps_inet_startcb(void *arg) if (ctx->options & DHCPCD_IPV6) { if (ipv6nd_open(ctx) == -1) logerr("%s: ipv6nd_open", __func__); +#ifdef HAVE_CAPSICUM + else if (cap_rights_limit(ctx->nd_fd, &rights) == -1 + && errno != ENOSYS) + { + logerr("%s: cap_rights_limit", __func__); + close(ctx->nd_fd); + ctx->nd_fd = -1; + } +#endif else if (eloop_event_add(ctx->eloop, ctx->nd_fd, ps_inet_recvra, ctx) == -1) { @@ -145,6 +175,15 @@ ps_inet_startcb(void *arg) ctx->dhcp6_fd = dhcp6_openudp(0, NULL); if (ctx->dhcp6_fd == -1) logerr("%s: dhcp6_open", __func__); +#ifdef HAVE_CAPSICUM + else if (cap_rights_limit(ctx->dhcp6_fd, &rights) == -1 + && errno != ENOSYS) + { + logerr("%s: cap_rights_limit", __func__); + close(ctx->dhcp6_fd); + ctx->dhcp6_fd = -1; + } +#endif else if (eloop_event_add(ctx->eloop, ctx->dhcp6_fd, ps_inet_recvdhcp6, ctx) == -1) { @@ -296,6 +335,11 @@ ps_inet_listenin(void *arg) struct ps_process *psp = arg; struct in_addr *ia = &psp->psp_id.psi_addr.psa_in_addr; char buf[INET_ADDRSTRLEN]; +#ifdef HAVE_CAPSICUM + cap_rights_t rights; + + cap_rights_init(&rights, CAP_RECV, CAP_CONNECT, CAP_SEND, CAP_EVENT); +#endif inet_ntop(AF_INET, ia, buf, sizeof(buf)); setproctitle("[network proxy] %s", buf); @@ -306,6 +350,15 @@ ps_inet_listenin(void *arg) return -1; } +#ifdef HAVE_CAPSICUM + if (cap_rights_limit(psp->psp_work_fd, &rights) == -1 && + errno != ENOSYS) + { + logerr("%s: cap_rights_limit", __func__); + return -1; + } +#endif + if (eloop_event_add(psp->psp_ctx->eloop, psp->psp_work_fd, ps_inet_recvinbootp, psp) == -1) { @@ -313,9 +366,7 @@ ps_inet_listenin(void *arg) return -1; } -#ifdef PRIVSEP_DEBUG logdebugx("spawned listener %s on PID %d", buf, getpid()); -#endif return 0; } #endif @@ -335,6 +386,11 @@ static int ps_inet_listennd(void *arg) { struct ps_process *psp = arg; +#ifdef HAVE_CAPSICUM + cap_rights_t rights; + + cap_rights_init(&rights, CAP_RECV, CAP_CONNECT, CAP_SEND, CAP_EVENT); +#endif setproctitle("[ND network proxy]"); @@ -344,6 +400,15 @@ ps_inet_listennd(void *arg) return -1; } +#ifdef HAVE_CAPSICUM + if (cap_rights_limit(psp->psp_work_fd, &rights) == -1 && + errno != ENOSYS) + { + logerr("%s: cap_rights_limit", __func__); + return -1; + } +#endif + if (eloop_event_add(psp->psp_ctx->eloop, psp->psp_work_fd, ps_inet_recvin6nd, psp) == -1) { @@ -373,6 +438,11 @@ ps_inet_listenin6(void *arg) struct ps_process *psp = arg; struct in6_addr *ia = &psp->psp_id.psi_addr.psa_in6_addr; char buf[INET6_ADDRSTRLEN]; +#ifdef HAVE_CAPSICUM + cap_rights_t rights; + + cap_rights_init(&rights, CAP_RECV, CAP_CONNECT, CAP_SEND, CAP_EVENT); +#endif inet_ntop(AF_INET6, ia, buf, sizeof(buf)); setproctitle("[network proxy] %s", buf); @@ -383,6 +453,15 @@ ps_inet_listenin6(void *arg) return -1; } +#ifdef HAVE_CAPSICUM + if (cap_rights_limit(psp->psp_work_fd, &rights) == -1 && + errno != ENOSYS) + { + logerr("%s: cap_rights_limit", __func__); + return -1; + } +#endif + if (eloop_event_add(psp->psp_ctx->eloop, psp->psp_work_fd, ps_inet_recvin6dhcp6, psp) == -1) { diff --git a/src/privsep.c b/src/privsep.c index 6ecc17f0..fd0cbd47 100644 --- a/src/privsep.c +++ b/src/privsep.c @@ -70,6 +70,9 @@ #include "logerr.h" #include "privsep.h" +#ifdef HAVE_CAPSICUM +#include +#endif #ifdef HAVE_UTIL_H #include #endif @@ -143,7 +146,7 @@ ps_init(struct dhcpcd_ctx *ctx) } int -ps_dropprivs(struct dhcpcd_ctx *ctx) +ps_dropprivs(struct dhcpcd_ctx *ctx, unsigned int flags) { struct passwd *pw = ctx->ps_user; @@ -163,6 +166,14 @@ ps_dropprivs(struct dhcpcd_ctx *ctx) return -1; } +#ifdef HAVE_CAPSICUM + if (flags & PSF_CAP_ENTER) { + if (cap_enter() == -1 && errno != ENOSYS) { + logerr("%s: cap_enter", __func__); + return -1; + } + } +#endif return 0; } @@ -176,6 +187,11 @@ ps_dostart(struct dhcpcd_ctx *ctx, int stype; int fd[2]; pid_t pid; +#ifdef HAVE_CAPSICUM + cap_rights_t rights; + + cap_rights_init(&rights, CAP_READ, CAP_WRITE, CAP_EVENT, CAP_SHUTDOWN); +#endif stype = SOCK_CLOEXEC | SOCK_NONBLOCK; if (socketpair(AF_UNIX, SOCK_DGRAM | stype, 0, fd) == -1) { @@ -195,8 +211,17 @@ ps_dostart(struct dhcpcd_ctx *ctx, *priv_pid = pid; *priv_fd = fd[0]; close(fd[1]); - if (recv_unpriv_msg != NULL && - eloop_event_add(ctx->eloop, *priv_fd, + if (recv_unpriv_msg == NULL) + ; +#ifdef HAVE_CAPSICUM + else if (cap_rights_limit(*priv_fd, &rights) == -1 + && errno != ENOSYS) + { + logerr("%s: cap_rights_limit", __func__); + return -1; + } +#endif + else if (eloop_event_add(ctx->eloop, *priv_fd, recv_unpriv_msg, recv_ctx) == -1) { logerr("%s: eloop_event_add", __func__); @@ -240,6 +265,11 @@ ps_dostart(struct dhcpcd_ctx *ctx, goto errexit; } +#ifdef HAVE_CAPSICUM + if (cap_rights_limit(*priv_fd, &rights) == -1 && errno != ENOSYS) + goto errexit; +#endif + if (eloop_event_add(ctx->eloop, *priv_fd, recv_msg, recv_ctx) == -1) { logerr("%s: eloop_event_add", __func__); @@ -257,7 +287,7 @@ ps_dostart(struct dhcpcd_ctx *ctx, } if (flags & PSF_DROPPRIVS) - ps_dropprivs(ctx); + ps_dropprivs(ctx, flags); return 0; diff --git a/src/privsep.h b/src/privsep.h index 9cdbb265..cb16ac1f 100644 --- a/src/privsep.h +++ b/src/privsep.h @@ -33,6 +33,7 @@ /* Start flags */ #define PSF_DROPPRIVS 0x01 +#define PSF_CAP_ENTER 0x02 /* Commands */ #define PS_BOOTP 0x01 @@ -139,7 +140,7 @@ TAILQ_HEAD(ps_process_head, ps_process); int ps_mkdir(char *); int ps_init(struct dhcpcd_ctx *); -int ps_dropprivs(struct dhcpcd_ctx *); +int ps_dropprivs(struct dhcpcd_ctx *, unsigned int); int ps_start(struct dhcpcd_ctx *); int ps_stop(struct dhcpcd_ctx *);