From: Roy Marples Date: Wed, 24 Aug 2022 09:04:19 +0000 (+0100) Subject: capsicum: sysctl NET_RT_DUMP is a privileged operation X-Git-Tag: v9.5.0~28 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4bc35f364df2315793c95e398a87e623597611e4;p=thirdparty%2Fdhcpcd.git capsicum: sysctl NET_RT_DUMP is a privileged operation --- diff --git a/src/if-bsd.c b/src/if-bsd.c index ad5da9fe..1bead9bf 100644 --- a/src/if-bsd.c +++ b/src/if-bsd.c @@ -915,27 +915,42 @@ if_copyrt(struct dhcpcd_ctx *ctx, struct rt *rt, const struct rt_msghdr *rtm) return 0; } +static int +if_sysctl(struct dhcpcd_ctx *ctx, + const int *name, u_int namelen, + void *oldp, size_t *oldlenp, const void *newp, size_t newlen) +{ +#if defined(PRIVSEP) && defined(HAVE_CAPSICUM) + if (IN_PRIVSEP(ctx)) + return (int)ps_root_sysctl(ctx, name, namelen, + oldp, oldlenp, newp, newlen); +#endif + + return sysctl(name, namelen, oldp, oldlenp, newp, newlen); +} + int if_initrt(struct dhcpcd_ctx *ctx, rb_tree_t *kroutes, int af) { struct rt_msghdr *rtm; int mib[6] = { CTL_NET, PF_ROUTE, 0, af, NET_RT_DUMP, 0 }; - size_t needed; + size_t bufl; char *buf, *p, *end; struct rt rt, *rtn; - if (sysctl(mib, __arraycount(mib), NULL, &needed, NULL, 0) == -1) + if (if_sysctl(ctx, mib, __arraycount(mib), NULL, &bufl, NULL, 0) == -1) return -1; - if (needed == 0) + if (bufl == 0) return 0; - if ((buf = malloc(needed)) == NULL) + if ((buf = malloc(bufl)) == NULL) return -1; - if (sysctl(mib, __arraycount(mib), buf, &needed, NULL, 0) == -1) { + if (if_sysctl(ctx, mib, __arraycount(mib), buf, &bufl, NULL, 0) == -1) + { free(buf); return -1; } - end = buf + needed; + end = buf + bufl; for (p = buf; p < end; p += rtm->rtm_msglen) { rtm = (void *)p; if (p + sizeof(*rtm) > end || p + rtm->rtm_msglen > end) { diff --git a/src/privsep-bsd.c b/src/privsep-bsd.c index 22472625..62fa6aa3 100644 --- a/src/privsep-bsd.c +++ b/src/privsep-bsd.c @@ -27,6 +27,7 @@ */ #include +#include /* Need these for filtering the ioctls */ #include @@ -51,6 +52,7 @@ #endif #include +#include #include #include @@ -171,9 +173,79 @@ ps_root_doifignoregroup(void *data, size_t len) } #endif +#ifdef HAVE_CAPSICUM +static ssize_t +ps_root_dosysctl(unsigned long flags, + void *data, size_t len, void **rdata, size_t *rlen) +{ + char *p = data, *e = p + len; + int name[10]; + unsigned int namelen; + void *oldp; + size_t *oldlenp, oldlen, nlen; + void *newp; + size_t newlen; + int err; + + if (sizeof(namelen) >= len) { + errno = EINVAL; + return -1; + } + memcpy(&namelen, p, sizeof(namelen)); + p += sizeof(namelen); + nlen = sizeof(*name) * namelen; + if (namelen > __arraycount(name)) { + errno = ENOBUFS; + return -1; + } + if (p + nlen > e) { + errno = EINVAL; + return -1; + } + memcpy(name, p, nlen); + p += nlen; + if (p + sizeof(oldlen) > e) { + errno = EINVAL; + return -1; + } + memcpy(&oldlen, p, sizeof(oldlen)); + p += sizeof(oldlen); + if (p + sizeof(newlen) > e) { + errno = EINVAL; + return -1; + } + memcpy(&newlen, p, sizeof(newlen)); + p += sizeof(newlen); + if (p + newlen > e) { + errno = EINVAL; + return -1; + } + newp = newlen ? p : NULL; + + if (flags & PS_SYSCTL_OLEN) { + *rlen = sizeof(oldlen) + oldlen; + *rdata = malloc(*rlen); + if (*rdata == NULL) + return -1; + oldlenp = (size_t *)*rdata; + *oldlenp = oldlen; + if (flags & PS_SYSCTL_ODATA) + oldp = (char *)*rdata + sizeof(oldlen); + else + oldp = NULL; + } else { + oldlenp = NULL; + oldp = NULL; + } + + err = sysctl(name, namelen, oldp, oldlenp, newp, newlen); + return err; +} +#endif + ssize_t ps_root_os(struct ps_msghdr *psm, struct msghdr *msg, - void **rdata, size_t *rlen) + void **rdata, size_t *rlen, bool *free_rdata) { struct iovec *iov = msg->msg_iov; void *data = iov->iov_base; @@ -197,6 +269,11 @@ ps_root_os(struct ps_msghdr *psm, struct msghdr *msg, #ifdef HAVE_PLEDGE case PS_IFIGNOREGRP: return ps_root_doifignoregroup(data, len); +#endif +#ifdef HAVE_CAPSICUM + case PS_SYSCTL: + *free_rdata = true; + return ps_root_dosysctl(psm->ps_flags, data, len, rdata, rlen); #endif default: errno = ENOTSUP; @@ -265,7 +342,9 @@ ps_root_indirectioctl(struct dhcpcd_ctx *ctx, unsigned long request, return -1; return ps_root_readerror(ctx, data, len); } +#endif +#ifdef HAVE_PLEDGE ssize_t ps_root_ifignoregroup(struct dhcpcd_ctx *ctx, const char *ifname) { @@ -276,3 +355,58 @@ ps_root_ifignoregroup(struct dhcpcd_ctx *ctx, const char *ifname) return ps_root_readerror(ctx, NULL, 0); } #endif + +#ifdef HAVE_CAPSICUM +ssize_t +ps_root_sysctl(struct dhcpcd_ctx *ctx, + const int *name, unsigned int namelen, + void *oldp, size_t *oldlenp, const void *newp, size_t newlen) +{ + char buf[PS_BUFLEN], *p = buf; + unsigned long flags = 0; + size_t olen = (oldp && oldlenp) ? *oldlenp : 0, nolen; + + if (sizeof(namelen) + (sizeof(*name) * namelen) + + sizeof(oldlenp) + + sizeof(newlen) + newlen > sizeof(buf)) + { + errno = ENOBUFS; + return -1; + } + + if (oldlenp) + flags |= PS_SYSCTL_OLEN; + if (oldp) + flags |= PS_SYSCTL_ODATA; + memcpy(p, &namelen, sizeof(namelen)); + p += sizeof(namelen); + memcpy(p, name, sizeof(*name) * namelen); + p += sizeof(*name) * namelen; + memcpy(p, &olen, sizeof(olen)); + p += sizeof(olen); + memcpy(p, &newlen, sizeof(newlen)); + p += sizeof(newlen); + if (newlen) { + memcpy(p, newp, newlen); + p += newlen; + } + + if (ps_sendcmd(ctx, ctx->ps_root->psp_fd, PS_SYSCTL, + flags, buf, (size_t)(p - buf)) == -1) + return -1; + + if (ps_root_readerror(ctx, buf, sizeof(buf)) == -1) + return -1; + + p = buf; + memcpy(&nolen, p, sizeof(nolen)); + p += sizeof(nolen); + if (oldlenp) { + *oldlenp = nolen; + if (oldp && nolen <= olen) + memcpy(oldp, p, nolen); + } + + return 0; +} +#endif diff --git a/src/privsep-linux.c b/src/privsep-linux.c index ee2e22d2..b238644b 100644 --- a/src/privsep-linux.c +++ b/src/privsep-linux.c @@ -87,7 +87,7 @@ out: ssize_t ps_root_os(struct ps_msghdr *psm, struct msghdr *msg, - __unused void **rdata, __unused size_t *rlen) + __unused void **rdata, __unused size_t *rlen, __unused bool *free_data) { switch (psm->ps_cmd) { diff --git a/src/privsep-root.c b/src/privsep-root.c index 45af3910..a42d84e4 100644 --- a/src/privsep-root.c +++ b/src/privsep-root.c @@ -593,7 +593,7 @@ ps_root_recvmsgcb(void *arg, struct ps_msghdr *psm, struct msghdr *msg) break; #endif default: - err = ps_root_os(psm, msg, &rdata, &rlen); + err = ps_root_os(psm, msg, &rdata, &rlen, &free_rdata); break; } diff --git a/src/privsep-root.h b/src/privsep-root.h index 7fdd9f69..0305f376 100644 --- a/src/privsep-root.h +++ b/src/privsep-root.h @@ -54,7 +54,8 @@ int ps_root_getauthrdm(struct dhcpcd_ctx *, uint64_t *); int ps_root_getifaddrs(struct dhcpcd_ctx *, struct ifaddrs **); #endif -ssize_t ps_root_os(struct ps_msghdr *, struct msghdr *, void **, size_t *); +ssize_t ps_root_os(struct ps_msghdr *, struct msghdr *, + void **, size_t *, bool *); #if defined(BSD) || defined(__sun) ssize_t ps_root_route(struct dhcpcd_ctx *, void *, size_t); ssize_t ps_root_ioctllink(struct dhcpcd_ctx *, unsigned long, void *, size_t); @@ -62,6 +63,8 @@ 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_ifignoregroup(struct dhcpcd_ctx *, const char *); +ssize_t ps_root_sysctl(struct dhcpcd_ctx *, const int *, unsigned int, + void *, size_t *, const void *, size_t); #endif #ifdef __linux__ ssize_t ps_root_sendnetlink(struct dhcpcd_ctx *, int, struct msghdr *); diff --git a/src/privsep-sun.c b/src/privsep-sun.c index 8043eed6..136a9d0c 100644 --- a/src/privsep-sun.c +++ b/src/privsep-sun.c @@ -77,7 +77,7 @@ ps_root_doroute(void *data, size_t len) ssize_t ps_root_os(struct ps_msghdr *psm, struct msghdr *msg, - void **rdata, size_t *rlen) + void **rdata, size_t *rlen, __unused bool *free_rdata) { struct iovec *iov = msg->msg_iov; void *data = iov->iov_base; diff --git a/src/privsep.h b/src/privsep.h index d843dda8..132f679a 100644 --- a/src/privsep.h +++ b/src/privsep.h @@ -61,6 +61,7 @@ #define PS_IP6FORWARDING 0x0104 #define PS_GETIFADDRS 0x0105 #define PS_IFIGNOREGRP 0x0106 +#define PS_SYSCTL 0x0107 /* Dev Commands */ #define PS_DEV_LISTENING 0x1001 @@ -76,6 +77,10 @@ #define PS_CTL_PRIV 0x0004 #define PS_CTL_UNPRIV 0x0005 +/* Sysctl Needs (via flags) */ +#define PS_SYSCTL_OLEN 0x0001 +#define PS_SYSCTL_ODATA 0x0002 + /* Process commands */ #define PS_START 0x4000 #define PS_STOP 0x8000