fork+exec model for pflogd(8); moves pcap init to the re-exec'd privsep
parent and uses 'legit' fdpassing primitives to send the bpf fd to the
unprivileged child process.

I've tried to keep this difff small so that it can go in, there's more
work to be done in the future, but this will let things proceed further.

Tested with normal usage, late snaplen setting, and also that the
optional tcpdump 'expression' feature still works.

Remember to use absolute paths if testing.

ok?

-Bryan.

Index: pflogd.c
===================================================================
RCS file: /cvs/src/sbin/pflogd/pflogd.c,v
retrieving revision 1.54
diff -u -p -u -r1.54 pflogd.c
--- sbin/pflogd/pflogd.c        23 Jul 2017 14:28:22 -0000      1.54
+++ sbin/pflogd/pflogd.c        30 Aug 2017 04:25:46 -0000
@@ -54,6 +54,7 @@ pcap_t *hpcap;
 static FILE *dpcap;
 
 int Debug = 0;
+static int privchild = 0;
 static int snaplen = DEF_SNAPLEN;
 static int cur_snaplen = DEF_SNAPLEN;
 
@@ -73,7 +74,6 @@ void  dump_packet(u_char *, const struct
 void  dump_packet_nobuf(u_char *, const struct pcap_pkthdr *, const u_char *);
 int   flush_buffer(FILE *);
 int   if_exists(char *);
-int   init_pcap(void);
 void  logmsg(int, const char *, ...);
 void  purge_buffer(void);
 int   reset_dump(int);
@@ -215,8 +215,6 @@ init_pcap(void)
 
        set_pcap_filter();
 
-       cur_snaplen = snaplen = pcap_snapshot(hpcap);
-
        /* lock */
        if (ioctl(pcap_fileno(hpcap), BIOCLOCK) < 0) {
                logmsg(LOG_ERR, "BIOCLOCK: %s", strerror(errno));
@@ -542,9 +540,7 @@ main(int argc, char **argv)
 
        ret = 0;
 
-       closefrom(STDERR_FILENO + 1);
-
-       while ((ch = getopt(argc, argv, "Dxd:f:i:s:")) != -1) {
+       while ((ch = getopt(argc, argv, "Dxd:f:i:P:s:")) != -1) {
                switch (ch) {
                case 'D':
                        Debug = 1;
@@ -560,6 +556,11 @@ main(int argc, char **argv)
                case 'i':
                        interface = optarg;
                        break;
+               case 'P': /* used internally, exec the parent */
+                       privchild = strtonum(optarg, 2, INT_MAX, &errstr);
+                       if (errstr)
+                               errx(1, "priv child %s: %s", errstr, optarg);
+                       break;
                case 's':
                        snaplen = strtonum(optarg, 0, PFLOGD_MAXSNAPLEN,
                            &errstr);
@@ -567,6 +568,7 @@ main(int argc, char **argv)
                                snaplen = DEF_SNAPLEN;
                        if (errstr)
                                snaplen = PFLOGD_MAXSNAPLEN;
+                       cur_snaplen = snaplen;
                        break;
                case 'x':
                        Xflag = 1;
@@ -606,18 +608,14 @@ main(int argc, char **argv)
                if (filter == NULL)
                        logmsg(LOG_NOTICE, "Failed to form filter expression");
        }
+       argc += optind;
+       argv -= optind;
 
-       /* initialize pcap before dropping privileges */
-       if (init_pcap()) {
-               logmsg(LOG_ERR, "Exiting, init failure");
-               exit(1);
-       }
+       if (privchild > 1)
+               priv_exec(privchild, argc, argv);
 
        /* Privilege separation begins here */
-       if (priv_init()) {
-               logmsg(LOG_ERR, "unable to privsep");
-               exit(1);
-       }
+       priv_init(argc, argv);
 
        /*
         * XXX needs wpath cpath rpath, for try_reset_dump() ?
@@ -633,6 +631,9 @@ main(int argc, char **argv)
        signal(SIGALRM, sig_alrm);
        signal(SIGHUP, sig_hup);
        alarm(delay);
+
+       if (priv_init_pcap(snaplen))
+               errx(1, "priv_init_pcap failed");
 
        buffer = malloc(PFLOGD_BUFSIZE);
 
Index: pflogd.h
===================================================================
RCS file: /cvs/src/sbin/pflogd/pflogd.h,v
retrieving revision 1.5
diff -u -p -u -r1.5 pflogd.h
--- sbin/pflogd/pflogd.h        10 Oct 2015 22:36:06 -0000      1.5
+++ sbin/pflogd/pflogd.h        30 Aug 2017 04:25:46 -0000
@@ -34,13 +34,15 @@
 void  logmsg(int priority, const char *message, ...);
 
 /* Privilege separation */
-int    priv_init(void);
+void   priv_init(int, char **);
+__dead void priv_exec(int, int, char **);
+int    priv_init_pcap(int);
 int    priv_set_snaplen(int snaplen);
 int    priv_open_log(void);
 int    priv_move_log(void);
 int    priv_pcap_stats(struct pcap_stat *);
-pcap_t *pcap_open_live_fd(int fd, int snaplen, char *ebuf);
 
+int   init_pcap(void);
 void set_pcap_filter(void);
 /* File descriptor send/recv */
 void send_fd(int, int);
Index: privsep.c
===================================================================
RCS file: /cvs/src/sbin/pflogd/privsep.c,v
retrieving revision 1.27
diff -u -p -u -r1.27 privsep.c
--- sbin/pflogd/privsep.c       12 Aug 2017 16:31:09 -0000      1.27
+++ sbin/pflogd/privsep.c       30 Aug 2017 04:25:46 -0000
@@ -40,6 +40,7 @@
 #include "pflogd.h"
 
 enum cmd_types {
+       PRIV_INIT_PCAP,         /* init pcap fdpass bpf */
        PRIV_SET_SNAPLEN,       /* set the snaplength */
        PRIV_MOVE_LOG,          /* move logfile away */
        PRIV_OPEN_LOG,          /* open logfile for appending */
@@ -59,18 +60,17 @@ static int  set_snaplen(int snap);
 static int  move_log(const char *name);
 
 extern char *filename;
+extern char *interface;
+extern char errbuf[PCAP_ERRBUF_SIZE];
 extern pcap_t *hpcap;
 
 /* based on syslogd privsep */
-int
-priv_init(void)
+void
+priv_init(int argc, char *argv[])
 {
-       int i, bpfd = -1, socks[2], cmd;
-       int snaplen, ret, olderrno;
+       int i, nargc, socks[2];
        struct passwd *pw;
-
-       for (i = 1; i < _NSIG; i++)
-               signal(i, SIG_DFL);
+       char childnum[11], **privargv;
 
        /* Create sockets */
        if (socketpair(AF_LOCAL, SOCK_STREAM, PF_UNSPEC, socks) == -1)
@@ -103,8 +103,45 @@ priv_init(void)
                        err(1, "setresuid() failed");
                close(socks[0]);
                priv_fd = socks[1];
-               return 0;
+               return;
        }
+       close(socks[1]);
+
+       if (dup2(socks[0], 3) == -1)
+               err(1, "dup2 priv sock failed");
+       closefrom(4);
+
+       snprintf(childnum, sizeof(childnum), "%d", child_pid);
+       if ((privargv = reallocarray(NULL, argc + 3, sizeof(char *))) == NULL)
+               err(1, "alloc priv argv failed");
+       nargc = 0;
+       privargv[nargc++] = argv[0];
+       privargv[nargc++] = "-P";
+       privargv[nargc++] = childnum;
+       for (i = 1; i < argc; i++)
+               privargv[nargc++] = argv[i];
+       privargv[nargc] = NULL;
+       execvp(privargv[0], privargv);
+       err(1, "exec priv '%s' failed", privargv[0]);
+}
+
+__dead void
+priv_exec(int child, int argc, char *argv[])
+{
+       int i, fd = -1, bpfd = -1, sock, cmd;
+       int snaplen, ret, olderrno;
+       unsigned int buflen;
+
+       if (argc <= 2 || strcmp("-P", argv[1]) != 0)
+               errx(1, "exec without priv");
+
+       sock = 3;
+       closefrom(4);
+
+       child_pid = child;
+
+       for (i = 1; i < _NSIG; i++)
+               signal(i, SIG_DFL);
 
        /* Father */
        /* Pass ALRM/TERM/HUP/INT/QUIT through to child, and accept CHLD */
@@ -116,7 +153,6 @@ priv_init(void)
        signal(SIGCHLD, sig_chld);
 
        setproctitle("[priv]");
-       close(socks[1]);
 
 #if 0
        /* This needs to do bpf ioctl */
@@ -125,13 +161,31 @@ BROKEN    if (pledge("stdio rpath wpath cpa
 #endif
 
        while (!gotsig_chld) {
-               if (may_read(socks[0], &cmd, sizeof(int)))
+               if (may_read(sock, &cmd, sizeof(int)))
                        break;
                switch (cmd) {
+               case PRIV_INIT_PCAP:
+                       logmsg(LOG_DEBUG,
+                           "[priv]: msg PRIV_INIT_PCAP received");
+                       /* initialize pcap */
+                       if (hpcap != NULL || init_pcap()) {
+                               logmsg(LOG_ERR, "[priv]: Exiting, init failed");
+                               _exit(1);
+                       }
+                       buflen = hpcap->bufsize; /* BIOCGBLEN for unpriv proc */
+                       must_write(sock, &buflen, sizeof(unsigned int));
+                       fd = pcap_fileno(hpcap);
+                       send_fd(sock, fd);
+                       if (fd < 0) {
+                               logmsg(LOG_ERR, "[priv]: Exiting, init failed");
+                               _exit(1);
+                       }
+                       break;
+
                case PRIV_SET_SNAPLEN:
                        logmsg(LOG_DEBUG,
                            "[priv]: msg PRIV_SET_SNAPLENGTH received");
-                       must_read(socks[0], &snaplen, sizeof(int));
+                       must_read(sock, &snaplen, sizeof(int));
 
                        ret = set_snaplen(snaplen);
                        if (ret) {
@@ -140,7 +194,7 @@ BROKEN      if (pledge("stdio rpath wpath cpa
                                   snaplen);
                        }
 
-                       must_write(socks[0], &ret, sizeof(int));
+                       must_write(sock, &ret, sizeof(int));
                        break;
 
                case PRIV_OPEN_LOG:
@@ -155,7 +209,7 @@ BROKEN      if (pledge("stdio rpath wpath cpa
                            O_RDWR|O_CREAT|O_APPEND|O_NONBLOCK|O_NOFOLLOW,
                            0600);
                        olderrno = errno;
-                       send_fd(socks[0], bpfd);
+                       send_fd(sock, bpfd);
                        if (bpfd < 0)
                                logmsg(LOG_NOTICE,
                                    "[priv]: failed to open %s: %s",
@@ -166,7 +220,7 @@ BROKEN      if (pledge("stdio rpath wpath cpa
                        logmsg(LOG_DEBUG,
                            "[priv]: msg PRIV_MOVE_LOG received");
                        ret = move_log(filename);
-                       must_write(socks[0], &ret, sizeof(int));
+                       must_write(sock, &ret, sizeof(int));
                        break;
 
                default:
@@ -176,7 +230,7 @@ BROKEN      if (pledge("stdio rpath wpath cpa
                }
        }
 
-       _exit(1);
+       exit(1);
 }
 
 /* this is called from parent */
@@ -229,6 +283,46 @@ move_log(const char *name)
 
        logmsg(LOG_NOTICE,
               "[priv]: log file %s moved to %s", name, ren);
+
+       return (0);
+}
+
+
+/* receive bpf fd from privileged proces using fdpass and init pcap */
+int
+priv_init_pcap(int snaplen)
+{
+       int cmd, fd;
+       unsigned int buflen;
+
+       if (priv_fd < 0)
+               errx(1, "%s: called from privileged portion", __func__);
+
+       cmd = PRIV_INIT_PCAP;
+
+       must_write(priv_fd, &cmd, sizeof(int));
+       must_read(priv_fd, &buflen, sizeof(unsigned int));
+       fd = receive_fd(priv_fd);
+       if (fd < 0)
+               return (-1);
+
+       /* XXX temporary until pcap_open_live_fd API */
+       hpcap = pcap_create(interface, errbuf);
+       if (hpcap == NULL)
+               return (-1);
+
+       /* XXX copies from pcap_open_live/pcap_activate */
+       hpcap->fd = fd;
+       pcap_set_snaplen(hpcap, snaplen);
+       pcap_set_promisc(hpcap, 1);
+       pcap_set_timeout(hpcap, PCAP_TO_MS);
+       hpcap->oldstyle = 1;
+       hpcap->linktype = DLT_PFLOG;
+       hpcap->bufsize = buflen; /* XXX bpf BIOCGBLEN */
+       hpcap->buffer = malloc(hpcap->bufsize);
+       if (hpcap->buffer == NULL)
+               return (-1);
+       hpcap->activated = 1;
 
        return (0);
 }

Reply via email to