On Thu, Sep 14, 2017 at 01:40:31AM -0400, Bryan Steele wrote:
> 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.
There's another solution to this coming soon, so I'd like to get my
original diff in, with a comment:
ok?
-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 18 Sep 2017 22:17:00 -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 18 Sep 2017 22:17:00 -0000
@@ -73,12 +73,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 +91,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,8 +102,11 @@ 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 *);
@@ -123,17 +116,47 @@ 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 +182,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,7 +208,7 @@ __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;
@@ -229,6 +230,10 @@ priv_exec(int argc, char *argv[])
nflag++;
break;
+ case 'o':
+ oflag = 1;
+ break;
+
case 'r':
RFileName = optarg;
break;
@@ -273,6 +278,8 @@ priv_exec(int argc, char *argv[])
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 +300,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 +316,16 @@ 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);
+ /* XXX rpath for /etc/{ethers,rpc} */
+ if (pledge("stdio rpath dns bpf", NULL) == -1)
+ err(1, "pledge");
+ } else {
+ drop_privs(0);
+ if (pledge("stdio bpf", NULL) == -1)
+ err(1, "pledge");
+ }
break;
case PRIV_GETHOSTBYADDR:
@@ -333,10 +352,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 +419,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;
@@ -565,55 +598,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 +758,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 +769,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 18 Sep 2017 22:17:00 -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 18 Sep 2017 22:17:00 -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);