diff options
author | Roy Marples <roy@marples.name> | 2020-05-10 16:09:54 +0100 |
---|---|---|
committer | Roy Marples <roy@marples.name> | 2020-05-10 16:09:54 +0100 |
commit | 2f9d9eae5fb8fb1922b0e096e157987ac0df110e (patch) | |
tree | c90effa39a8875fdf93f703bd9174a3d7e59dc77 | |
parent | 82d7c0bc10c09e0fb5ae4df1eeaede78377d1c8b (diff) | |
download | dhcpcd-2f9d9eae5fb8fb1922b0e096e157987ac0df110e.tar.xz |
privsep: Implement pledge(2) support as found on OpenBSD
-rwxr-xr-x | configure | 15 | ||||
-rw-r--r-- | src/dhcpcd.c | 31 | ||||
-rw-r--r-- | src/if-bsd.c | 50 | ||||
-rw-r--r-- | src/if.c | 5 | ||||
-rw-r--r-- | src/if.h | 7 | ||||
-rw-r--r-- | src/ipv6.c | 16 | ||||
-rw-r--r-- | src/ipv6nd.c | 4 | ||||
-rw-r--r-- | src/privsep-bpf.c | 10 | ||||
-rw-r--r-- | src/privsep-bsd.c | 18 | ||||
-rw-r--r-- | src/privsep-inet.c | 6 | ||||
-rw-r--r-- | src/privsep-root.c | 2 | ||||
-rw-r--r-- | src/privsep-root.h | 1 | ||||
-rw-r--r-- | src/privsep.c | 18 | ||||
-rw-r--r-- | src/privsep.h | 4 |
14 files changed, 141 insertions, 46 deletions
@@ -663,6 +663,21 @@ EOF 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 diff --git a/src/dhcpcd.c b/src/dhcpcd.c index 4c61c6f0..e17bebc0 100644 --- a/src/dhcpcd.c +++ b/src/dhcpcd.c @@ -1152,6 +1152,7 @@ dhcpcd_linkoverflow(struct dhcpcd_ctx *ctx) 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 @@ dhcpcd_linkoverflow(struct dhcpcd_ctx *ctx) eloop_exit(ctx->eloop, EXIT_FAILURE); return; } +#endif + #ifndef SMALL dhcpcd_setlinkrcvbuf(ctx); #endif @@ -2235,20 +2238,6 @@ printpidfile: (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 @@ printpidfile: 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)) diff --git a/src/if-bsd.c b/src/if-bsd.c index 15d20168..e75952e2 100644 --- a/src/if-bsd.c +++ b/src/if-bsd.c @@ -298,6 +298,9 @@ if_ignore(struct dhcpcd_ctx *ctx, const char *ifname) 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_linkaddr(struct sockaddr_dl *sdl, const struct interface *ifp) } #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) { - 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; +#ifdef HAVE_PLEDGE + return (int)ps_root_indirectioctl(ctx, cmd, ifname, data, len); +#else + struct ifreq ifr = { .ifr_flags = 0 }; - memset(&ifr, 0, sizeof(ifr)); + strlcpy(data, ifname, IFNAMSIZ); 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_getssid1(int s, const char *ifname, void *ssid) #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 @@ if_getssid1(int s, const char *ifname, void *ssid) 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 @@ if_getssid(struct interface *ifp) { 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 @@ if_getssid(struct interface *ifp) * 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_vimaster(const struct dhcpcd_ctx *ctx, const char *ifname) 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 @@ unsigned short 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 @@ if_machinearch(char *str, size_t len) 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; @@ -617,7 +617,7 @@ if_discover(struct dhcpcd_ctx *ctx, struct ifaddrs **ifaddrs, /* 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_domtu(const struct interface *ifp, short int mtu) 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; @@ -126,6 +126,11 @@ int if_getsubnet(struct dhcpcd_ctx *, const char *, int, void *, size_t); #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_conf(struct interface *); 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 *); @@ -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 @@ ipv6_anyglobal(struct interface *sifp) 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) diff --git a/src/ipv6nd.c b/src/ipv6nd.c index ae267c7a..09595bc2 100644 --- a/src/ipv6nd.c +++ b/src/ipv6nd.c @@ -524,7 +524,11 @@ ipv6nd_advertise(struct ipv6_addr *ia) 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; diff --git a/src/privsep-bpf.c b/src/privsep-bpf.c index b1ed821d..fd76576c 100644 --- a/src/privsep-bpf.c +++ b/src/privsep-bpf.c @@ -203,6 +203,7 @@ ps_bpf_cmd(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg) 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 @@ ps_bpf_cmd(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg) 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 @@ ps_bpf_cmd(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg) &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); diff --git a/src/privsep-bsd.c b/src/privsep-bsd.c index 1a105b63..f466156a 100644 --- a/src/privsep-bsd.c +++ b/src/privsep-bsd.c @@ -49,6 +49,7 @@ ps_root_doioctldom(int domain, unsigned long req, void *data, size_t len) return err; } +#ifdef HAVE_PLEDGE static ssize_t ps_root_doindirectioctl(unsigned long req, void *data, size_t len) { @@ -68,6 +69,7 @@ ps_root_doindirectioctl(unsigned long req, void *data, size_t len) 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 @@ ps_root_os(struct ps_msghdr *psm, struct msghdr *msg) 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 @@ ps_root_ioctl6(struct dhcpcd_ctx *ctx, unsigned long request, 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 @@ ps_root_indirectioctl(struct dhcpcd_ctx *ctx, unsigned long request, } 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) { diff --git a/src/privsep-inet.c b/src/privsep-inet.c index f0dc79a2..cb8632ec 100644 --- a/src/privsep-inet.c +++ b/src/privsep-inet.c @@ -308,7 +308,8 @@ ps_inet_start(struct dhcpcd_ctx *ctx) 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 @@ ps_inet_cmd(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, 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); diff --git a/src/privsep-root.c b/src/privsep-root.c index dcfe3091..9584af84 100644 --- a/src/privsep-root.c +++ b/src/privsep-root.c @@ -380,7 +380,7 @@ ps_root_recvmsgcb(void *arg, struct ps_msghdr *psm, struct msghdr *msg) break; } - assert(msg->msg_iovlen == 1); + assert(msg->msg_iovlen == 0 || msg->msg_iovlen == 1); /* Reset errno */ errno = 0; diff --git a/src/privsep-root.h b/src/privsep-root.h index 27a481a6..7f8bdc65 100644 --- a/src/privsep-root.h +++ b/src/privsep-root.h @@ -46,6 +46,7 @@ ssize_t ps_root_ioctllink(struct dhcpcd_ctx *, unsigned long, void *, size_t); 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 *); diff --git a/src/privsep.c b/src/privsep.c index 51fdf7b7..ac503a53 100644 --- a/src/privsep.c +++ b/src/privsep.c @@ -176,6 +176,24 @@ ps_dropprivs(struct dhcpcd_ctx *ctx, unsigned int flags) #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; } diff --git a/src/privsep.h b/src/privsep.h index 9d156e9e..d64210cd 100644 --- a/src/privsep.h +++ b/src/privsep.h @@ -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 |