]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
capsicum: sysctl NET_RT_DUMP is a privileged operation
authorRoy Marples <roy@marples.name>
Wed, 24 Aug 2022 09:04:19 +0000 (10:04 +0100)
committerRoy Marples <roy@marples.name>
Wed, 24 Aug 2022 09:04:19 +0000 (10:04 +0100)
src/if-bsd.c
src/privsep-bsd.c
src/privsep-linux.c
src/privsep-root.c
src/privsep-root.h
src/privsep-sun.c
src/privsep.h

index b54715a0c462f666a9b9d26f6ed0aa0c15d136c7..e01d3dae63cd9ca757f47777f3df079d6eb8a306 100644 (file)
@@ -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) {
index 9bcddbd6811a33496a25e9145074c58b29d3ce60..b399abceebab276cb5efbda7e6869f69de8d31b7 100644 (file)
@@ -27,6 +27,7 @@
  */
 
 #include <sys/ioctl.h>
+#include <sys/sysctl.h>
 
 /* Need these for filtering the ioctls */
 #include <arpa/inet.h>
@@ -51,6 +52,7 @@
 #endif
 
 #include <errno.h>
+#include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
@@ -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
index 4dfaf66bca4942eae6e6a05b0dbbbb8823e04ffa..c2de54bac63fc27c124876229d4503673b44330b 100644 (file)
@@ -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) {
index 4c404aeb85996cac46071362337cc8bdeb088ad7..ef968d0e30efcc8e0c198002e39f217f894288fa 100644 (file)
@@ -609,7 +609,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;
        }
 
index a54696c30772bcec146feefbf44c9453db08ac9e..98ff101bcc0c970f020f50c45bcfa9cbdc8eef79 100644 (file)
@@ -56,7 +56,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);
@@ -64,6 +65,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 *);
index 8043eed6ef657323f30eecbb5c097585a24044d2..136a9d0c1958363223456758c01cbf6114a3fbd2 100644 (file)
@@ -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;
index deb24b9fe73ab1efeb8c2a1a6947e13a02c81ee2..856848e0f79101f6a76215a6f3255aaeb6e3004b 100644 (file)
@@ -68,6 +68,7 @@
 #define        PS_IP6FORWARDING        0x0204
 #define        PS_GETIFADDRS           0x0205
 #define        PS_IFIGNOREGRP          0x0206
+#define        PS_SYSCTL               0x0207
 
 /* Dev Commands */
 #define        PS_DEV_LISTENING        0x1001
 #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