]> git.ipfire.org Git - thirdparty/lldpd.git/commitdiff
privsep: separate OS specific code to dedicated files
authorVincent Bernat <bernat@luffy.cx>
Tue, 2 Jul 2013 20:38:16 +0000 (22:38 +0200)
committerVincent Bernat <bernat@luffy.cx>
Tue, 2 Jul 2013 21:01:44 +0000 (23:01 +0200)
Linux only stuff goes in `priv-linux.c`. This includes interface
handling, ethtool and ability to open files (which is not Linux
specific but only Linux requires this).

BSD stuff goes in `priv-bpf.c` since it only includes interface
handling.

Moreover, `privsep_fdpass.c` is merged into `privsep_io.c`, a new file
for almost everything about IO (read/write and passing FD). The global
`remote` object is put into this file.

This commit is mostly moving stuff around. This will enable the
ability to write interface aliases without putting too much #ifdef.

src/daemon/Makefile.am
src/daemon/lldpd.h
src/daemon/priv-bpf.c [new file with mode: 0644]
src/daemon/priv-linux.c [new file with mode: 0644]
src/daemon/priv.c
src/daemon/privsep_io.c [moved from src/daemon/privsep_fdpass.c with 61% similarity]

index 86ea9039612f50a548bbd72c260445f2d00edb9c..dd803abddad70f1fd03fa8ea03535efa0c50a981 100644 (file)
@@ -13,7 +13,7 @@ liblldpd_la_SOURCES  = \
        sonmp.c sonmp.h \
        edp.c edp.h \
        client.c \
-       priv.c privsep_fdpass.c \
+       priv.c privsep_io.c \
        interfaces.c \
        event.c lldpd.c
 liblldpd_la_CFLAGS   = $(AM_CFLAGS) @LIBEVENT_CFLAGS@
@@ -27,42 +27,48 @@ liblldpd_la_SOURCES += \
        forward-linux.c \
        interfaces-linux.c \
        netlink.c \
-       dmi-linux.c
+       dmi-linux.c \
+       priv-linux.c
 endif
 if HOST_OS_DRAGONFLY
 liblldpd_la_SOURCES += \
        forward-bsd.c \
        interfaces-bpf.c \
        interfaces-bsd.c \
-       dmi-dummy.c
+       dmi-dummy.c \
+       priv-bpf.c
 endif
 if HOST_OS_FREEBSD
 liblldpd_la_SOURCES += \
        forward-bsd.c \
        interfaces-bpf.c \
        interfaces-bsd.c \
-       dmi-freebsd.c
+       dmi-freebsd.c \
+       priv-bpf.c
 endif
 if HOST_OS_OPENBSD
 liblldpd_la_SOURCES += \
        interfaces-bpf.c \
        forward-bsd.c \
        interfaces-bsd.c \
-       dmi-openbsd.c
+       dmi-openbsd.c \
+       priv-bpf.c
 endif
 if HOST_OS_NETBSD
 liblldpd_la_SOURCES += \
        forward-bsd.c \
        interfaces-bpf.c \
        interfaces-bsd.c \
-       dmi-dummy.c
+       dmi-dummy.c \
+       priv-bpf.c
 endif
 if HOST_OS_OSX
 liblldpd_la_SOURCES += \
        forward-bsd.c \
        interfaces-bpf.c \
        interfaces-bsd.c \
-       dmi-osx.c
+       dmi-osx.c \
+       priv-bpf.c
 liblldpd_la_LDFLAGS  = -framework Foundation
 liblldpd_la_LDFLAGS += -framework CoreFoundation -framework IOKit
 liblldpd_la_LDFLAGS += -framework IOKit
@@ -72,7 +78,8 @@ liblldpd_la_SOURCES += \
        forward-solaris.c \
        interfaces-bpf.c \
        interfaces-solaris.c \
-       dmi-dummy.c
+       dmi-dummy.c \
+       priv-bpf.c
 endif
 
 # Add SNMP support if needed
index 49167feb952208e963debf370d138e9c9f15ab93..3ed65d0d8849e2c0fa707adfeffe000c36cde691 100644 (file)
@@ -215,15 +215,34 @@ void       priv_ctl_cleanup(const char *ctlname);
 char           *priv_gethostbyname(void);
 #ifdef HOST_OS_LINUX
 int             priv_open(char*);
+void    asroot_open(void);
 int             priv_ethtool(char*, void*, size_t);
+void    asroot_ethtool(void);
 #endif
 int             priv_iface_init(int, char *);
+int     asroot_iface_init_os(int, char *, int *);
 int     priv_iface_multicast(const char *, u_int8_t *, int);
 int     priv_snmp_socket(struct sockaddr_un *);
 
-/* privsep_fdpass.c */
-int     receive_fd(int);
-void    send_fd(int, int);
+enum {
+       PRIV_PING,
+       PRIV_DELETE_CTL_SOCKET,
+       PRIV_GET_HOSTNAME,
+       PRIV_OPEN,
+       PRIV_ETHTOOL,
+       PRIV_IFACE_INIT,
+       PRIV_IFACE_MULTICAST,
+       PRIV_SNMP_SOCKET,
+} priv_cmd;
+
+
+/* privsep_io.c */
+int     may_read(void *, size_t);
+void    must_read(void *, size_t);
+void    must_write(const void *, size_t);
+void    priv_remote(int);
+int     receive_fd(void);
+void    send_fd(int);
 
 /* interfaces-*.c */
 
diff --git a/src/daemon/priv-bpf.c b/src/daemon/priv-bpf.c
new file mode 100644 (file)
index 0000000..ea9bb71
--- /dev/null
@@ -0,0 +1,136 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "lldpd.h"
+
+#include <unistd.h>
+#include <net/bpf.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <errno.h>
+
+int
+asroot_iface_init_os(int ifindex, char *name, int *fd)
+{
+       int enable, required, rc;
+       struct bpf_insn filter[] = { LLDPD_FILTER_F };
+       struct ifreq ifr = { .ifr_name = {} };
+       struct bpf_program fprog = {
+               .bf_insns = filter,
+               .bf_len = sizeof(filter)/sizeof(struct bpf_insn)
+       };
+
+#ifndef HOST_OS_SOLARIS
+       int n = 0;
+       char dev[20];
+       do {
+               snprintf(dev, sizeof(dev), "/dev/bpf%d", n++);
+               *fd = open(dev, O_RDWR);
+       } while (*fd < 0 && errno == EBUSY);
+#else
+       *fd = open("/dev/bpf", O_RDWR);
+#endif
+       if (*fd < 0) {
+               rc = errno;
+               log_warn("privsep", "unable to find a free BPF");
+               return rc;
+       }
+
+       /* Set buffer size */
+       required = ETHER_MAX_LEN;
+       if (ioctl(*fd, BIOCSBLEN, (caddr_t)&required) < 0) {
+               rc = errno;
+               log_warn("privsep",
+                   "unable to set receive buffer size for BPF on %s",
+                   name);
+               return rc;
+       }
+
+       /* Bind the interface to BPF device */
+       strlcpy(ifr.ifr_name, name, IFNAMSIZ);
+       if (ioctl(*fd, BIOCSETIF, (caddr_t)&ifr) < 0) {
+               rc = errno;
+               log_warn("privsep", "failed to bind interface %s to BPF",
+                   name);
+               return rc;
+       }
+
+       /* Disable buffering */
+       enable = 1;
+       if (ioctl(*fd, BIOCIMMEDIATE, (caddr_t)&enable) < 0) {
+               rc = errno;
+               log_warn("privsep", "unable to disable buffering for %s",
+                   name);
+               return rc;
+       }
+
+       /* Let us write the MAC address (raw packet mode) */
+       enable = 1;
+       if (ioctl(*fd, BIOCSHDRCMPLT, (caddr_t)&enable) < 0) {
+               rc = errno;
+               log_warn("privsep",
+                   "unable to set the `header complete` flag for %s",
+                   name);
+               return rc;
+       }
+
+       /* Don't see sent packets */
+#ifdef HOST_OS_OPENBSD
+       enable = BPF_DIRECTION_OUT;
+       if (ioctl(*fd, BIOCSDIRFILT, (caddr_t)&enable) < 0)
+#else
+       enable = 0;
+       if (ioctl(*fd, BIOCSSEESENT, (caddr_t)&enable) < 0)
+#endif
+       {
+               rc = errno;
+               log_warn("privsep",
+                   "unable to set packet direction for BPF filter on %s",
+                   name);
+               return rc;
+       }
+
+       /* Install read filter */
+       if (ioctl(*fd, BIOCSETF, (caddr_t)&fprog) < 0) {
+               rc = errno;
+               log_warn("privsep", "unable to setup BPF filter for %s",
+                   name);
+               return rc;
+       }
+#ifdef BIOCSETWF
+       /* Install write filter (optional) */
+       if (ioctl(*fd, BIOCSETWF, (caddr_t)&fprog) < 0) {
+               rc = errno;
+               log_info("privsep", "unable to setup write BPF filter for %s",
+                   name);
+               return rc;
+       }
+#endif
+
+#ifdef BIOCLOCK
+       /* Lock interface */
+       if (ioctl(*fd, BIOCLOCK, (caddr_t)&enable) < 0) {
+               rc = errno;
+               log_info("privsep", "unable to lock BPF interface %s",
+                   name);
+               return rc;
+       }
+#endif
+       return 0;
+}
diff --git a/src/daemon/priv-linux.c b/src/daemon/priv-linux.c
new file mode 100644 (file)
index 0000000..553a853
--- /dev/null
@@ -0,0 +1,199 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "lldpd.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <regex.h>
+#include <sys/ioctl.h>
+#include <netpacket/packet.h> /* For sockaddr_ll */
+#include <linux/filter.h>     /* For BPF filtering */
+#include <linux/ethtool.h>
+#include <linux/sockios.h>
+
+/* Proxy for open */
+int
+priv_open(char *file)
+{
+       int cmd, len, rc;
+       cmd = PRIV_OPEN;
+       must_write(&cmd, sizeof(int));
+       len = strlen(file);
+       must_write(&len, sizeof(int));
+       must_write(file, len + 1);
+       must_read(&rc, sizeof(int));
+       if (rc == -1)
+               return rc;
+       return receive_fd();
+}
+
+/* Proxy for ethtool ioctl */
+int
+priv_ethtool(char *ifname, void *ethc, size_t length)
+{
+       int cmd, rc, len;
+       cmd = PRIV_ETHTOOL;
+       must_write(&cmd, sizeof(int));
+       len = strlen(ifname);
+       must_write(&len, sizeof(int));
+       must_write(ifname, len + 1);
+       must_read(&rc, sizeof(int));
+       if (rc != 0)
+               return rc;
+       must_read(ethc, length);
+       return rc;
+}
+
+void
+asroot_open()
+{
+       const char* authorized[] = {
+               "/proc/sys/net/ipv4/ip_forward",
+               "/proc/net/bonding/[^.][^/]*",
+               "/proc/self/net/bonding/[^.][^/]*",
+               SYSFS_CLASS_NET "[^.][^/]*/brforward",
+               SYSFS_CLASS_NET "[^.][^/]*/brport",
+               SYSFS_CLASS_NET "[^.][^/]*/brif/[^.][^/]*/port_no",
+               SYSFS_CLASS_DMI "product_version",
+               SYSFS_CLASS_DMI "product_serial",
+               SYSFS_CLASS_DMI "product_name",
+               SYSFS_CLASS_DMI "bios_version",
+               SYSFS_CLASS_DMI "sys_vendor",
+               SYSFS_CLASS_DMI "chassis_asset_tag",
+               NULL
+       };
+       const char **f;
+       char *file;
+       int fd, len, rc;
+       regex_t preg;
+
+       must_read(&len, sizeof(len));
+       if ((file = (char *)malloc(len + 1)) == NULL)
+               fatal("privsep", NULL);
+       must_read(file, len);
+       file[len] = '\0';
+
+       for (f=authorized; *f != NULL; f++) {
+               if (regcomp(&preg, *f, REG_NOSUB) != 0)
+                       /* Should not happen */
+                       fatal("privsep", "unable to compile a regex");
+               if (regexec(&preg, file, 0, NULL, 0) == 0) {
+                       regfree(&preg);
+                       break;
+               }
+               regfree(&preg);
+       }
+       if (*f == NULL) {
+               log_warnx("privsep", "not authorized to open %s", file);
+               rc = -1;
+               must_write(&rc, sizeof(int));
+               free(file);
+               return;
+       }
+       if ((fd = open(file, O_RDONLY)) == -1) {
+               rc = -1;
+               must_write(&rc, sizeof(int));
+               free(file);
+               return;
+       }
+       free(file);
+       must_write(&fd, sizeof(int));
+       send_fd(fd);
+       close(fd);
+}
+
+void
+asroot_ethtool()
+{
+       struct ifreq ifr = {};
+       struct ethtool_cmd ethc = {};
+       int len, rc, sock;
+       char *ifname;
+
+       must_read(&len, sizeof(int));
+       if ((ifname = (char*)malloc(len + 1)) == NULL)
+               fatal("privsep", NULL);
+       must_read(ifname, len);
+       ifname[len] = '\0';
+       strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+       free(ifname);
+       ifr.ifr_data = (caddr_t)&ethc;
+       ethc.cmd = ETHTOOL_GSET;
+       if (((rc = sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) ||
+           (rc = ioctl(sock, SIOCETHTOOL, &ifr)) != 0) {
+               must_write(&rc, sizeof(int));
+               return;
+       }
+       must_write(&rc, sizeof(int));
+       must_write(&ethc, sizeof(struct ethtool_cmd));
+}
+
+int
+asroot_iface_init_os(int ifindex, char *name, int *fd)
+{
+       int rc;
+       /* Open listening socket to receive/send frames */
+       if ((*fd = socket(PF_PACKET, SOCK_RAW,
+                   htons(ETH_P_ALL))) < 0) {
+               rc = errno;
+               must_write(&rc, sizeof(rc));
+               return rc;
+       }
+
+       struct sockaddr_ll sa = {
+               .sll_family = AF_PACKET,
+               .sll_ifindex = ifindex
+       };
+       if (bind(*fd, (struct sockaddr*)&sa, sizeof(sa)) < 0) {
+               rc = errno;
+               log_warn("privsep",
+                   "unable to bind to raw socket for interface %s",
+                   name);
+               return rc;
+       }
+
+       /* Set filter */
+       log_debug("privsep", "set BPF filter for %s", name);
+       static struct sock_filter lldpd_filter_f[] = { LLDPD_FILTER_F };
+       struct sock_fprog prog = {
+               .filter = lldpd_filter_f,
+               .len = sizeof(lldpd_filter_f) / sizeof(struct sock_filter)
+       };
+       if (setsockopt(*fd, SOL_SOCKET, SO_ATTACH_FILTER,
+                &prog, sizeof(prog)) < 0) {
+               rc = errno;
+               log_warn("privsep", "unable to change filter for %s", name);
+               return rc;
+       }
+
+#ifdef SO_LOCK_FILTER
+       int enable = 1;
+       if (setsockopt(*fd, SOL_SOCKET, SO_LOCK_FILTER,
+               &enable, sizeof(enable)) < 0) {
+               if (errno != ENOPROTOOPT) {
+                       rc = errno;
+                       log_warn("privsep", "unable to lock filter for %s", name);
+                       return rc;
+               }
+       }
+#endif
+
+       return 0;
+}
index 23cfbc4f7a96093de58de7d5fe1b32cea8249cf1..8cc61001c69c75b337caeba33e72f946086be0c7 100644 (file)
 #include <sys/stat.h>
 #include <sys/socket.h>
 #include <sys/un.h>
-#include <regex.h>
 #include <fcntl.h>
 #include <grp.h>
 #include <sys/utsname.h>
 #include <sys/ioctl.h>
 #include <netdb.h>
-#ifdef HOST_OS_LINUX
-# include <netpacket/packet.h> /* For sockaddr_ll */
-# include <linux/filter.h>     /* For BPF filtering */
-#endif
-#if defined HOST_OS_FREEBSD || \
-           HOST_OS_DRAGONFLY || \
-           HOST_OS_NETBSD || \
-           HOST_OS_OPENBSD || \
-           HOST_OS_OSX     || \
-            HOST_OS_SOLARIS
-# include <net/bpf.h>
-#endif
+#include <netinet/if_ether.h>
+
 #if defined HOST_OS_FREEBSD || HOST_OS_OSX || HOST_OS_DRAGONFLY
 # include <net/if_dl.h>
 #endif
 #if defined HOST_OS_SOLARIS
 # include <sys/sockio.h>
 #endif
-#include <netinet/if_ether.h>
 
 /* Use resolv.h */
 #ifdef HAVE_SYS_TYPES_H
 #endif
 #include <resolv.h>
 
-enum {
-       PRIV_PING,
-       PRIV_DELETE_CTL_SOCKET,
-       PRIV_GET_HOSTNAME,
-       PRIV_OPEN,
-       PRIV_ETHTOOL,
-       PRIV_IFACE_INIT,
-       PRIV_IFACE_MULTICAST,
-       PRIV_SNMP_SOCKET,
-};
-
-static int may_read(int, void *, size_t);
-static void must_read(int, void *, size_t);
-static void must_write(int, const void *, size_t);
-
-static int remote;                     /* Other side */
 static int monitored = -1;             /* Child */
-static int sock = -1;
 
 /* Proxies */
-
 static void
 priv_ping()
 {
        int cmd, rc;
        cmd = PRIV_PING;
-       must_write(remote, &cmd, sizeof(int));
-       must_read(remote, &rc, sizeof(int));
+       must_write(&cmd, sizeof(int));
+       must_read(&rc, sizeof(int));
        log_debug("privsep", "monitor ready");
 }
 
@@ -109,10 +79,10 @@ priv_ctl_cleanup(const char *ctlname)
        int cmd, rc;
        int len = strlen(ctlname);
        cmd = PRIV_DELETE_CTL_SOCKET;
-       must_write(remote, &cmd, sizeof(int));
-       must_write(remote, &len, sizeof(int));
-       must_write(remote, ctlname, len);
-       must_read(remote, &rc, sizeof(int));
+       must_write(&cmd, sizeof(int));
+       must_write(&len, sizeof(int));
+       must_write(ctlname, len);
+       must_read(&rc, sizeof(int));
 }
 
 /* Proxy for gethostbyname */
@@ -122,50 +92,14 @@ priv_gethostbyname()
        int cmd, rc;
        static char *buf = NULL;
        cmd = PRIV_GET_HOSTNAME;
-       must_write(remote, &cmd, sizeof(int));
-       must_read(remote, &rc, sizeof(int));
+       must_write(&cmd, sizeof(int));
+       must_read(&rc, sizeof(int));
        if ((buf = (char*)realloc(buf, rc+1)) == NULL)
                fatal("privsep", NULL);
-       must_read(remote, buf, rc+1);
+       must_read(buf, rc+1);
        return buf;
 }
 
-#ifdef HOST_OS_LINUX
-/* Proxy for open */
-int
-priv_open(char *file)
-{
-       int cmd, len, rc;
-       cmd = PRIV_OPEN;
-       must_write(remote, &cmd, sizeof(int));
-       len = strlen(file);
-       must_write(remote, &len, sizeof(int));
-       must_write(remote, file, len + 1);
-       must_read(remote, &rc, sizeof(int));
-       if (rc == -1)
-               return rc;
-       return receive_fd(remote);
-}
-#endif
-
-#ifdef HOST_OS_LINUX
-/* Proxy for ethtool ioctl */
-int
-priv_ethtool(char *ifname, void *ethc, size_t length)
-{
-       int cmd, rc, len;
-       cmd = PRIV_ETHTOOL;
-       must_write(remote, &cmd, sizeof(int));
-       len = strlen(ifname);
-       must_write(remote, &len, sizeof(int));
-       must_write(remote, ifname, len + 1);
-       must_read(remote, &rc, sizeof(int));
-       if (rc != 0)
-               return rc;
-       must_read(remote, ethc, length);
-       return rc;
-}
-#endif
 
 int
 priv_iface_init(int index, char *iface)
@@ -173,13 +107,13 @@ priv_iface_init(int index, char *iface)
        int cmd, rc;
        char dev[IFNAMSIZ];
        cmd = PRIV_IFACE_INIT;
-       must_write(remote, &cmd, sizeof(int));
-       must_write(remote, &index, sizeof(int));
+       must_write(&cmd, sizeof(int));
+       must_write(&index, sizeof(int));
        strlcpy(dev, iface, IFNAMSIZ);
-       must_write(remote, dev, IFNAMSIZ);
-       must_read(remote, &rc, sizeof(int));
+       must_write(dev, IFNAMSIZ);
+       must_read(&rc, sizeof(int));
        if (rc != 0) return -1;
-       return receive_fd(remote);
+       return receive_fd();
 }
 
 int
@@ -187,11 +121,11 @@ priv_iface_multicast(const char *name, u_int8_t *mac, int add)
 {
        int cmd, rc;
        cmd = PRIV_IFACE_MULTICAST;
-       must_write(remote, &cmd, sizeof(int));
-       must_write(remote, name, IFNAMSIZ);
-       must_write(remote, mac, ETHER_ADDR_LEN);
-       must_write(remote, &add, sizeof(int));
-       must_read(remote, &rc, sizeof(int));
+       must_write(&cmd, sizeof(int));
+       must_write(name, IFNAMSIZ);
+       must_write(mac, ETHER_ADDR_LEN);
+       must_write(&add, sizeof(int));
+       must_read(&rc, sizeof(int));
        return rc;
 }
 
@@ -200,19 +134,19 @@ priv_snmp_socket(struct sockaddr_un *addr)
 {
        int cmd, rc;
        cmd = PRIV_SNMP_SOCKET;
-       must_write(remote, &cmd, sizeof(int));
-       must_write(remote, addr, sizeof(struct sockaddr_un));
-       must_read(remote, &rc, sizeof(int));
+       must_write(&cmd, sizeof(int));
+       must_write(addr, sizeof(struct sockaddr_un));
+       must_read(&rc, sizeof(int));
        if (rc < 0)
                return rc;
-       return receive_fd(remote);
+       return receive_fd();
 }
 
 static void
 asroot_ping()
 {
        int rc = 1;
-       must_write(remote, &rc, sizeof(int));
+       must_write(&rc, sizeof(int));
 }
 
 static void
@@ -222,18 +156,18 @@ asroot_ctl_cleanup()
        char *ctlname;
        int rc = 0;
 
-       must_read(remote, &len, sizeof(int));
+       must_read(&len, sizeof(int));
        if ((ctlname = (char*)malloc(len+1)) == NULL)
                fatal("ctlname", NULL);
 
-       must_read(remote, ctlname, len);
+       must_read(ctlname, len);
        ctlname[len] = 0;
 
        ctl_cleanup(ctlname);
        free(ctlname);
 
        /* Ack */
-       must_write(remote, &rc, sizeof(int));
+       must_write(&rc, sizeof(int));
 }
 
 static void
@@ -250,294 +184,39 @@ asroot_gethostbyname()
                res_init();
 #endif
                 len = strlen(un.nodename);
-                must_write(remote, &len, sizeof(int));
-                must_write(remote, un.nodename, len + 1);
+                must_write(&len, sizeof(int));
+                must_write(un.nodename, len + 1);
         } else {
                 len = strlen(hp->h_name);
-                must_write(remote, &len, sizeof(int));
-                must_write(remote, hp->h_name, len + 1);
+                must_write(&len, sizeof(int));
+                must_write(hp->h_name, len + 1);
         }
 }
 
-#ifdef HOST_OS_LINUX
-static void
-asroot_open()
-{
-       const char* authorized[] = {
-               "/proc/sys/net/ipv4/ip_forward",
-               "/proc/net/bonding/[^.][^/]*",
-               "/proc/self/net/bonding/[^.][^/]*",
-               SYSFS_CLASS_NET "[^.][^/]*/brforward",
-               SYSFS_CLASS_NET "[^.][^/]*/brport",
-               SYSFS_CLASS_NET "[^.][^/]*/brif/[^.][^/]*/port_no",
-               SYSFS_CLASS_DMI "product_version",
-               SYSFS_CLASS_DMI "product_serial",
-               SYSFS_CLASS_DMI "product_name",
-               SYSFS_CLASS_DMI "bios_version",
-               SYSFS_CLASS_DMI "sys_vendor",
-               SYSFS_CLASS_DMI "chassis_asset_tag",
-               NULL
-       };
-       const char **f;
-       char *file;
-       int fd, len, rc;
-       regex_t preg;
-
-       must_read(remote, &len, sizeof(len));
-       if ((file = (char *)malloc(len + 1)) == NULL)
-               fatal("privsep", NULL);
-       must_read(remote, file, len);
-       file[len] = '\0';
-
-       for (f=authorized; *f != NULL; f++) {
-               if (regcomp(&preg, *f, REG_NOSUB) != 0)
-                       /* Should not happen */
-                       fatal("privsep", "unable to compile a regex");
-               if (regexec(&preg, file, 0, NULL, 0) == 0) {
-                       regfree(&preg);
-                       break;
-               }
-               regfree(&preg);
-       }
-       if (*f == NULL) {
-               log_warnx("privsep", "not authorized to open %s", file);
-               rc = -1;
-               must_write(remote, &rc, sizeof(int));
-               free(file);
-               return;
-       }
-       if ((fd = open(file, O_RDONLY)) == -1) {
-               rc = -1;
-               must_write(remote, &rc, sizeof(int));
-               free(file);
-               return;
-       }
-       free(file);
-       must_write(remote, &fd, sizeof(int));
-       send_fd(remote, fd);
-       close(fd);
-}
-#endif
-
-#ifdef HOST_OS_LINUX
-#include <linux/ethtool.h>
-#include <linux/sockios.h>
-static void
-asroot_ethtool()
-{
-       struct ifreq ifr = {};
-       struct ethtool_cmd ethc = {};
-       int len, rc;
-       char *ifname;
-
-       must_read(remote, &len, sizeof(int));
-       if ((ifname = (char*)malloc(len + 1)) == NULL)
-               fatal("privsep", NULL);
-       must_read(remote, ifname, len);
-       ifname[len] = '\0';
-       strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
-       free(ifname);
-       ifr.ifr_data = (caddr_t)&ethc;
-       ethc.cmd = ETHTOOL_GSET;
-       if ((rc = ioctl(sock, SIOCETHTOOL, &ifr)) != 0) {
-               must_write(remote, &rc, sizeof(int));
-               return;
-       }
-       must_write(remote, &rc, sizeof(int));
-       must_write(remote, &ethc, sizeof(struct ethtool_cmd));
-}
-#endif
-
 static void
 asroot_iface_init()
 {
        int rc = -1, fd = -1;
        int ifindex;
        char name[IFNAMSIZ];
-       must_read(remote, &ifindex, sizeof(ifindex));
-       must_read(remote, &name, sizeof(name));
+       must_read(&ifindex, sizeof(ifindex));
+       must_read(&name, sizeof(name));
        name[sizeof(name) - 1] = '\0';
 
-#if defined HOST_OS_LINUX
-       /* Open listening socket to receive/send frames */
-       if ((fd = socket(PF_PACKET, SOCK_RAW,
-                   htons(ETH_P_ALL))) < 0) {
-               rc = errno;
-               must_write(remote, &rc, sizeof(rc));
-               return;
-       }
-
-       struct sockaddr_ll sa = {
-               .sll_family = AF_PACKET,
-               .sll_ifindex = ifindex
-       };
-       if (bind(fd, (struct sockaddr*)&sa, sizeof(sa)) < 0) {
-               rc = errno;
-               log_warn("privsep",
-                   "unable to bind to raw socket for interface %s",
-                   name);
-               goto end;
-       }
-
-       /* Set filter */
-       log_debug("privsep", "set BPF filter for %s", name);
-       static struct sock_filter lldpd_filter_f[] = { LLDPD_FILTER_F };
-       struct sock_fprog prog = {
-               .filter = lldpd_filter_f,
-               .len = sizeof(lldpd_filter_f) / sizeof(struct sock_filter)
-       };
-       if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER,
-                &prog, sizeof(prog)) < 0) {
-               rc = errno;
-               log_warn("privsep", "unable to change filter for %s", name);
-               goto end;
-       }
-
-#ifdef SO_LOCK_FILTER
-       int enable = 1;
-       if (setsockopt(fd, SOL_SOCKET, SO_LOCK_FILTER,
-               &enable, sizeof(enable)) < 0) {
-               if (errno != ENOPROTOOPT) {
-                       rc = errno;
-                       log_warn("privsep", "unable to lock filter for %s", name);
-                       goto end;
-               }
-       }
-#endif
-
-       rc = 0;
-
-#elif defined HOST_OS_FREEBSD   || \
-      defined HOST_OS_DRAGONFLY || \
-      defined HOST_OS_OPENBSD   || \
-      defined HOST_OS_NETBSD    || \
-      defined HOST_OS_OSX       || \
-      defined HOST_OS_SOLARIS
-       int enable, required;
-       struct bpf_insn filter[] = { LLDPD_FILTER_F };
-       struct ifreq ifr = { .ifr_name = {} };
-       struct bpf_program fprog = {
-               .bf_insns = filter,
-               .bf_len = sizeof(filter)/sizeof(struct bpf_insn)
-       };
-
-#ifndef HOST_OS_SOLARIS
-       int n = 0;
-       char dev[20];
-       do {
-               snprintf(dev, sizeof(dev), "/dev/bpf%d", n++);
-               fd = open(dev, O_RDWR);
-       } while (fd < 0 && errno == EBUSY);
-#else
-       fd = open("/dev/bpf", O_RDWR);
-#endif
-       if (fd < 0) {
-               rc = errno;
-               log_warn("privsep", "unable to find a free BPF");
-               goto end;
-       }
-
-       /* Set buffer size */
-       required = ETHER_MAX_LEN;
-       if (ioctl(fd, BIOCSBLEN, (caddr_t)&required) < 0) {
-               rc = errno;
-               log_warn("privsep",
-                   "unable to set receive buffer size for BPF on %s",
-                   name);
-               goto end;
-       }
-
-       /* Bind the interface to BPF device */
-       strlcpy(ifr.ifr_name, name, IFNAMSIZ);
-       if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) < 0) {
-               rc = errno;
-               log_warn("privsep", "failed to bind interface %s to BPF",
-                   name);
-               goto end;
-       }
-
-       /* Disable buffering */
-       enable = 1;
-       if (ioctl(fd, BIOCIMMEDIATE, (caddr_t)&enable) < 0) {
-               rc = errno;
-               log_warn("privsep", "unable to disable buffering for %s",
-                   name);
-               goto end;
-       }
-
-       /* Let us write the MAC address (raw packet mode) */
-       enable = 1;
-       if (ioctl(fd, BIOCSHDRCMPLT, (caddr_t)&enable) < 0) {
-               rc = errno;
-               log_warn("privsep",
-                   "unable to set the `header complete` flag for %s",
-                   name);
-               goto end;
-       }
-
-       /* Don't see sent packets */
-#ifdef HOST_OS_OPENBSD
-       enable = BPF_DIRECTION_OUT;
-       if (ioctl(fd, BIOCSDIRFILT, (caddr_t)&enable) < 0)
-#else
-       enable = 0;
-       if (ioctl(fd, BIOCSSEESENT, (caddr_t)&enable) < 0)
-#endif
-       {
-               rc = errno;
-               log_warn("privsep",
-                   "unable to set packet direction for BPF filter on %s",
-                   name);
-               goto end;
-       }
-
-       /* Install read filter */
-       if (ioctl(fd, BIOCSETF, (caddr_t)&fprog) < 0) {
-               rc = errno;
-               log_warn("privsep", "unable to setup BPF filter for %s",
-                   name);
-               goto end;
-       }
-#ifdef BIOCSETWF
-       /* Install write filter (optional) */
-       if (ioctl(fd, BIOCSETWF, (caddr_t)&fprog) < 0) {
-               rc = errno;
-               log_info("privsep", "unable to setup write BPF filter for %s",
-                   name);
-               goto end;
-       }
-#endif
-
-#ifdef BIOCLOCK
-       /* Lock interface */
-       if (ioctl(fd, BIOCLOCK, (caddr_t)&enable) < 0) {
-               rc = errno;
-               log_info("privsep", "unable to lock BPF interface %s",
-                   name);
-               goto end;
-       }
-#endif
-
-       rc = 0;
-
-#else
-#error Unsupported OS
-#endif
-
-end:
-       must_write(remote, &rc, sizeof(rc));
-       if (rc == 0 && fd >=0) send_fd(remote, fd);
+       rc = asroot_iface_init_os(ifindex, name, &fd);
+       must_write(&rc, sizeof(rc));
+       if (rc == 0 && fd >=0) send_fd(fd);
        if (fd >= 0) close(fd);
 }
 
 static void
 asroot_iface_multicast()
 {
-       int add, rc = 0;
+       int sock, add, rc = 0;
        struct ifreq ifr = { .ifr_name = {} };
-       must_read(remote, ifr.ifr_name, IFNAMSIZ);
+       must_read(ifr.ifr_name, IFNAMSIZ);
 #if defined HOST_OS_LINUX
-       must_read(remote, ifr.ifr_hwaddr.sa_data, ETHER_ADDR_LEN);
+       must_read(ifr.ifr_hwaddr.sa_data, ETHER_ADDR_LEN);
 #elif defined HOST_OS_FREEBSD || defined HOST_OS_OSX || defined HOST_OS_DRAGONFLY
        /* Black magic from mtest.c */
        struct sockaddr_dl *dlp = (struct sockaddr_dl *)&ifr.ifr_addr;
@@ -547,24 +226,25 @@ asroot_iface_multicast()
        dlp->sdl_nlen = 0;
        dlp->sdl_alen = ETHER_ADDR_LEN;
        dlp->sdl_slen = 0;
-       must_read(remote, LLADDR(dlp), ETHER_ADDR_LEN);
+       must_read(LLADDR(dlp), ETHER_ADDR_LEN);
 #elif defined HOST_OS_OPENBSD || defined HOST_OS_NETBSD || defined HOST_OS_SOLARIS
        struct sockaddr *sap = (struct sockaddr *)&ifr.ifr_addr;
 #if ! defined HOST_OS_SOLARIS
        sap->sa_len = sizeof(struct sockaddr);
 #endif
        sap->sa_family = AF_UNSPEC;
-       must_read(remote, sap->sa_data, ETHER_ADDR_LEN);
+       must_read(sap->sa_data, ETHER_ADDR_LEN);
 #else
 #error Unsupported OS
 #endif
 
-       must_read(remote, &add, sizeof(int));
-       if ((ioctl(sock, (add)?SIOCADDMULTI:SIOCDELMULTI,
-                   &ifr) < 0) && (errno != EADDRINUSE))
+       must_read(&add, sizeof(int));
+       if (((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) ||
+           ((ioctl(sock, (add)?SIOCADDMULTI:SIOCDELMULTI,
+                   &ifr) < 0) && (errno != EADDRINUSE)))
                rc = errno;
 
-       must_write(remote, &rc, sizeof(rc));
+       must_write(&rc, sizeof(rc));
 }
 
 static void
@@ -576,18 +256,18 @@ asroot_snmp_socket()
 
        if (!addr) {
                addr = (struct sockaddr_un *)malloc(sizeof(struct sockaddr_un));
-               must_read(remote, addr, sizeof(struct sockaddr_un));
+               must_read(addr, sizeof(struct sockaddr_un));
        } else
                /* We have already been asked to connect to a socket. We will
                 * connect to the same socket. */
-               must_read(remote, &bogus, sizeof(struct sockaddr_un));
+               must_read(&bogus, sizeof(struct sockaddr_un));
        if (addr->sun_family != AF_UNIX)
                fatal("privsep", "someone is trying to trick me");
        addr->sun_path[sizeof(addr->sun_path)-1] = '\0';
 
        if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
                log_warn("privsep", "cannot open socket");
-               must_write(remote, &sock, sizeof(int));
+               must_write(&sock, sizeof(int));
                return;
        }
         if ((rc = connect(sock, (struct sockaddr *) addr,
@@ -596,11 +276,11 @@ asroot_snmp_socket()
                           addr->sun_path, strerror(errno));
                close(sock);
                rc = -1;
-               must_write(remote, &rc, sizeof(int));
+               must_write(&rc, sizeof(int));
                return;
         }
-       must_write(remote, &rc, sizeof(int));
-       send_fd(remote, sock);
+       must_write(&rc, sizeof(int));
+       send_fd(sock);
        close(sock);
 }
 
@@ -631,7 +311,7 @@ priv_loop()
        struct dispatch_actions *a;
 
        setproctitle("monitor");
-       while (!may_read(remote, &cmd, sizeof(int))) {
+       while (!may_read(&cmd, sizeof(int))) {
                for (a = actions; a->function != NULL; a++) {
                        if (cmd == a->msg) {
                                a->function();
@@ -766,20 +446,17 @@ priv_init(const char *chrootdir, int ctl, uid_t uid, gid_t gid)
                                fatal("privsep", "setreuid() failed");
 #endif
                }
-               remote = pair[0];
+               priv_remote(pair[0]);
                close(pair[1]);
                priv_ping();
                break;
        default:
                /* We are in the monitor */
                if (ctl != -1) close(ctl);
-               remote = pair[1];
+               priv_remote(pair[1]);
                close(pair[0]);
                if (atexit(priv_exit) != 0)
                        fatal("privsep", "unable to set exit function");
-               if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
-                       fatal("privsep", "unable to get a socket");
-               }
 
                signal(SIGALRM, sig_pass_to_chld);
                signal(SIGTERM, sig_pass_to_chld);
@@ -795,86 +472,3 @@ priv_init(const char *chrootdir, int ctl, uid_t uid, gid_t gid)
        }
 }
 
-/* Stolen from sbin/pflogd/privsep.c from OpenBSD */
-/*
- * Copyright (c) 2003 Can Erkin Acar
- * Copyright (c) 2003 Anil Madhavapeddy <anil@recoil.org>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-/* Read all data or return 1 for error.  */
-static int
-may_read(int fd, void *buf, size_t n)
-{
-       char *s = buf;
-       ssize_t res, pos = 0;
-
-       while (n > pos) {
-               res = read(fd, s + pos, n - pos);
-               switch (res) {
-               case -1:
-                       if (errno == EINTR || errno == EAGAIN)
-                               continue;
-               case 0:
-                       return (1);
-               default:
-                       pos += res;
-               }
-       }
-       return (0);
-}
-
-/* Read data with the assertion that it all must come through, or
- * else abort the process.  Based on atomicio() from openssh. */
-static void
-must_read(int fd, void *buf, size_t n)
-{
-       char *s = buf;
-       ssize_t res, pos = 0;
-
-       while (n > pos) {
-               res = read(fd, s + pos, n - pos);
-               switch (res) {
-               case -1:
-                       if (errno == EINTR || errno == EAGAIN)
-                               continue;
-               case 0:
-                       _exit(0);
-               default:
-                       pos += res;
-               }
-       }
-}
-
-/* Write data with the assertion that it all has to be written, or
- * else abort the process.  Based on atomicio() from openssh. */
-static void
-must_write(int fd, const void *buf, size_t n)
-{
-       const char *s = buf;
-       ssize_t res, pos = 0;
-
-       while (n > pos) {
-               res = write(fd, s + pos, n - pos);
-               switch (res) {
-               case -1:
-                       if (errno == EINTR || errno == EAGAIN)
-                               continue;
-               case 0:
-                       _exit(0);
-               default:
-                       pos += res;
-               }
-       }
-}
similarity index 61%
rename from src/daemon/privsep_fdpass.c
rename to src/daemon/privsep_io.c
index 0a1ba08b4595d2618fb646849df23f19d1f42b70..638e54584647f98b7e0aad15c0215bdcf0593f71 100644 (file)
@@ -1,4 +1,7 @@
 /* -*- mode: c; c-file-style: "openbsd" -*- */
+
+static int remote;
+
 /*
  * Copyright 2001 Niels Provos <provos@citi.umich.edu>
  * All rights reserved.
@@ -52,7 +55,7 @@
 #include <unistd.h>
 
 void
-send_fd(int sock, int fd)
+send_fd(int fd)
 {
        struct msghdr msg;
        union {
@@ -84,15 +87,15 @@ send_fd(int sock, int fd)
        msg.msg_iov = &vec;
        msg.msg_iovlen = 1;
 
-       if ((n = sendmsg(sock, &msg, 0)) == -1)
-               log_warn("privsep", "sendmsg(%d)", sock);
+       if ((n = sendmsg(remote, &msg, 0)) == -1)
+               log_warn("privsep", "sendmsg(%d)", remote);
        if (n != sizeof(int))
                log_warnx("privsep", "sendmsg: expected sent 1 got %ld",
                    (long)n);
 }
 
 int
-receive_fd(int sock)
+receive_fd()
 {
        struct msghdr msg;
        union {
@@ -113,7 +116,7 @@ receive_fd(int sock)
        msg.msg_control = &cmsgbuf.buf;
        msg.msg_controllen = sizeof(cmsgbuf.buf);
 
-       if ((n = recvmsg(sock, &msg, 0)) == -1)
+       if ((n = recvmsg(remote, &msg, 0)) == -1)
                log_warn("privsep", "recvmsg");
        if (n != sizeof(int))
                log_warnx("privsep", "recvmsg: expected received 1 got %ld",
@@ -134,3 +137,93 @@ receive_fd(int sock)
                return -1;
        }
 }
+
+/* Stolen from sbin/pflogd/privsep.c from OpenBSD */
+/*
+ * Copyright (c) 2003 Can Erkin Acar
+ * Copyright (c) 2003 Anil Madhavapeddy <anil@recoil.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* Read all data or return 1 for error.  */
+int
+may_read(void *buf, size_t n)
+{
+       char *s = buf;
+       ssize_t res, pos = 0;
+
+       while (n > pos) {
+               res = read(remote, s + pos, n - pos);
+               switch (res) {
+               case -1:
+                       if (errno == EINTR || errno == EAGAIN)
+                               continue;
+               case 0:
+                       return (1);
+               default:
+                       pos += res;
+               }
+       }
+       return (0);
+}
+
+/* Read data with the assertion that it all must come through, or
+ * else abort the process.  Based on atomicio() from openssh. */
+void
+must_read(void *buf, size_t n)
+{
+       char *s = buf;
+       ssize_t res, pos = 0;
+
+       while (n > pos) {
+               res = read(remote, s + pos, n - pos);
+               switch (res) {
+               case -1:
+                       if (errno == EINTR || errno == EAGAIN)
+                               continue;
+               case 0:
+                       _exit(0);
+               default:
+                       pos += res;
+               }
+       }
+}
+
+/* Write data with the assertion that it all has to be written, or
+ * else abort the process.  Based on atomicio() from openssh. */
+void
+must_write(const void *buf, size_t n)
+{
+       const char *s = buf;
+       ssize_t res, pos = 0;
+
+       while (n > pos) {
+               res = write(remote, s + pos, n - pos);
+               switch (res) {
+               case -1:
+                       if (errno == EINTR || errno == EAGAIN)
+                               continue;
+               case 0:
+                       _exit(0);
+               default:
+                       pos += res;
+               }
+       }
+}
+
+void
+priv_remote(int fd)
+{
+       remote = fd;
+}