changeset 5197:b02566d71169 draft

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.
author Roy Marples <roy@marples.name>
date Thu, 07 May 2020 20:57:22 +0100
parents 6f2362548171
children 84674dd6a288
files configure src/dhcpcd.c src/privsep-bpf.c src/privsep-inet.c src/privsep.c src/privsep.h
diffstat 6 files changed, 162 insertions(+), 11 deletions(-) [+]
line wrap: on
line diff
--- a/configure	Thu May 07 15:02:26 2020 +0100
+++ b/configure	Thu May 07 20:57:22 2020 +0100
@@ -648,6 +648,23 @@
 	echo "EMBEDDEDINSTALL=	_embeddedinstall" >>$CONFIG_MK
 fi
 
+if [ "$PRIVSEP" = yes ]; then
+	printf "Testing for capsicum ... "
+	cat <<EOF >_capsicum.c
+#include <sys/capsicum.h>
+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 <<EOF >_nl80211.c
--- a/src/dhcpcd.c	Thu May 07 15:02:26 2020 +0100
+++ b/src/dhcpcd.c	Thu May 07 20:57:22 2020 +0100
@@ -2236,9 +2236,16 @@
 		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
 
--- a/src/privsep-bpf.c	Thu May 07 15:02:26 2020 +0100
+++ b/src/privsep-bpf.c	Thu May 07 20:57:22 2020 +0100
@@ -54,6 +54,10 @@
 #include "logerr.h"
 #include "privsep.h"
 
+#ifdef HAVE_CAPSICUM
+#include <sys/capsicum.h>
+#endif
+
 static void
 ps_bpf_recvbpf(void *arg)
 {
@@ -152,6 +156,13 @@
 {
 	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 @@
 	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 @@
 	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);
--- a/src/privsep-inet.c	Thu May 07 15:02:26 2020 +0100
+++ b/src/privsep-inet.c	Thu May 07 20:57:22 2020 +0100
@@ -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 <sys/capsicum.h>
+#endif
+
 #ifdef INET
 static void
 ps_inet_recvbootp(void *arg)
@@ -92,6 +99,11 @@
 {
 	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 @@
 		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 @@
 	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 @@
 		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 @@
 	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 @@
 		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 @@
 		return -1;
 	}
 
-#ifdef PRIVSEP_DEBUG
 	logdebugx("spawned listener %s on PID %d", buf, getpid());
-#endif
 	return 0;
 }
 #endif
@@ -335,6 +386,11 @@
 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 @@
 		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 @@
 	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 @@
 		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)
 	{
--- a/src/privsep.c	Thu May 07 15:02:26 2020 +0100
+++ b/src/privsep.c	Thu May 07 20:57:22 2020 +0100
@@ -70,6 +70,9 @@
 #include "logerr.h"
 #include "privsep.h"
 
+#ifdef HAVE_CAPSICUM
+#include <sys/capsicum.h>
+#endif
 #ifdef HAVE_UTIL_H
 #include <util.h>
 #endif
@@ -143,7 +146,7 @@
 }
 
 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 @@
 		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 @@
 	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 @@
 		*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 @@
 		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 @@
 	}
 
 	if (flags & PSF_DROPPRIVS)
-		ps_dropprivs(ctx);
+		ps_dropprivs(ctx, flags);
 
 	return 0;
 
--- a/src/privsep.h	Thu May 07 15:02:26 2020 +0100
+++ b/src/privsep.h	Thu May 07 20:57:22 2020 +0100
@@ -33,6 +33,7 @@
 
 /* Start flags */
 #define	PSF_DROPPRIVS		0x01
+#define	PSF_CAP_ENTER		0x02
 
 /* Commands */
 #define	PS_BOOTP		0x01
@@ -139,7 +140,7 @@
 
 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 *);