changeset 5204:47f18579daae draft

privsep: Implement pledge(2) support as found on OpenBSD
author Roy Marples <roy@marples.name>
date Sun, 10 May 2020 16:09:54 +0100
parents 9d2d76abda6a
children cac489d5a8fa
files configure src/dhcpcd.c src/if-bsd.c src/if.c src/if.h src/ipv6.c src/ipv6nd.c src/privsep-bpf.c src/privsep-bsd.c src/privsep-inet.c src/privsep-root.c src/privsep-root.h src/privsep.c src/privsep.h
diffstat 14 files changed, 141 insertions(+), 46 deletions(-) [+]
line wrap: on
line diff
--- a/configure	Sun May 10 12:19:59 2020 +0100
+++ b/configure	Sun May 10 16:09:54 2020 +0100
@@ -663,6 +663,21 @@
 		echo "no"
 	fi
 	rm -f _capsicum.c _capsicum
+
+	printf "Testing for pledge ... "
+        cat <<EOF >_pledge.c
+#include <unistd.h>
+int main(void) {
+        return pledge("stdio", NULL);
+}
+EOF
+        if $XCC _pledge.c -o _pledge 2>&3; then
+                echo "yes"
+                echo "#define   HAVE_PLEDGE" >>$CONFIG_H
+        else
+                echo "no"
+        fi
+        rm -f _pledge.c _pledge
 fi
 
 if [ "$OS" = linux ]; then
--- a/src/dhcpcd.c	Sun May 10 12:19:59 2020 +0100
+++ b/src/dhcpcd.c	Sun May 10 16:09:54 2020 +0100
@@ -1152,6 +1152,7 @@
 	logerrx("route socket overflowed (rcvbuflen %d)"
 	    " - learning interface state", rcvbuflen);
 
+#ifndef HAVE_PLEDGE
 	/* Close the existing socket and open a new one.
 	 * This is easier than draining the kernel buffer of an
 	 * in-determinate size. */
@@ -1163,6 +1164,8 @@
 		eloop_exit(ctx->eloop, EXIT_FAILURE);
 		return;
 	}
+#endif
+
 #ifndef SMALL
 	dhcpcd_setlinkrcvbuf(ctx);
 #endif
@@ -2235,20 +2238,6 @@
 	    (DHCPCD_MASTER | DHCPCD_DEV))
 		dev_start(&ctx);
 
-#ifdef PRIVSEP
-	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
-
 	setproctitle("%s%s%s",
 	    ctx.options & DHCPCD_MASTER ? "[master]" : argv[optind],
 	    ctx.options & DHCPCD_IPV4 ? " [ip4]" : "",
@@ -2262,6 +2251,20 @@
 	dhcpcd_setlinkrcvbuf(&ctx);
 #endif
 
+#ifdef PRIVSEP
+	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, PSF_PLEDGE) == -1) {
+			logerr("ps_dropprivs");
+			goto exit_failure;
+		}
+	}
+#endif
+
 	/* When running dhcpcd against a single interface, we need to retain
 	 * the old behaviour of waiting for an IP address */
 	if (ctx.ifc == 1 && !(ctx.options & DHCPCD_BACKGROUND))
--- a/src/if-bsd.c	Sun May 10 12:19:59 2020 +0100
+++ b/src/if-bsd.c	Sun May 10 16:09:54 2020 +0100
@@ -298,6 +298,9 @@
 		return true;
 
 #ifdef SIOCGIFGROUP
+#ifdef HAVE_PLEDGE
+#warning Fix SIOCGIFGROUP for to remove inet pledge requirement
+#endif
 	struct ifgroupreq ifgr = { .ifgr_len = 0 };
 	struct ifg_req *ifg;
 	size_t ifg_len;
@@ -354,27 +357,25 @@
 }
 
 #if defined(SIOCG80211NWID) || defined(SIOCGETVLAN)
-static int if_direct_ioctl(int s, const char *ifname,
-    unsigned long cmd, void *data)
+static int if_indirect_ioctl(struct dhcpcd_ctx *ctx,
+    const char *ifname, unsigned long cmd, void *data, size_t len)
 {
 
+#ifdef HAVE_PLEDGE
+	return (int)ps_root_indirectioctl(ctx, cmd, ifname, data, len);
+#else
+	struct ifreq ifr = { .ifr_flags = 0 };
+
 	strlcpy(data, ifname, IFNAMSIZ);
-	return ioctl(s, cmd, data);
-}
-
-static int if_indirect_ioctl(int s, const char *ifname,
-    unsigned long cmd, void *data)
-{
-	struct ifreq ifr;
-
-	memset(&ifr, 0, sizeof(ifr));
 	ifr.ifr_data = data;
-	return if_direct_ioctl(s, ifname, cmd, &ifr);
+	UNUSED(len);
+	return ioctl(ctx->pf_inet_fd, cmd, &ifr);
+#endif
 }
 #endif
 
 static int
-if_getssid1(int s, const char *ifname, void *ssid)
+if_getssid1(struct dhcpcd_ctx *ctx, const char *ifname, void *ssid)
 {
 	int retval = -1;
 #if defined(SIOCG80211NWID)
@@ -386,7 +387,9 @@
 
 #if defined(SIOCG80211NWID) /* NetBSD */
 	memset(&nwid, 0, sizeof(nwid));
-	if (if_indirect_ioctl(s, ifname, SIOCG80211NWID, &nwid) == 0) {
+	if (if_indirect_ioctl(ctx, ifname, SIOCG80211NWID,
+	    &nwid, sizeof(nwid)) == 0)
+	{
 		if (ssid == NULL)
 			retval = nwid.i_len;
 		else if (nwid.i_len > IF_SSIDLEN)
@@ -403,7 +406,7 @@
 	ireq.i_val = -1;
 	memset(nwid, 0, sizeof(nwid));
 	ireq.i_data = &nwid;
-	if (ioctl(s, SIOCG80211, &ireq) == 0) {
+	if (ioctl(ctx->pf_inet_fd, SIOCG80211, &ireq) == 0) {
 		if (ssid == NULL)
 			retval = ireq.i_len;
 		else if (ireq.i_len > IF_SSIDLEN)
@@ -425,7 +428,7 @@
 {
 	int r;
 
-	r = if_getssid1(ifp->ctx->pf_inet_fd, ifp->name, ifp->ssid);
+	r = if_getssid1(ifp->ctx, ifp->name, ifp->ssid);
 	if (r != -1)
 		ifp->ssid_len = (unsigned int)r;
 	else
@@ -442,7 +445,7 @@
  * returning the SSID gives an error.
  */
 int
-if_vimaster(const struct dhcpcd_ctx *ctx, const char *ifname)
+if_vimaster(struct dhcpcd_ctx *ctx, const char *ifname)
 {
 	int r;
 	struct ifmediareq ifmr;
@@ -455,7 +458,7 @@
 	if (ifmr.ifm_status & IFM_AVALID &&
 	    IFM_TYPE(ifmr.ifm_active) == IFM_IEEE80211)
 	{
-		if (if_getssid1(ctx->pf_inet_fd, ifname, NULL) == -1)
+		if (if_getssid1(ctx, ifname, NULL) == -1)
 			return 1;
 	}
 	return 0;
@@ -465,17 +468,15 @@
 if_vlanid(const struct interface *ifp)
 {
 #ifdef SIOCGETVLAN
-	struct vlanreq vlr;
+	struct vlanreq vlr = { .vlr_tag = 0 };
 
-	memset(&vlr, 0, sizeof(vlr));
-	if (if_indirect_ioctl(ifp->ctx->pf_inet_fd,
-	    ifp->name, SIOCGETVLAN, &vlr) != 0)
+	if (if_indirect_ioctl(ifp->ctx, ifp->name, SIOCGETVLAN,
+	    &vlr, sizeof(vlr)) != 0)
 		return 0; /* 0 means no VLANID */
 	return vlr.vlr_tag;
 #elif defined(SIOCGVNETID)
-	struct ifreq ifr;
+	struct ifreq ifr = { .ifr_vnetid = 0 };
 
-	memset(&ifr, 0, sizeof(ifr));
 	strlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name));
 	if (ioctl(ifp->ctx->pf_inet_fd, SIOCGVNETID, &ifr) != 0)
 		return 0; /* 0 means no VLANID */
@@ -1569,6 +1570,7 @@
 	char march[SYS_NMLN];
 	size_t marchlen = sizeof(march);
 
+return -1;
 	if (sysctl(mib, sizeof(mib) / sizeof(mib[0]),
 	    march, &marchlen, NULL, 0) != 0)
 		return -1;
--- a/src/if.c	Sun May 10 12:19:59 2020 +0100
+++ b/src/if.c	Sun May 10 16:09:54 2020 +0100
@@ -617,7 +617,7 @@
 		/* Respect the interface priority */
 		memset(&ifr, 0, sizeof(ifr));
 		strlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name));
-		if (ioctl(ctx->pf_inet_fd, SIOCGIFPRIORITY, &ifr) == 0)
+		if (pioctl(ctx, SIOCGIFPRIORITY, &ifr, sizeof(ifr)) == 0)
 			ifp->metric = (unsigned int)ifr.ifr_metric;
 		if_getssid(ifp);
 #else
@@ -771,7 +771,8 @@
 	if (mtu != 0)
 		r = if_ioctl(ifp->ctx, SIOCSIFMTU, &ifr, sizeof(ifr));
 	else
-		r = ioctl(ifp->ctx->pf_inet_fd, SIOCGIFMTU, &ifr);
+		r = pioctl(ifp->ctx, SIOCGIFMTU, &ifr, sizeof(ifr));
+
 	if (r == -1)
 		return -1;
 	return ifr.ifr_mtu;
--- a/src/if.h	Sun May 10 12:19:59 2020 +0100
+++ b/src/if.h	Sun May 10 16:09:54 2020 +0100
@@ -126,6 +126,11 @@
 #endif
 
 int if_ioctl(struct dhcpcd_ctx *, ioctl_request_t, void *, size_t);
+#ifdef HAVE_PLEDGE
+#define	pioctl(ctx, req, data, len) if_ioctl((ctx), (req), (data), (len))
+#else
+#define	pioctl(ctx, req, data, len) ioctl((ctx)->pf_inet_fd, (req),(data),(len))
+#endif
 int if_getflags(struct interface *);
 int if_setflag(struct interface *, short, short);
 #define if_up(ifp) if_setflag((ifp), (IFF_UP | IFF_RUNNING), 0)
@@ -171,7 +176,7 @@
 int if_init(struct interface *);
 int if_getssid(struct interface *);
 bool if_ignore(struct dhcpcd_ctx *, const char *);
-int if_vimaster(const struct dhcpcd_ctx *ctx, const char *);
+int if_vimaster(struct dhcpcd_ctx *ctx, const char *);
 unsigned short if_vlanid(const struct interface *);
 int if_opensockets(struct dhcpcd_ctx *);
 int if_opensockets_os(struct dhcpcd_ctx *);
--- a/src/ipv6.c	Sun May 10 12:19:59 2020 +0100
+++ b/src/ipv6.c	Sun May 10 16:09:54 2020 +0100
@@ -69,6 +69,7 @@
 #include "ipv6.h"
 #include "ipv6nd.h"
 #include "logerr.h"
+#include "privsep.h"
 #include "sa.h"
 #include "script.h"
 
@@ -1094,10 +1095,25 @@
 	struct interface *ifp;
 	struct ipv6_state *state;
 	struct ipv6_addr *ia;
+#ifdef BSD
+	bool forwarding;
+
+#ifdef HAVE_PLEDGE
+	forwarding = ps_root_ip6forwarding(sifp->ctx) == 1;
+#else
+	forwarding = ip6_forwarding(NULL) == 1;
+#endif
+#endif
+
 
 	TAILQ_FOREACH(ifp, sifp->ctx->ifaces, next) {
+#ifdef BSD
+		if (ifp != sifp && !forwarding)
+			continue;
+#else
 		if (ifp != sifp && ip6_forwarding(ifp->name) != 1)
 			continue;
+#endif
 
 		state = IPV6_STATE(ifp);
 		if (state == NULL)
--- a/src/ipv6nd.c	Sun May 10 12:19:59 2020 +0100
+++ b/src/ipv6nd.c	Sun May 10 16:09:54 2020 +0100
@@ -524,7 +524,11 @@
 
 	na->nd_na_type = ND_NEIGHBOR_ADVERT;
 	na->nd_na_flags_reserved = ND_NA_FLAG_OVERRIDE;
+#ifdef HAVE_PLEDGE
+	if (ps_root_ip6_forwarding(ctx) == 1)
+#else
 	if (ip6_forwarding(ifp->name) == 1)
+#endif
 		na->nd_na_flags_reserved |= ND_NA_FLAG_ROUTER;
 	na->nd_na_target = ia->addr;
 
--- a/src/privsep-bpf.c	Sun May 10 12:19:59 2020 +0100
+++ b/src/privsep-bpf.c	Sun May 10 16:09:54 2020 +0100
@@ -203,6 +203,7 @@
 	struct iovec *iov = msg->msg_iov;
 	struct interface *ifp;
 	struct ipv4_state *istate;
+	unsigned int flags = PSF_DROPPRIVS | PSF_CAP_ENTER;
 
 	cmd = (uint8_t)(psm->ps_cmd & ~(PS_START | PS_STOP));
 	psp = ps_findprocess(ctx, &psm->ps_id);
@@ -256,12 +257,19 @@
 		psp->psp_proto = ETHERTYPE_ARP;
 		psp->psp_protostr = "ARP";
 		psp->psp_filter = bpf_arp;
+		/*
+		 * Pledge is currently useless for BPF ARP because we cannot
+		 * change the filter:
+		 * http://openbsd-archive.7691.n7.nabble.com/ \
+		 * 	  pledge-bpf-32bit-arch-unbreak-td299901.html
+		 */
 		break;
 #endif
 	case PS_BPF_BOOTP:
 		psp->psp_proto = ETHERTYPE_IP;
 		psp->psp_protostr = "BOOTP";
 		psp->psp_filter = bpf_bootp;
+		flags |= PSF_PLEDGE;
 		break;
 	}
 
@@ -269,7 +277,7 @@
 	    &psp->psp_pid, &psp->psp_fd,
 	    ps_bpf_recvmsg, NULL, psp,
 	    ps_bpf_start_bpf, ps_bpf_signal_bpfcb,
-	    PSF_DROPPRIVS | PSF_CAP_ENTER);
+	    flags);
 	switch (start) {
 	case -1:
 		ps_freeprocess(psp);
--- a/src/privsep-bsd.c	Sun May 10 12:19:59 2020 +0100
+++ b/src/privsep-bsd.c	Sun May 10 16:09:54 2020 +0100
@@ -49,6 +49,7 @@
 	return err;
 }
 
+#ifdef HAVE_PLEDGE
 static ssize_t
 ps_root_doindirectioctl(unsigned long req, void *data, size_t len)
 {
@@ -68,6 +69,7 @@
 		memmove(data, ifr.ifr_data, len - IFNAMSIZ);
 	return err;
 }
+#endif
 
 static ssize_t
 ps_root_doroute(void *data, size_t len)
@@ -97,8 +99,12 @@
 		return ps_root_doioctldom(PF_LINK, psm->ps_flags, data, len);
 	case PS_IOCTL6:
 		return ps_root_doioctldom(PF_INET6, psm->ps_flags, data, len);
+#ifdef HAVE_PLEDGE
 	case PS_IOCTLINDIRECT:
 		return ps_root_doindirectioctl(psm->ps_flags, data, len);
+	case PS_IP6FORWARDING:
+		return ip6_forwarding(NULL);
+#endif
 	case PS_ROUTE:
 		return ps_root_doroute(data, len);
 	default:
@@ -134,6 +140,7 @@
 	return ps_root_ioctldom(ctx, PS_IOCTL6, request, data, len);
 }
 
+#ifdef HAVE_PLEDGE
 ssize_t
 ps_root_indirectioctl(struct dhcpcd_ctx *ctx, unsigned long request,
     const char *ifname, void *data, size_t len)
@@ -149,6 +156,17 @@
 }
 
 ssize_t
+ps_root_ip6forwarding(struct dhcpcd_ctx *ctx)
+{
+
+	if (ps_sendcmd(ctx, ctx->ps_root_fd,
+	    PS_IP6FORWARDING, 0, NULL, 0) == -1)
+		return -1;
+	return ps_root_readerror(ctx, NULL, 0);
+}
+#endif
+
+ssize_t
 ps_root_route(struct dhcpcd_ctx *ctx, void *data, size_t len)
 {
 
--- a/src/privsep-inet.c	Sun May 10 12:19:59 2020 +0100
+++ b/src/privsep-inet.c	Sun May 10 16:09:54 2020 +0100
@@ -308,7 +308,8 @@
 
 	return ps_dostart(ctx, &ctx->ps_inet_pid, &ctx->ps_inet_fd,
 	    ps_inet_recvmsg, ps_inet_dodispatch, ctx,
-	    ps_inet_startcb, ps_inet_signalcb, PSF_DROPPRIVS);
+	    ps_inet_startcb, ps_inet_signalcb,
+	    PSF_DROPPRIVS | PSF_PLEDGE);
 }
 
 int
@@ -553,7 +554,8 @@
 	start = ps_dostart(ctx,
 	    &psp->psp_pid, &psp->psp_fd,
 	    ps_inet_recvmsgpsp, NULL, psp,
-	    start_func, ps_inet_signalcb, PSF_DROPPRIVS);
+	    start_func, ps_inet_signalcb,
+	    PSF_DROPPRIVS | PSF_PLEDGE);
 	switch (start) {
 	case -1:
 		ps_freeprocess(psp);
--- a/src/privsep-root.c	Sun May 10 12:19:59 2020 +0100
+++ b/src/privsep-root.c	Sun May 10 16:09:54 2020 +0100
@@ -380,7 +380,7 @@
 		break;
 	}
 
-	assert(msg->msg_iovlen == 1);
+	assert(msg->msg_iovlen == 0 || msg->msg_iovlen == 1);
 
 	/* Reset errno */
 	errno = 0;
--- a/src/privsep-root.h	Sun May 10 12:19:59 2020 +0100
+++ b/src/privsep-root.h	Sun May 10 16:09:54 2020 +0100
@@ -46,6 +46,7 @@
 ssize_t ps_root_ioctl6(struct dhcpcd_ctx *, unsigned long, void *, size_t);
 ssize_t ps_root_indirectioctl(struct dhcpcd_ctx *, unsigned long, const char *,
     void *, size_t);
+ssize_t ps_root_ip6forwarding(struct dhcpcd_ctx *);
 #endif
 #ifdef __linux__
 ssize_t ps_root_sendnetlink(struct dhcpcd_ctx *, int, struct msghdr *);
--- a/src/privsep.c	Sun May 10 12:19:59 2020 +0100
+++ b/src/privsep.c	Sun May 10 16:09:54 2020 +0100
@@ -176,6 +176,24 @@
 #else
 	UNUSED(flags);
 #endif
+
+#ifdef HAVE_PLEDGE
+	if (flags & PSF_PLEDGE) {
+		const char *promises;
+
+		if (ctx->options & DHCPCD_UNPRIV)
+			promises = "stdio dns bpf";
+		else
+			/* SIOCGIFGROUP requries inet
+			 * lease files and foo require rpath, wpath and cpath */
+			promises = "stdio dns inet route rpath wpath cpath";
+                if (pledge(promises, NULL) == -1) {
+                        logerr("%s: pledge", __func__);
+                        return -1;
+                }
+	}
+#endif
+
 	return 0;
 }
 
--- a/src/privsep.h	Sun May 10 12:19:59 2020 +0100
+++ b/src/privsep.h	Sun May 10 16:09:54 2020 +0100
@@ -34,6 +34,7 @@
 /* Start flags */
 #define	PSF_DROPPRIVS		0x01
 #define	PSF_CAP_ENTER		0x02
+#define	PSF_PLEDGE		0x04
 
 /* Commands */
 #define	PS_BOOTP		0x01
@@ -53,9 +54,10 @@
 #define	PS_IOCTLLINK		0x15
 #define	PS_IOCTL6		0x16
 #define	PS_IOCTLINDIRECT	0x17
+#define	PS_IP6FORWARDING	0x18
 
 /* Linux commands */
-#define	PS_WRITEPATHUINT	0x18
+#define	PS_WRITEPATHUINT	0x19
 
 #define	PS_DELETE		0x20
 #define	PS_START		0x40