From 4ea0565ebd7c884f9e78432bf70fc34777ffe6bd Mon Sep 17 00:00:00 2001 From: Vincent Bernat Date: Tue, 2 Jul 2013 22:38:16 +0200 Subject: [PATCH] privsep: separate OS specific code to dedicated files 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 | 23 +- src/daemon/lldpd.h | 25 +- src/daemon/priv-bpf.c | 136 +++++ src/daemon/priv-linux.c | 199 +++++++ src/daemon/priv.c | 520 ++---------------- src/daemon/{privsep_fdpass.c => privsep_io.c} | 103 +++- 6 files changed, 527 insertions(+), 479 deletions(-) create mode 100644 src/daemon/priv-bpf.c create mode 100644 src/daemon/priv-linux.c rename src/daemon/{privsep_fdpass.c => privsep_io.c} (61%) diff --git a/src/daemon/Makefile.am b/src/daemon/Makefile.am index 86ea9039..dd803abd 100644 --- a/src/daemon/Makefile.am +++ b/src/daemon/Makefile.am @@ -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 diff --git a/src/daemon/lldpd.h b/src/daemon/lldpd.h index 49167feb..3ed65d0d 100644 --- a/src/daemon/lldpd.h +++ b/src/daemon/lldpd.h @@ -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 index 00000000..ea9bb71b --- /dev/null +++ b/src/daemon/priv-bpf.c @@ -0,0 +1,136 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2008 Vincent Bernat + * + * 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 +#include +#include +#include +#include +#include +#include + +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 index 00000000..553a8531 --- /dev/null +++ b/src/daemon/priv-linux.c @@ -0,0 +1,199 @@ +/* -*- mode: c; c-file-style: "openbsd" -*- */ +/* + * Copyright (c) 2008 Vincent Bernat + * + * 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 +#include +#include +#include +#include +#include +#include /* For sockaddr_ll */ +#include /* For BPF filtering */ +#include +#include + +/* 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)ðc; + 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(ðc, 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; +} diff --git a/src/daemon/priv.c b/src/daemon/priv.c index 23cfbc4f..8cc61001 100644 --- a/src/daemon/priv.c +++ b/src/daemon/priv.c @@ -30,31 +30,19 @@ #include #include #include -#include #include #include #include #include #include -#ifdef HOST_OS_LINUX -# include /* For sockaddr_ll */ -# include /* 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 -#endif +#include + #if defined HOST_OS_FREEBSD || HOST_OS_OSX || HOST_OS_DRAGONFLY # include #endif #if defined HOST_OS_SOLARIS # include #endif -#include /* Use resolv.h */ #ifdef HAVE_SYS_TYPES_H @@ -71,34 +59,16 @@ #endif #include -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 -#include -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)ðc; - 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, ðc, 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 - * - * 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; - } - } -} diff --git a/src/daemon/privsep_fdpass.c b/src/daemon/privsep_io.c similarity index 61% rename from src/daemon/privsep_fdpass.c rename to src/daemon/privsep_io.c index 0a1ba08b..638e5458 100644 --- a/src/daemon/privsep_fdpass.c +++ b/src/daemon/privsep_io.c @@ -1,4 +1,7 @@ /* -*- mode: c; c-file-style: "openbsd" -*- */ + +static int remote; + /* * Copyright 2001 Niels Provos * All rights reserved. @@ -52,7 +55,7 @@ #include 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 + * + * 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; +} -- 2.39.5