summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBryan Steele <brynet@cvs.openbsd.org>2017-09-08 19:10:58 +0000
committerBryan Steele <brynet@cvs.openbsd.org>2017-09-08 19:10:58 +0000
commit073189b1b78265ed7d2747af9706c401a52505ce (patch)
treedebc9666ea1909510337c9e02ee4f67e5752302c
parentfc311b6332b9c4feb00a0dee6adb30a05a3a96cd (diff)
fork+exec model for tcpdump(8); re-exec the privileged child after fork
While tcpdump isn't a daemon in the traditional sense, it's not uncommon for people to have long running sessions. At least on OpenBSD, this is even safe thanks to the existing privsep design by otto@, canacar@ and pledge(2) work done by deraadt. ok deraadt@
-rw-r--r--usr.sbin/tcpdump/privsep.c85
-rw-r--r--usr.sbin/tcpdump/privsep.h1
-rw-r--r--usr.sbin/tcpdump/tcpdump.c6
3 files changed, 66 insertions, 26 deletions
diff --git a/usr.sbin/tcpdump/privsep.c b/usr.sbin/tcpdump/privsep.c
index 46007a65e24..448dc19a180 100644
--- a/usr.sbin/tcpdump/privsep.c
+++ b/usr.sbin/tcpdump/privsep.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: privsep.c,v 1.45 2017/06/14 20:48:54 akfaew Exp $ */
+/* $OpenBSD: privsep.c,v 1.46 2017/09/08 19:10:57 brynet Exp $ */
/*
* Copyright (c) 2003 Can Erkin Acar
@@ -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
diff --git a/usr.sbin/tcpdump/privsep.h b/usr.sbin/tcpdump/privsep.h
index 0e5678e5f68..725d61d0fa8 100644
--- a/usr.sbin/tcpdump/privsep.h
+++ b/usr.sbin/tcpdump/privsep.h
@@ -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 *);
diff --git a/usr.sbin/tcpdump/tcpdump.c b/usr.sbin/tcpdump/tcpdump.c
index 5a5d2c600f9..d90fd45d7b8 100644
--- a/usr.sbin/tcpdump/tcpdump.c
+++ b/usr.sbin/tcpdump/tcpdump.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: tcpdump.c,v 1.79 2016/11/16 13:47:27 reyk Exp $ */
+/* $OpenBSD: tcpdump.c,v 1.80 2017/09/08 19:10:57 brynet Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997
@@ -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");