]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
privsep: Open OS sockets for the privileged process at startup
authorRoy Marples <roy@marples.name>
Tue, 30 Aug 2022 12:46:31 +0000 (13:46 +0100)
committerRoy Marples <roy@marples.name>
Tue, 30 Aug 2022 12:46:31 +0000 (13:46 +0100)
Rather than opening / closing on demand.
This mirrors the behaviour of dhcpcd without privsep and ensures
that dhcpcd always has the resource available to do it's operations
to ensure the network stays up.

This also has the advantage of working around a recent FreeBSD-14
capsicum issue where opening a route socket in the privileged
process without capsicum fails with the same error as if it was
in capsicum.

src/if-bsd.c
src/if-linux.c
src/if-sun.c
src/if.h
src/privsep-bsd.c
src/privsep-linux.c
src/privsep-root.c
src/privsep-root.h

index 62232964c57672e184f81973423e6d8e4cebabc4..519055c9dc293c3fe67df06395be8c0797fbfbf0 100644 (file)
@@ -113,10 +113,6 @@ static const char * const ifnames_ignore[] = {
        NULL
 };
 
-struct priv {
-       int pf_inet6_fd;
-};
-
 struct rtm
 {
        struct rt_msghdr hdr;
@@ -223,6 +219,12 @@ if_opensockets_os(struct dhcpcd_ctx *ctx)
                ps_rights_limit_fd_sockopt(ctx->link_fd);
 #endif
 
+
+#if defined(SIOCALIFADDR) && defined(IFLR_ACTIVE) /*NetBSD */
+       priv->pf_link_fd = socket(PF_LINK, SOCK_DGRAM, 0);
+       if (priv->pf_link_fd == -1)
+               logerr("%s: socket(PF_LINK)", __func__);
+#endif
        return 0;
 }
 
@@ -234,6 +236,10 @@ if_closesockets_os(struct dhcpcd_ctx *ctx)
        priv = (struct priv *)ctx->priv;
        if (priv->pf_inet6_fd != -1)
                close(priv->pf_inet6_fd);
+#if defined(SIOCALIFADDR) && defined(IFLR_ACTIVE) /*NetBSD */
+       if (priv->pf_link_fd != -1)
+               close(priv->pf_link_fd);
+#endif
        free(priv);
        ctx->priv = NULL;
        free(ctx->rt_missfilter);
@@ -243,22 +249,14 @@ if_closesockets_os(struct dhcpcd_ctx *ctx)
 static int
 if_ioctllink(struct dhcpcd_ctx *ctx, unsigned long req, void *data, size_t len)
 {
-       int s;
-       int retval;
+       struct priv *priv = (struct priv *)ctx->priv;
 
 #ifdef PRIVSEP
        if (ctx->options & DHCPCD_PRIVSEP)
                return (int)ps_root_ioctllink(ctx, req, data, len);
-#else
-       UNUSED(ctx);
 #endif
 
-       s = socket(PF_LINK, SOCK_DGRAM, 0);
-       if (s == -1)
-               return -1;
-       retval = ioctl(s, req, data, len);
-       close(s);
-       return retval;
+       return ioctl(priv->pf_link_fd, req, data, len);
 }
 #endif
 
index c3bfd03dc0ccea806a91af1ff4efc1a0ecacfe47..655750a2d216379293e4711216bbdb34a9e2d22a 100644 (file)
@@ -141,12 +141,6 @@ int if_getssid_wext(const char *ifname, uint8_t *ssid);
        (struct rtattr *)(void *)(((char *)(rta)) \
        + RTA_ALIGN((rta)->rta_len)))
 
-struct priv {
-       int route_fd;
-       int generic_fd;
-       uint32_t route_pid;
-};
-
 /* We need this to send a broadcast for InfiniBand.
  * Our old code used sendto, but our new code writes to a raw BPF socket.
  * What header structure does IPoIB use? */
@@ -495,8 +489,10 @@ if_closesockets_os(struct dhcpcd_ctx *ctx)
 
        if (ctx->priv != NULL) {
                priv = (struct priv *)ctx->priv;
-               close(priv->route_fd);
-               close(priv->generic_fd);
+               if (priv->route_fd != -1)
+                       close(priv->route_fd);
+               if (priv->generic_fd != -1)
+                       close(priv->generic_fd);
        }
 }
 
index d25bbc817a29a2edd6e726f612302f69de9262d9..176a8141bc2fd946b7a589618e5a3b4281947332 100644 (file)
@@ -94,12 +94,6 @@ extern int getallifaddrs(sa_family_t, struct ifaddrs **, int64_t);
 
 #define COPYSA(dst, src) memcpy((dst), (src), sa_len((src)))
 
-struct priv {
-#ifdef INET6
-       int pf_inet6_fd;
-#endif
-};
-
 struct rtm
 {
        struct rt_msghdr hdr;
index d24fbc92cf48ca6cb5e6289b74eae249d7ad4385..b8dd1a0c65ec99c12ca6773952832ae5de582583 100644 (file)
--- a/src/if.h
+++ b/src/if.h
@@ -119,7 +119,33 @@ typedef unsigned long              ioctl_request_t;
  * It used to work, but lukily Solaris can fall back to
  * IP_PKTINFO. */
 #undef IP_RECVIF
+#endif
+
+/* Private structures specific to an OS */
+#ifdef BSD
+struct priv {
+#ifdef INET6
+       int pf_inet6_fd;
+#endif
+       int pf_link_fd; /* NetBSD only, but hard to define for here */
+};
+#endif
+#ifdef __linux__
+struct priv {
+       int route_fd;
+       int generic_fd;
+       uint32_t route_pid;
+};
+#endif
+#ifdef __sun
+struct priv {
+#ifdef INET6
+       int pf_inet6_fd;
+#endif
+};
+#endif
 
+#ifdef __sun
 /* Solaris getifaddrs is very un-suitable for dhcpcd.
  * See if-sun.c for details why. */
 struct ifaddrs;
index 91bd7ceb3ddce7d79bb72d19f71ae932d86dfdd2..00438b04e946df9ada2a3b90f2290f3ab42aa6df 100644 (file)
 #include <unistd.h>
 
 #include "dhcpcd.h"
+#include "if.h"
 #include "logerr.h"
 #include "privsep.h"
 
 static ssize_t
-ps_root_doioctldom(int domain, unsigned long req, void *data, size_t len)
+ps_root_doioctldom(struct dhcpcd_ctx *ctx, int domain, unsigned long req, void *data, size_t len)
 {
-       int s, err;
+#if defined(INET6) || (defined(SIOCALIFADDR) && defined(IFLR_ACTIVE))
+       struct priv *priv = (struct priv *)ctx->priv;
+#endif
+       int s;
+
+       switch(domain) {
+#ifdef INET
+       case PF_INET:
+               s = ctx->pf_inet_fd;
+               break;
+#endif
+#ifdef INET6
+       case PF_INET6:
+               s = priv->pf_inet6_fd;
+               break;
+#endif
+#if defined(SIOCALIFADDR) && defined(IFLR_ACTIVE) /*NetBSD */
+       case PF_LINK:
+               s = priv->pf_link_fd;
+               break;
+#endif
+       default:
+               errno = EPFNOSUPPORT;
+               return -1;
+       }
 
        /* Only allow these ioctls */
        switch(req) {
@@ -107,33 +132,20 @@ ps_root_doioctldom(int domain, unsigned long req, void *data, size_t len)
                return -1;
        }
 
-       s = socket(domain, SOCK_DGRAM, 0);
-       if (s == -1)
-               return -1;
-       err = ioctl(s, req, data, len);
-       close(s);
-       return err;
+       return ioctl(s, req, data, len);
 }
 
 static ssize_t
-ps_root_doroute(void *data, size_t len)
+ps_root_doroute(struct dhcpcd_ctx *ctx, void *data, size_t len)
 {
-       int s;
-       ssize_t err;
 
-       s = socket(PF_ROUTE, SOCK_RAW, 0);
-       if (s != -1)
-               err = write(s, data, len);
-       else
-               err = -1;
-       if (s != -1)
-               close(s);
-       return err;
+       return write(ctx->link_fd, data, len);
 }
 
 #if defined(HAVE_CAPSICUM) || defined(HAVE_PLEDGE)
 static ssize_t
-ps_root_doindirectioctl(unsigned long req, void *data, size_t len)
+ps_root_doindirectioctl(struct dhcpcd_ctx *ctx,
+    unsigned long req, void *data, size_t len)
 {
        char *p = data;
        struct ifreq ifr = { .ifr_flags = 0 };
@@ -150,27 +162,21 @@ ps_root_doindirectioctl(unsigned long req, void *data, size_t len)
        memmove(data, p + IFNAMSIZ, len);
        ifr.ifr_data = data;
 
-       return ps_root_doioctldom(PF_INET, req, &ifr, sizeof(ifr));
+       return ps_root_doioctldom(ctx, PF_INET, req, &ifr, sizeof(ifr));
 }
 #endif
 
 #ifdef HAVE_PLEDGE
 static ssize_t
-ps_root_doifignoregroup(void *data, size_t len)
+ps_root_doifignoregroup(struct dhcpcd_ctx *ctx, void *data, size_t len)
 {
-       int s, err;
 
        if (len == 0 || ((const char *)data)[len - 1] != '\0') {
                errno = EINVAL;
                return -1;
        }
 
-       s = socket(PF_INET, SOCK_DGRAM, 0);
-       if (s == -1)
-               return -1;
-       err = if_ignoregroup(s, data);
-       close(s);
-       return err;
+       return if_ignoregroup(ctx->pf_inet_fd, data);
 }
 #endif
 
@@ -245,7 +251,7 @@ ps_root_dosysctl(unsigned long flags,
 #endif
 
 ssize_t
-ps_root_os(struct ps_msghdr *psm, struct msghdr *msg,
+ps_root_os(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg,
     void **rdata, size_t *rlen, bool *free_rdata)
 {
        struct iovec *iov = msg->msg_iov;
@@ -255,21 +261,21 @@ ps_root_os(struct ps_msghdr *psm, struct msghdr *msg,
 
        switch (psm->ps_cmd) {
        case PS_IOCTLLINK:
-               err = ps_root_doioctldom(PF_LINK, psm->ps_flags, data, len);
+               err = ps_root_doioctldom(ctx, PF_LINK, psm->ps_flags, data, len);
                break;
        case PS_IOCTL6:
-               err = ps_root_doioctldom(PF_INET6, psm->ps_flags, data, len);
+               err = ps_root_doioctldom(ctx, PF_INET6, psm->ps_flags, data, len);
                break;
        case PS_ROUTE:
-               return ps_root_doroute(data, len);
+               return ps_root_doroute(ctx, data, len);
 #if defined(HAVE_CAPSICUM) || defined(HAVE_PLEDGE)
        case PS_IOCTLINDIRECT:
-               err = ps_root_doindirectioctl(psm->ps_flags, data, len);
+               err = ps_root_doindirectioctl(ctx, psm->ps_flags, data, len);
                break;
 #endif
 #ifdef HAVE_PLEDGE
        case PS_IFIGNOREGRP:
-               return ps_root_doifignoregroup(data, len);
+               return ps_root_doifignoregroup(ctx, data, len);
 #endif
 #ifdef HAVE_CAPSICUM
        case PS_SYSCTL:
index 66aeb49029e8a03b2b7cbf19b8ef000e78b55abf..68f782976bab89c67b9b9b3f074faa6ecfaf9146 100644 (file)
 //#define SECCOMP_FILTER_DEBUG
 
 static ssize_t
-ps_root_dosendnetlink(int protocol, struct msghdr *msg)
+ps_root_dosendnetlink(struct dhcpcd_ctx *ctx, int protocol, struct msghdr *msg)
 {
-       struct sockaddr_nl snl = { .nl_family = AF_NETLINK };
+       struct priv *priv = (struct priv *)ctx->priv;
        int s;
        unsigned char buf[16 * 1024];
        struct iovec riov = {
                .iov_base = buf,
                .iov_len = sizeof(buf),
        };
-       ssize_t retval;
 
-       if ((s = if_linksocket(&snl, protocol, 0)) == -1)
+       switch(protocol) {
+       case NETLINK_GENERIC:
+               s = priv->netlink_fd;
+               break;
+       case NETLINK_ROUTE:
+               s = priv->route_fd;
+               break;
+       default:
+               errno = EPFNOSUPPORT;
                return -1;
-
-       if (sendmsg(s, msg, 0) == -1) {
-               retval = -1;
-               goto out;
        }
 
-       retval = if_getnetlink(NULL, &riov, s, 0, NULL, NULL);
-out:
-       close(s);
-       return retval;
+       if (sendmsg(s, msg, 0) == -1)
+               return =-1;
+
+       return if_getnetlink(NULL, &riov, s, 0, NULL, NULL);
 }
 
 ssize_t
-ps_root_os(struct ps_msghdr *psm, struct msghdr *msg,
+ps_root_os(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg,
     __unused void **rdata, __unused size_t *rlen, __unused bool *free_data)
 {
 
        switch (psm->ps_cmd) {
        case PS_ROUTE:
-               return ps_root_dosendnetlink((int)psm->ps_flags, msg);
+               return ps_root_dosendnetlink(ctx, (int)psm->ps_flags, msg);
        default:
                errno = ENOTSUP;
                return -1;
index ef968d0e30efcc8e0c198002e39f217f894288fa..7bf2e06822e7c12a3aa64632a36854d25415b808 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, &free_rdata);
+               err = ps_root_os(ctx, psm, msg, &rdata, &rlen, &free_rdata);
                break;
        }
 
@@ -667,6 +667,26 @@ ps_root_startcb(struct ps_process *psp)
                    ctx->options & DHCPCD_IPV6 ? " [ip6]" : "");
        ctx->options |= DHCPCD_PRIVSEPROOT;
 
+       if (if_opensockets(ctx) == -1)
+               logerr("%s: if_opensockets", __func__);
+       else {
+#ifdef BSD
+               /* We only want to write to this socket, so set
+                * a small as possible buffer size. */
+               socklen_t smallbuf = 1;
+
+               if (setsockopt(ctx->link_fd, SOL_SOCKET, SO_RCVBUF,
+                   &smallbuf, (socklen_t)sizeof(smallbuf)) == -1)
+                       logerr("%s: setsockopt(SO_RCVBUF)", __func__);
+#endif
+#ifdef __linux__
+               /* See if_opensockets_os as to why we close link_fd
+                * rather than not open it. */
+               close(ctx->link_fd);
+               ctx->link_fd = -1;
+#endif
+       }
+
        /* Open network sockets for sending.
         * This is a small bit wasteful for non sandboxed OS's
         * but makes life very easy for unicasting DHCPv6 in non manager
index 98ff101bcc0c970f020f50c45bcfa9cbdc8eef79..3d3e0d04eeab7425d5ed2886db6c296b3e49d661 100644 (file)
@@ -56,7 +56,7 @@ 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 *,
+ssize_t ps_root_os(struct dhcpcd_ctx *, 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);