On Thu, Sep 07, 2017 at 12:27:18AM -0400, Bryan Steele wrote:
> Hi,
>
> This turned out easier then pflogd thanks to the existing privsep design
> work done by [otto@] and canacar@ many years ago. While tcpdump isn't a
> daemon in the traditional sense, it isn't so uncommon for people to have
> long running sessions. At least on OpenBSD, this is even safe thanks to
> privsep and very strict pledge(2) promises.
>
> This does shuffle things around a bit internally, but I don't think I've
> totally broken anything.
>
> Seems to work with some light testing, live captures, -w/-r offline pcap
> files.
>
> Comments?
>
> -Bryan.
This undos my reshuffling and just calls execv in the child after fork,
not the parent. This made the the diff smaller..
Suggested by deraadt@
-Bryan.
Index: privsep.c
===================================================================
RCS file: /cvs/src/usr.sbin/tcpdump/privsep.c,v
retrieving revision 1.45
diff -u -p -u -r1.45 privsep.c
--- usr.sbin/tcpdump/privsep.c 14 Jun 2017 20:48:54 -0000 1.45
+++ usr.sbin/tcpdump/privsep.c 7 Sep 2017 12:01:03 -0000
@@ -33,6 +33,7 @@
#include <err.h>
#include <errno.h>
#include <fcntl.h>
+#include <limits.h>
#include <netdb.h>
#include <paths.h>
#include <pwd.h>
@@ -132,13 +133,10 @@ static void logmsg(int, const char *, ..
int
priv_init(int argc, char **argv)
{
- int bpfd = -1;
- int i, socks[2], cmd, nflag = 0;
+ int i, nargc, socks[2];
struct passwd *pw;
- char *cmdbuf, *infile = NULL;
- char *RFileName = NULL;
- char *WFileName = NULL;
sigset_t allsigs, oset;
+ char **privargv;
closefrom(STDERR_FILENO + 1);
for (i = 1; i < _NSIG; i++)
@@ -188,14 +186,45 @@ priv_init(int argc, char **argv)
return (0);
}
+ close(socks[1]);
+
+ if (dup2(socks[0], 3) == -1)
+ err(1, "dup2 priv sock failed");
+ closefrom(4);
+
+ if ((privargv = reallocarray(NULL, argc + 2, sizeof(char *))) == NULL)
+ err(1, "alloc priv argv failed");
+ nargc = 0;
+ privargv[nargc++] = argv[0];
+ privargv[nargc++] = "-P";
+ 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 argc, char *argv[])
+{
+ int bpfd = -1;
+ int i, sock, cmd, nflag = 0, Pflag = 0;
+ char *cmdbuf, *infile = NULL;
+ char *RFileName = NULL;
+ char *WFileName = NULL;
+
+ sock = 3;
+
+ closefrom(4);
+ for (i = 1; i < _NSIG; i++)
+ signal(i, SIG_DFL);
- sigprocmask(SIG_SETMASK, &oset, NULL);
signal(SIGINT, SIG_IGN);
/* parse the arguments for required options */
opterr = 0;
while ((i = getopt(argc, argv,
- "ac:D:deE:fF:i:lLnNOopqr:s:StT:vw:xXy:Y")) != -1) {
+ "ac:D:deE:fF:i:lLnNOopPqr:s:StT:vw:xXy:Y")) != -1) {
switch (i) {
case 'n':
nflag++;
@@ -213,12 +242,19 @@ priv_init(int argc, char **argv)
infile = optarg;
break;
+ case 'P':
+ Pflag = 1;
+ break;
+
default:
/* nothing */
break;
}
}
+ if (!Pflag)
+ errx(1, "exec without priv");
+
if (RFileName != NULL) {
if (strcmp(RFileName, "-") != 0)
allowed_ext[STATE_INIT] |= ALLOW(PRIV_OPEN_DUMP);
@@ -245,31 +281,30 @@ priv_init(int argc, char **argv)
cmdbuf = copy_argv(&argv[optind]);
setproctitle("[priv]");
- close(socks[1]);
for (;;) {
- if (may_read(socks[0], &cmd, sizeof(int)))
+ if (may_read(sock, &cmd, sizeof(int)))
break;
switch (cmd) {
case PRIV_OPEN_BPF:
test_state(cmd, STATE_BPF);
- impl_open_bpf(socks[0], &bpfd);
+ impl_open_bpf(sock, &bpfd);
break;
case PRIV_OPEN_DUMP:
test_state(cmd, STATE_BPF);
- impl_open_dump(socks[0], RFileName);
+ impl_open_dump(sock, RFileName);
break;
case PRIV_OPEN_OUTPUT:
test_state(cmd, STATE_RUN);
- impl_open_output(socks[0], WFileName);
+ impl_open_output(sock, WFileName);
break;
case PRIV_SETFILTER:
test_state(cmd, STATE_FILTER);
- impl_setfilter(socks[0], cmdbuf, &bpfd);
+ impl_setfilter(sock, cmdbuf, &bpfd);
break;
case PRIV_INIT_DONE:
test_state(cmd, STATE_RUN);
- impl_init_done(socks[0], &bpfd);
+ impl_init_done(sock, &bpfd);
if (pledge("stdio rpath inet unix dns recvfd bpf",
NULL) == -1)
err(1, "pledge");
@@ -277,45 +312,45 @@ priv_init(int argc, char **argv)
break;
case PRIV_GETHOSTBYADDR:
test_state(cmd, STATE_RUN);
- impl_gethostbyaddr(socks[0]);
+ impl_gethostbyaddr(sock);
break;
case PRIV_ETHER_NTOHOST:
test_state(cmd, cur_state);
- impl_ether_ntohost(socks[0]);
+ impl_ether_ntohost(sock);
break;
case PRIV_GETRPCBYNUMBER:
test_state(cmd, STATE_RUN);
- impl_getrpcbynumber(socks[0]);
+ impl_getrpcbynumber(sock);
break;
case PRIV_GETSERVENTRIES:
test_state(cmd, STATE_FILTER);
- impl_getserventries(socks[0]);
+ impl_getserventries(sock);
break;
case PRIV_GETPROTOENTRIES:
test_state(cmd, STATE_FILTER);
- impl_getprotoentries(socks[0]);
+ impl_getprotoentries(sock);
break;
case PRIV_LOCALTIME:
test_state(cmd, STATE_RUN);
- impl_localtime(socks[0]);
+ impl_localtime(sock);
break;
case PRIV_GETLINES:
test_state(cmd, STATE_RUN);
- impl_getlines(socks[0]);
+ impl_getlines(sock);
break;
case PRIV_PCAP_STATS:
test_state(cmd, STATE_RUN);
- impl_pcap_stats(socks[0], &bpfd);
+ impl_pcap_stats(sock, &bpfd);
break;
default:
logmsg(LOG_ERR, "[priv]: unknown command %d", cmd);
- _exit(1);
+ exit(1);
/* NOTREACHED */
}
}
/* NOTREACHED */
- _exit(0);
+ exit(0);
}
static void
Index: privsep.h
===================================================================
RCS file: /cvs/src/usr.sbin/tcpdump/privsep.h,v
retrieving revision 1.9
diff -u -p -u -r1.9 privsep.h
--- usr.sbin/tcpdump/privsep.h 14 Jun 2017 20:48:54 -0000 1.9
+++ usr.sbin/tcpdump/privsep.h 7 Sep 2017 12:01:03 -0000
@@ -44,6 +44,7 @@ struct ether_addr;
/* Privilege separation */
int priv_init(int, char **);
+__dead void priv_exec(int, char **);
void priv_init_done(void);
int setfilter(int, int, char *);
Index: tcpdump.c
===================================================================
RCS file: /cvs/src/usr.sbin/tcpdump/tcpdump.c,v
retrieving revision 1.79
diff -u -p -u -r1.79 tcpdump.c
--- usr.sbin/tcpdump/tcpdump.c 16 Nov 2016 13:47:27 -0000 1.79
+++ usr.sbin/tcpdump/tcpdump.c 7 Sep 2017 12:01:03 -0000
@@ -218,6 +218,10 @@ main(int argc, char **argv)
else
program_name = argv[0];
+ /* '-P' used internally, exec privileged portion */
+ if (argc >= 2 && strcmp("-P", argv[1]) == 0)
+ priv_exec(argc, argv);
+
if (priv_init(argc, argv))
error("Failed to setup privsep");