The following diff is loosely based on the approach that was taken for
pledging mountd. Other code/approaches leveraged from various networking
daemons.

This first step moves the ioctl with SIOCGIFDATA call to a privileged
child so we can at least pledge "stdio rpath dns inet proc exec" without
(intentionally) changing existing behaviour.

Comments and suggestions welcome.

Thanks!

Rob

Index: Makefile
===================================================================
RCS file: /cvs/src/usr.sbin/ifstated/Makefile,v
retrieving revision 1.10
diff -u -p -r1.10 Makefile
--- Makefile    20 Jul 2010 02:05:15 -0000      1.10
+++ Makefile    13 Jul 2017 16:17:20 -0000
@@ -8,7 +8,7 @@ CFLAGS+= -Wmissing-declarations
 CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual
 YFLAGS=
 MAN= ifstated.8 ifstated.conf.5
-LDADD+=-levent
+LDADD+=-levent -lutil
 DPADD+= ${LIBEVENT}
 
 .include <bsd.prog.mk>
Index: ifstated.c
===================================================================
RCS file: /cvs/src/usr.sbin/ifstated/ifstated.c,v
retrieving revision 1.50
diff -u -p -r1.50 ifstated.c
--- ifstated.c  4 Jul 2017 21:09:52 -0000       1.50
+++ ifstated.c  13 Jul 2017 16:17:20 -0000
@@ -25,13 +25,16 @@
 #include <sys/types.h>
 #include <sys/time.h>
 #include <sys/ioctl.h>
+#include <sys/queue.h>
 #include <sys/socket.h>
+#include <sys/uio.h>
 #include <sys/wait.h>
 
 #include <net/if.h>
 #include <net/route.h>
 #include <netinet/in.h>
 
+#include <poll.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -39,6 +42,7 @@
 #include <stdint.h>
 #include <syslog.h>
 #include <err.h>
+#include <errno.h>
 #include <event.h>
 #include <unistd.h>
 #include <ifaddrs.h>
@@ -47,7 +51,9 @@
 #include "log.h"
 
 struct  ifsd_config *conf = NULL, *newconf = NULL;
+struct  imsgbuf ibuf;
 
+pid_t   privchild_pid;
 int     opts = 0;
 int     opt_inhibit = 0;
 char   *configfile = "/etc/ifstated.conf";
@@ -74,6 +80,7 @@ void          do_action(struct ifsd_action *);
 void           remove_action(struct ifsd_action *, struct ifsd_state *);
 void           remove_expression(struct ifsd_expression *,
                    struct ifsd_state *);
+void           privchild(int);
 
 __dead void
 usage(void)
@@ -89,6 +96,7 @@ int
 main(int argc, char *argv[])
 {
        struct timeval tv;
+       int socks[2];
        int ch, rt_fd;
        int debug = 0;
        unsigned int rtfilter;
@@ -143,6 +151,22 @@ main(int argc, char *argv[])
        if (!debug)
                daemon(1, 0);
 
+       if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, socks) == -1) {
+               fatal("%s: socketpair", __func__);
+       }
+
+       switch (privchild_pid = fork()) {
+       case -1:
+               fatal("%s: fork", __func__);
+       case 0:
+               close(socks[0]);
+               privchild(socks[1]);
+       }
+       close(socks[1]);
+
+       imsg_init(&ibuf, socks[0]);
+       setproctitle("parent");
+
        event_init();
        log_init(debug, LOG_DAEMON);
        log_setverbose(opts & IFSD_OPT_VERBOSE);
@@ -160,6 +184,9 @@ main(int argc, char *argv[])
            &rtfilter, sizeof(rtfilter)) == -1) /* not fatal */
                log_warn("%s: setsockopt tablefilter", __func__);
 
+       if (pledge("stdio rpath dns inet proc exec", NULL) == -1)
+               fatal("pledge");
+
        signal_set(&sigchld_ev, SIGCHLD, sigchld_handler, NULL);
        signal_add(&sigchld_ev, NULL);
 
@@ -252,6 +279,16 @@ rt_msg_handler(int fd, short event, void
 void
 sigchld_handler(int fd, short event, void *arg)
 {
+       pid_t pid;
+       int status;
+
+       do {
+               pid = waitpid(privchild_pid, &status, WNOHANG);
+               if (pid > 0 && (WIFEXITED(status) || WIFSIGNALED(status)))
+                       fatal("privchild %i exited due to receipt of signal %i",
+                           privchild_pid, WTERMSIG(status));
+       } while (pid == -1 && errno == EINTR);
+
        check_external_status(&conf->initstate);
        if (conf->curstate != NULL)
                check_external_status(conf->curstate);
@@ -599,29 +636,75 @@ do_action(struct ifsd_action *action)
 void
 fetch_ifstate(void)
 {
+       struct imsg imsg;
        struct ifaddrs *ifap, *ifa;
+       struct pollfd pfd[1];
        char *oname = NULL;
        int sock = socket(AF_INET, SOCK_DGRAM, 0);
+       int done = 0, state, rv;
+       ssize_t n;
 
        if (getifaddrs(&ifap) != 0)
                err(1, "getifaddrs");
 
        for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
                struct ifreq ifr;
-               struct if_data ifrdat;
 
                if (oname && !strcmp(oname, ifa->ifa_name))
                        continue;
                oname = ifa->ifa_name;
 
                strlcpy(ifr.ifr_name, ifa->ifa_name, sizeof(ifr.ifr_name));
-               ifr.ifr_data = (caddr_t)&ifrdat;
-
-               if (ioctl(sock, SIOCGIFDATA, (caddr_t)&ifr) == -1)
-                       continue;
 
-               scan_ifstate(if_nametoindex(ifa->ifa_name),
-                   ifrdat.ifi_link_state, 0);
+               /*
+                * synchronous imsg request/response to privchild
+                */
+               if (imsg_compose(&ibuf, IMSG_IFSTATE_REQ, 0, 0, -1, &ifr,
+                  sizeof(struct ifreq)) == -1)
+                       fatal("%s: imsg_compose", __func__);
+               do {
+                       rv = imsg_flush(&ibuf);
+               } while (rv == -1 && errno == EAGAIN);
+               if (rv == -1)
+                       fatal("%s: imsg_flush", __func__);
+
+               pfd[0].fd = ibuf.fd;
+               pfd[0].events = POLLIN;
+               while (!done) {
+                       log_info("%s: polling IN\n", __func__);
+                       switch (poll(pfd, 1, IFSTATE_REQ_TIMEOUT)) {
+                       case -1:
+                               fatal("%s: poll", __func__);
+                       case 0:
+                               log_warnx("%s: poll timeout", __func__);
+                               break;
+                       default:
+                               break;
+                       }
+                       if ((n = imsg_read(&ibuf)) == -1 && errno != EAGAIN)
+                               fatal("%s: imsg_read", __func__);
+                       if (n == 0)
+                               fatalx("%s: socket disconnected", __func__);
+                       while (!done) {
+                               if ((n = imsg_get(&ibuf, &imsg)) == -1)
+                                       fatal("%s: imsg_get", __func__);
+                               if (n == 0) {
+                                       log_warnx("%s: no message ready",
+                                          __func__);
+                                       break;
+                               }
+                               if (imsg.hdr.type != IMSG_IFSTATE_RES)
+                                       fatalx("%s: invalid response",
+                                          __func__);
+                               IMSG_SIZE_CHECK(&imsg, &state);
+                               memcpy(&state, imsg.data,
+                                  sizeof(int));
+                               scan_ifstate(if_nametoindex(
+                                  ifa->ifa_name), state, 0);
+                               done = 1;
+                               imsg_free(&imsg);
+                       }
+               }
        }
        freeifaddrs(ifap);
        close(sock);
@@ -710,4 +793,84 @@ remove_expression(struct ifsd_expression
                break;
        }
        free(expression);
+}
+
+void
+privchild(int sock)
+{
+       struct pollfd pfd[1];
+       struct imsg imsg;
+       struct ifreq ifr;
+       struct if_data ifrdat;
+       int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+       int n, rv, state;
+
+       setproctitle("[priv]");
+       imsg_init(&ibuf, sock);
+
+       signal(SIGHUP, SIG_IGN);
+
+       for (;;) {
+               pfd[0].fd = ibuf.fd;
+               pfd[0].events = POLLIN;
+               switch (poll(pfd, 1, INFTIM)) {
+               case -1:
+                       if (errno == EINTR)
+                               continue;
+                       fatal("%s: poll", __func__);
+               case 0:
+                       log_warnx("%s: poll timeout", __func__);
+                       break;
+               default:
+                       break;
+               }
+               if (pfd[0].revents & POLLHUP) {
+                       fatalx("%s: socket disconnected", __func__);
+               }
+               if (!(pfd[0].revents & POLLIN))
+                       continue;
+
+               if ((n = imsg_read(&ibuf)) == -1 && errno != EAGAIN)
+                       fatal("%s: imsg_read", __func__);
+               if (n == 0)
+                       fatalx("%s: socket disconnected", __func__);
+
+               while ((n = imsg_get(&ibuf, &imsg)) != 0) {
+                       if (n == -1) {
+                               fatal("%s: imsg_get", __func__);
+                       }
+                       if (n == 0) {
+                               log_warnx("%s: no message ready", __func__);
+                               break;
+                       }
+                       switch (imsg.hdr.type) {
+                       case IMSG_IFSTATE_REQ:
+                               IMSG_SIZE_CHECK(&imsg, &ifr);
+                               memcpy(&ifr, imsg.data, sizeof(struct ifreq
+                                  *));
+                               ifr.ifr_data = (caddr_t)&ifrdat;
+
+                               if (ioctl(sockfd, SIOCGIFDATA,
+                                  (caddr_t)&ifr) == -1) {
+                                       fatal("%s: ioctl", __func__);
+                               }
+                               state = (int)ifrdat.ifi_link_state;
+
+                               if (imsg_compose(&ibuf, IMSG_IFSTATE_RES, 0,
+                                  0, -1, &state, sizeof(int)) == -1)
+                                       fatal("%s: imsg_compose", __func__);
+                               do {
+                                       rv = imsg_flush(&ibuf);
+                               } while (rv == -1 && errno == EAGAIN);
+                               if (rv == -1)
+                                       fatal("imsg_flush");
+                               break;
+                       default:
+                               fatalx("%s: invalid message type", __func__);
+                       }
+                       imsg_free(&imsg);
+               }
+       }
+       close(sockfd);
+       close(sock);
 }
Index: ifstated.h
===================================================================
RCS file: /cvs/src/usr.sbin/ifstated/ifstated.h,v
retrieving revision 1.16
diff -u -p -r1.16 ifstated.h
--- ifstated.h  4 Jul 2017 21:04:14 -0000       1.16
+++ ifstated.h  13 Jul 2017 16:17:21 -0000
@@ -28,6 +28,7 @@
 
 #include <sys/types.h>
 #include <sys/queue.h>
+#include <imsg.h>
 
 struct ifsd_expression;
 TAILQ_HEAD(ifsd_expression_list, ifsd_expression);
@@ -126,6 +127,19 @@ struct ifsd_config {
 #define IFSD_OPT_VERBOSE2      0x00000002
 #define IFSD_OPT_NOACTION      0x00000004
        int                              maxdepth;
+};
+
+#define IMSG_SIZE_CHECK(imsg, p) do {                          \
+       if (IMSG_DATA_SIZE(imsg) < sizeof(*p))                  \
+               fatalx("bad length imsg received");             \
+} while (0)
+#define IMSG_DATA_SIZE(imsg)   ((imsg)->hdr.len - IMSG_HEADER_SIZE)
+
+#define IFSTATE_REQ_TIMEOUT    200
+
+enum imsg_type {
+       IMSG_IFSTATE_REQ,
+       IMSG_IFSTATE_RES
 };
 
 enum   { IFSD_EVTIMER_ADD, IFSD_EVTIMER_DEL };

Reply via email to