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, ðer, sizeof(ether)); - if (ether_ntohost(hostname, ðer) == -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, ðer, 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 < } -/* 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);