On Wed, Sep 13, 2017 at 09:53:09PM -0400, Bryan Steele wrote:
> On Wed, Sep 13, 2017 at 08:58:28PM -0400, Bryan Steele wrote:
> > 
> > 'rpath dns' for DNS lookups
> >
> 
> To clarify, "rpath" is not needed for DNS lookups, but currently for
> other address to name translations, i.e: getrpcbynumber(3) and
> ether_ntohost(3).

Here's a diff to pre-open both of these, using setrpcent(1) which
keeps /etc/rpc open in libc, and including a modified copy of
ether_ntohost from libc.

I'd appreciate testing to see if any more open paths happen late,
with the diff below, the "rpath" promise is completely removed!

-Bryan.

Index: pfctl_osfp.c
===================================================================
RCS file: /cvs/src/usr.sbin/tcpdump/pfctl_osfp.c,v
retrieving revision 1.13
diff -u -p -u -r1.13 pfctl_osfp.c
--- usr.sbin/tcpdump/pfctl_osfp.c       28 May 2017 10:06:12 -0000      1.13
+++ usr.sbin/tcpdump/pfctl_osfp.c       14 Sep 2017 05:25:27 -0000
@@ -81,17 +81,14 @@ void                         print_name_list(int, struct 
name
 void                    sort_name_list(int, struct name_list *);
 struct name_entry      *lookup_name_list(struct name_list *, const char *);
 
-/* XXX arbitrary */
-#define MAX_FP_LINE 1024
-
 /* Load fingerprints from a file */
 int
 pfctl_file_fingerprints(int dev, int opts, const char *fp_filename)
 {
-       u_char buf[MAX_FP_LINE];
+       FILE *in;
        u_char *line;
        size_t len;
-       int i, lineno = 0;
+       int i, fd, lineno = 0;
        int window, w_mod, ttl, df, psize, p_mod, mss, mss_mod, wscale,
            wscale_mod, optcnt, ts0;
        pf_tcpopts_t packed_tcpopts;
@@ -99,15 +96,22 @@ pfctl_file_fingerprints(int dev, int opt
        struct pf_osfp_ioctl fp;
 
        pfctl_flush_my_fingerprints(&classes);
+       
+       fd = priv_open_pfosfp();
+       if (fd < 0)
+               return (1);
+       
+       if ((in = fdopen(fd, "r")) == NULL) {
+               warn("%s", fp_filename);
+               return (1);
+       }
+
        class = version = subtype = desc = tcpopts = NULL;
 
        if ((opts & PF_OPT_NOACTION) == 0)
                pfctl_clear_fingerprints(dev, opts);
 
-       priv_getlines(FTAB_PFOSFP);
-       while ((len = priv_getline(buf, sizeof(buf))) > 0) {
-               buf[len -1] = '\n';
-               line = buf;
+       while ((line = fgetln(in, &len)) != NULL) {
                lineno++;
                free(class);
                free(version);
Index: privsep.c
===================================================================
RCS file: /cvs/src/usr.sbin/tcpdump/privsep.c,v
retrieving revision 1.47
diff -u -p -u -r1.47 privsep.c
--- usr.sbin/tcpdump/privsep.c  8 Sep 2017 19:30:13 -0000       1.47
+++ usr.sbin/tcpdump/privsep.c  14 Sep 2017 05:25:27 -0000
@@ -29,6 +29,7 @@
 #include <net/pfvar.h>
 
 #include <rpc/rpc.h>
+#include <rpcsvc/ypclnt.h>
 
 #include <err.h>
 #include <errno.h>
@@ -73,12 +74,13 @@ static const int allowed_max[] = {
        /* INIT */      ALLOW(PRIV_OPEN_BPF) | ALLOW(PRIV_OPEN_DUMP) |
                        ALLOW(PRIV_SETFILTER),
        /* BPF */       ALLOW(PRIV_SETFILTER),
-       /* FILTER */    ALLOW(PRIV_OPEN_OUTPUT) | ALLOW(PRIV_GETSERVENTRIES) |
+       /* FILTER */    ALLOW(PRIV_OPEN_PFOSFP) | ALLOW(PRIV_OPEN_OUTPUT) |
+                       ALLOW(PRIV_GETSERVENTRIES) |
                        ALLOW(PRIV_GETPROTOENTRIES) |
                        ALLOW(PRIV_ETHER_NTOHOST) | ALLOW(PRIV_INIT_DONE),
        /* RUN */       ALLOW(PRIV_GETHOSTBYADDR) | ALLOW(PRIV_ETHER_NTOHOST) |
-                       ALLOW(PRIV_GETRPCBYNUMBER) | ALLOW(PRIV_GETLINES) |
-                       ALLOW(PRIV_LOCALTIME) | ALLOW(PRIV_PCAP_STATS),
+                       ALLOW(PRIV_GETRPCBYNUMBER) | ALLOW(PRIV_LOCALTIME) |
+                       ALLOW(PRIV_PCAP_STATS),
        /* EXIT */      0
 };
 
@@ -90,21 +92,10 @@ static int allowed_ext[] = {
        /* INIT */      ALLOW(PRIV_SETFILTER),
        /* BPF */       ALLOW(PRIV_SETFILTER),
        /* FILTER */    ALLOW(PRIV_GETSERVENTRIES),
-       /* RUN */       ALLOW(PRIV_GETLINES) | ALLOW(PRIV_LOCALTIME) |
-                       ALLOW(PRIV_PCAP_STATS),
+       /* RUN */       ALLOW(PRIV_LOCALTIME) | ALLOW(PRIV_PCAP_STATS),
        /* EXIT */      0
 };
 
-struct ftab {
-       char *name;
-       int max;
-       int count;
-};
-
-static struct ftab file_table[] = {{PF_OSFP_FILE, 1, 0}};
-
-#define NUM_FILETAB (sizeof(file_table) / sizeof(struct ftab))
-
 int            debug_level = LOG_INFO;
 int            priv_fd = -1;
 volatile       pid_t child_pid = -1;
@@ -112,28 +103,61 @@ static volatile   sig_atomic_t cur_state =
 
 extern void    set_slave_signals(void);
 
+static void    drop_privs(int);
+
 static void    impl_open_bpf(int, int *);
 static void    impl_open_dump(int, const char *);
+static void    impl_open_pfosfp(int);
 static void    impl_open_output(int, const char *);
 static void    impl_setfilter(int, char *, int *);
 static void    impl_init_done(int, int *);
 static void    impl_gethostbyaddr(int);
-static void    impl_ether_ntohost(int);
+static void    impl_ether_ntohost(int, FILE *);
 static void    impl_getrpcbynumber(int);
 static void    impl_getserventries(int);
 static void    impl_getprotoentries(int);
 static void    impl_localtime(int fd);
-static void    impl_getlines(int);
 static void    impl_pcap_stats(int, int *);
 
 static void    test_state(int, int);
 static void    logmsg(int, const char *, ...);
 
+static void
+drop_privs(int nochroot)
+{
+       struct passwd *pw;
+
+       /*
+        * If run as regular user, then tcpdump will rely on
+        * pledge(2). If we are root, we want to chroot also..
+        */
+       if (getuid() != 0)
+               return;
+
+       pw = getpwnam("_tcpdump");
+       if (pw == NULL)
+               errx(1, "unknown user _tcpdump");
+
+       if (!nochroot) {
+               if (chroot(pw->pw_dir) == -1)
+                       err(1, "unable to chroot");
+               if (chdir("/") == -1)
+                       err(1, "unable to chdir");
+       }
+
+       /* drop to _tcpdump */
+       if (setgroups(1, &pw->pw_gid) == -1)
+               err(1, "setgroups() failed");
+       if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1)
+               err(1, "setresgid() failed");
+       if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1)
+               err(1, "setresuid() failed");
+}
+
 int
 priv_init(int argc, char **argv)
 {
        int i, nargc, socks[2];
-       struct passwd *pw;
        sigset_t allsigs, oset;
        char **privargv;
 
@@ -159,29 +183,7 @@ priv_init(int argc, char **argv)
                set_slave_signals();
                sigprocmask(SIG_SETMASK, &oset, NULL);
 
-               /*
-                * If run as regular user, packet parser will rely on
-                * pledge(2). If we are root, we want to chroot also..
-                */
-               if (getuid() != 0)
-                       return (0);
-
-               pw = getpwnam("_tcpdump");
-               if (pw == NULL)
-                       errx(1, "unknown user _tcpdump");
-
-               if (chroot(pw->pw_dir) == -1)
-                       err(1, "unable to chroot");
-               if (chdir("/") == -1)
-                       err(1, "unable to chdir");
-
-               /* drop to _tcpdump */
-               if (setgroups(1, &pw->pw_gid) == -1)
-                       err(1, "setgroups() failed");
-               if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1)
-                       err(1, "setresgid() failed");
-               if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1)
-                       err(1, "setresuid() failed");
+               drop_privs(0);
 
                return (0);
        }
@@ -207,10 +209,11 @@ __dead void
 priv_exec(int argc, char *argv[])
 {
        int bpfd = -1;
-       int i, sock, cmd, nflag = 0, Pflag = 0;
+       int i, sock, cmd, nflag = 0, oflag = 0, Pflag = 0;
        char *cmdbuf, *infile = NULL;
        char *RFileName = NULL;
        char *WFileName = NULL;
+       FILE *ethersfp = NULL;
 
        sock = 3;
 
@@ -229,6 +232,10 @@ priv_exec(int argc, char *argv[])
                        nflag++;
                        break;
 
+               case 'o':
+                       oflag = 1;
+                       break;
+
                case 'r':
                        RFileName = optarg;
                        break;
@@ -267,12 +274,22 @@ priv_exec(int argc, char *argv[])
        } else
                allowed_ext[STATE_FILTER] |= ALLOW(PRIV_INIT_DONE);
        if (!nflag) {
+               /* pre-open /etc/rpc for getrpcbynumber(3) */
+               setrpcent(1);
+               /* pre-open ethers(5) for ether_ntohost */
+               ethersfp = fopen("/etc/ethers", "r");
+               if (ethersfp == NULL) {
+                       if (errno != ENOENT && errno != EACCES)
+                               err(1, "open");
+               }
                allowed_ext[STATE_RUN] |= ALLOW(PRIV_GETHOSTBYADDR);
                allowed_ext[STATE_FILTER] |= ALLOW(PRIV_ETHER_NTOHOST);
                allowed_ext[STATE_RUN] |= ALLOW(PRIV_ETHER_NTOHOST);
                allowed_ext[STATE_RUN] |= ALLOW(PRIV_GETRPCBYNUMBER);
                allowed_ext[STATE_FILTER] |= ALLOW(PRIV_GETPROTOENTRIES);
        }
+       if (oflag)
+               allowed_ext[STATE_FILTER] |= ALLOW(PRIV_OPEN_PFOSFP);
 
        if (infile)
                cmdbuf = read_infile(infile);
@@ -293,6 +310,10 @@ priv_exec(int argc, char *argv[])
                        test_state(cmd, STATE_BPF);
                        impl_open_dump(sock, RFileName);
                        break;
+               case PRIV_OPEN_PFOSFP:
+                       test_state(cmd, STATE_FILTER);
+                       impl_open_pfosfp(sock);
+                       break;
                case PRIV_OPEN_OUTPUT:
                        test_state(cmd, STATE_RUN);
                        impl_open_output(sock, WFileName);
@@ -305,8 +326,15 @@ priv_exec(int argc, char *argv[])
                        test_state(cmd, STATE_RUN);
                        impl_init_done(sock, &bpfd);
 
-                       if (pledge("stdio rpath inet unix dns recvfd bpf", 
NULL) == -1)
-                               err(1, "pledge");
+                       if (!nflag && WFileName == NULL) {
+                               drop_privs(1);
+                               if (pledge("stdio dns bpf", NULL) == -1)
+                                       err(1, "pledge");
+                       } else {
+                               drop_privs(0);
+                               if (pledge("stdio bpf", NULL) == -1)
+                                       err(1, "pledge");
+                       }
 
                        break;
                case PRIV_GETHOSTBYADDR:
@@ -315,7 +343,7 @@ priv_exec(int argc, char *argv[])
                        break;
                case PRIV_ETHER_NTOHOST:
                        test_state(cmd, cur_state);
-                       impl_ether_ntohost(sock);
+                       impl_ether_ntohost(sock, ethersfp);
                        break;
                case PRIV_GETRPCBYNUMBER:
                        test_state(cmd, STATE_RUN);
@@ -333,10 +361,6 @@ priv_exec(int argc, char *argv[])
                        test_state(cmd, STATE_RUN);
                        impl_localtime(sock);
                        break;
-               case PRIV_GETLINES:
-                       test_state(cmd, STATE_RUN);
-                       impl_getlines(sock);
-                       break;
                case PRIV_PCAP_STATS:
                        test_state(cmd, STATE_RUN);
                        impl_pcap_stats(sock, &bpfd);
@@ -404,6 +428,24 @@ impl_open_dump(int fd, const char *RFile
 }
 
 static void
+impl_open_pfosfp(int fd)
+{
+       int file, err = 0;
+
+       logmsg(LOG_DEBUG, "[priv]: msg PRIV_OPEN_PFOSFP received");
+
+       file = open(PF_OSFP_FILE, O_RDONLY, 0);
+       err = errno;
+       if (file < 0)
+               logmsg(LOG_DEBUG, "[priv]: failed to open %s: %s",
+                   PF_OSFP_FILE, strerror(errno));
+       send_fd(fd, file);
+       must_write(fd, &err, sizeof(int));
+       if (file >= 0)
+               close(file);
+}
+
+static void
 impl_open_output(int fd, const char *WFileName)
 {
        int file, err;
@@ -464,19 +506,71 @@ impl_gethostbyaddr(int fd)
 }
 
 static void
-impl_ether_ntohost(int fd)
+impl_ether_ntohost(int fd, FILE *f)
 {
-       struct ether_addr ether;
-       char hostname[HOST_NAME_MAX+1];
+       char buf[BUFSIZ+1], hostname[HOST_NAME_MAX+1], *p;
+       size_t len;
+       struct ether_addr ether, try;
+       /* YP */
+       char trybuf[sizeof("xx:xx:xx:xx:xx:xx")];
+       int trylen;
 
        logmsg(LOG_DEBUG, "[priv]: msg PRIV_ETHER_NTOHOST received");
 
        /* Expecting: ethernet address */
        must_read(fd, &ether, sizeof(ether));
-       if (ether_ntohost(hostname, &ether) == -1)
+
+       /* YP */
+       snprintf(trybuf, sizeof trybuf, "%x:%x:%x:%x:%x:%x",
+           ether.ether_addr_octet[0], ether.ether_addr_octet[1],
+           ether.ether_addr_octet[2], ether.ether_addr_octet[3],
+           ether.ether_addr_octet[4], ether.ether_addr_octet[5]);
+       trylen = strlen(trybuf);
+
+       if (f == NULL) {
                write_zero(fd);
-       else
-               write_string(fd, hostname);
+               return;
+       }
+
+       /* rewind */
+       (void)fseek(f, 0L, SEEK_SET);
+
+       /* copied from libc/net/ethers.c */
+       while ((p = fgetln(f, &len)) != NULL) {
+               if (p[len-1] == '\n')
+                       len--;
+               if (len > sizeof(buf) - 2)
+                       continue;
+               (void)memcpy(buf, p, len);
+               buf[len] = '\n';        /* code assumes newlines later on */
+               buf[len+1] = '\0';
+
+               /* A + in the file means try YP now.  */
+               if (!strncmp(buf, "+\n", sizeof(buf))) {
+                       char *ypbuf, *ypdom;
+                       int ypbuflen;
+
+                       if (yp_get_default_domain(&ypdom))
+                               continue;
+                       if (yp_match(ypdom, "ethers.byaddr", trybuf,
+                           trylen, &ypbuf, &ypbuflen))
+                               continue;
+                       if (ether_line(ypbuf, &try, hostname) == 0) {
+                               write_string(fd, hostname);
+                               free(ypbuf);
+                               return;
+                       }
+                       free(ypbuf);
+                       continue;
+               }
+
+               if (ether_line(buf, &try, hostname) == 0 &&
+                   memcmp(&try, &ether, sizeof(try)) == 0) {
+                       write_string(fd, hostname);
+                       return;
+               }
+       }
+       write_zero(fd);
 }
 
 static void
@@ -565,55 +659,6 @@ impl_localtime(int fd)
 }
 
 static void
-impl_getlines(int fd)
-{
-       FILE *fp;
-       char *buf, *lbuf, *file;
-       size_t len, fid;
-
-       logmsg(LOG_DEBUG, "[priv]: msg PRIV_GETLINES received");
-
-       must_read(fd, &fid, sizeof(size_t));
-       if (fid >= NUM_FILETAB)
-               errx(1, "invalid file id");
-
-       file = file_table[fid].name;
-
-       if (file == NULL)
-               errx(1, "invalid file referenced");
-
-       if (file_table[fid].count >= file_table[fid].max)
-               errx(1, "maximum open count exceeded for %s", file);
-
-       file_table[fid].count++;
-
-       if ((fp = fopen(file, "r")) == NULL) {
-               write_zero(fd);
-               return;
-       }
-
-       lbuf = NULL;
-       while ((buf = fgetln(fp, &len))) {
-               if (buf[len - 1] == '\n')
-                       buf[len - 1] = '\0';
-               else {
-                       if ((lbuf = malloc(len + 1)) == NULL)
-                               err(1, NULL);
-                       memcpy(lbuf, buf, len);
-                       lbuf[len] = '\0';
-                       buf = lbuf;
-               }
-
-               write_string(fd, buf);
-
-               free(lbuf);
-               lbuf = NULL;
-       }
-       write_zero(fd);
-       fclose(fp);
-}
-
-static void
 impl_pcap_stats(int fd, int *bpfd)
 {
        struct pcap_stat stats;
@@ -774,17 +819,6 @@ priv_localtime(const time_t *t)
        return &lt;
 }
 
-/* start getting lines from a file */
-void
-priv_getlines(size_t sz)
-{
-       if (priv_fd < 0)
-               errx(1, "%s called from privileged portion", __func__);
-
-       write_command(priv_fd, PRIV_GETLINES);
-       must_write(priv_fd, &sz, sizeof(size_t));
-}
-
 int
 priv_pcap_stats(struct pcap_stat *ps)
 {
@@ -796,16 +830,20 @@ priv_pcap_stats(struct pcap_stat *ps)
        return (0);
 }
 
-/* retrieve a line from a file, should be called repeatedly after calling
-   priv_getlines(), until it returns zero. */
-size_t
-priv_getline(char *line, size_t line_len)
+int
+priv_open_pfosfp(void)
 {
-       if (priv_fd < 0)
-               errx(1, "%s called from privileged portion", __func__);
+       int fd, err = 0;
+       write_command(priv_fd, PRIV_OPEN_PFOSFP);
+
+       fd = receive_fd(priv_fd);
+       must_read(priv_fd, &err, sizeof(int));
+       if (fd < 0) {
+               warnc(err, "%s", PF_OSFP_FILE);
+               return (-1);
+       }
 
-       /* read the line */
-       return (read_string(priv_fd, line, line_len, __func__));
+       return (fd);
 }
 
 /* Read all data or return 1 for error. */
Index: privsep.h
===================================================================
RCS file: /cvs/src/usr.sbin/tcpdump/privsep.h,v
retrieving revision 1.10
diff -u -p -u -r1.10 privsep.h
--- usr.sbin/tcpdump/privsep.h  8 Sep 2017 19:10:57 -0000       1.10
+++ usr.sbin/tcpdump/privsep.h  14 Sep 2017 05:25:27 -0000
@@ -21,12 +21,10 @@
 
 #define TCPDUMP_MAGIC 0xa1b2c3d4
 
-/* file ids used by priv_getlines */
-#define FTAB_PFOSFP    0
-
 enum cmd_types {
        PRIV_OPEN_BPF,          /* open a bpf descriptor */
        PRIV_OPEN_DUMP,         /* open dump file for reading */
+       PRIV_OPEN_PFOSFP,       /* open pf.os(5) fingerprint db for reading */
        PRIV_OPEN_OUTPUT,       /* open output file */
        PRIV_SETFILTER,         /* set a bpf read filter */
        PRIV_GETHOSTBYADDR,     /* resolve numeric address into hostname */
@@ -35,7 +33,6 @@ enum cmd_types {
        PRIV_GETSERVENTRIES,    /* get the service entries table */
        PRIV_GETPROTOENTRIES,   /* get the ip protocol entries table */
        PRIV_LOCALTIME,         /* return localtime */
-       PRIV_GETLINES,          /* get lines from a file */
        PRIV_INIT_DONE,         /* signal that the initialization is done */
        PRIV_PCAP_STATS         /* get pcap_stats() results */
 };
@@ -75,12 +72,8 @@ void priv_getprotoentries(void);
    calling priv_getprotoentries() until it returns zero */
 size_t priv_getprotoentry(char *, size_t, int *);
 
-/* Start getting lines from a file */
-void   priv_getlines(size_t);
-
-/* Retrieve a single line from a file, should be called repeatedly after
-   calling priv_getlines() until it returns zero */
-size_t priv_getline(char *, size_t);
+/* Retrieve pf.os(5) fingerprints file descriptor */
+int    priv_open_pfosfp();
 
 /* Return the pcap statistics upon completion */
 int    priv_pcap_stats(struct pcap_stat *);
Index: tcpdump.c
===================================================================
RCS file: /cvs/src/usr.sbin/tcpdump/tcpdump.c,v
retrieving revision 1.80
diff -u -p -u -r1.80 tcpdump.c
--- usr.sbin/tcpdump/tcpdump.c  8 Sep 2017 19:10:57 -0000       1.80
+++ usr.sbin/tcpdump/tcpdump.c  14 Sep 2017 05:25:27 -0000
@@ -461,6 +461,8 @@ main(int argc, char **argv)
                bpf_dump(fcode, dflag);
                exit(0);
        }
+       if (oflag)
+               oflag = init_pfosfp();
        init_addrtoname(localnet, netmask);
 
        if (WFileName) {
@@ -490,8 +492,6 @@ main(int argc, char **argv)
                (void)fflush(stderr);
        }
 
-       if (oflag)
-               oflag = init_pfosfp();
        if (tflag > 0)
                thiszone = gmt2local(0);
 

Reply via email to