>From lessons learned with tcpdump and talking to deraadt@ and bluhm@,
this reworks pflogd to re-exec its unpriv child instead of the priv
parent, my initial understanding of the syslogd privsep design was off
and pflogd doesn't need any such clever approach.
I can also send a diff with my previous attempt reverted if that helps
ease review.
-Bryan.
Index: pflogd.c
===================================================================
RCS file: /cvs/src/sbin/pflogd/pflogd.c,v
retrieving revision 1.57
diff -u -p -u -r1.57 pflogd.c
--- sbin/pflogd/pflogd.c 8 Sep 2017 13:34:29 -0000 1.57
+++ sbin/pflogd/pflogd.c 8 Sep 2017 15:03:21 -0000
@@ -54,7 +54,6 @@ pcap_t *hpcap;
static FILE *dpcap;
int Debug = 0;
-static int privchild = 0;
static int snaplen = DEF_SNAPLEN;
static int cur_snaplen = DEF_SNAPLEN;
@@ -534,13 +533,13 @@ dump_packet(u_char *user, const struct p
int
main(int argc, char **argv)
{
- int ch, np, ret, Xflag = 0;
+ int ch, np, ret, Pflag = 0, Xflag = 0;
pcap_handler phandler = dump_packet;
const char *errstr = NULL;
ret = 0;
- while ((ch = getopt(argc, argv, "Dxd:f:i:P:s:")) != -1) {
+ while ((ch = getopt(argc, argv, "Dxd:f:i:Ps:")) != -1) {
switch (ch) {
case 'D':
Debug = 1;
@@ -556,10 +555,9 @@ main(int argc, char **argv)
case 'i':
interface = optarg;
break;
- case 'P': /* used internally, exec the parent */
- privchild = strtonum(optarg, 2, INT_MAX, &errstr);
- if (errstr)
- errx(1, "priv child %s: %s", errstr, optarg);
+ case 'P': /* used internally, exec the child */
+ if (strcmp("-P", argv[1]) == 0)
+ Pflag = 1;
break;
case 's':
snaplen = strtonum(optarg, 0, PFLOGD_MAXSNAPLEN,
@@ -593,9 +591,12 @@ main(int argc, char **argv)
if (!Debug) {
openlog("pflogd", LOG_PID, LOG_DAEMON);
- if (daemon(0, 0)) {
- logmsg(LOG_WARNING, "Failed to become daemon: %s",
- strerror(errno));
+ if (!Pflag) {
+ if (daemon(0, 0)) {
+ logmsg(LOG_WARNING,
+ "Failed to become daemon: %s",
+ strerror(errno));
+ }
}
}
@@ -611,11 +612,8 @@ main(int argc, char **argv)
argc += optind;
argv -= optind;
- if (privchild > 1)
- priv_exec(privchild, argc, argv);
-
/* Privilege separation begins here */
- priv_init(argc, argv);
+ priv_init(Pflag, argc, argv);
if (pledge("stdio recvfd", NULL) == -1)
err(1, "pledge");
Index: pflogd.h
===================================================================
RCS file: /cvs/src/sbin/pflogd/pflogd.h,v
retrieving revision 1.6
diff -u -p -u -r1.6 pflogd.h
--- sbin/pflogd/pflogd.h 5 Sep 2017 15:41:25 -0000 1.6
+++ sbin/pflogd/pflogd.h 8 Sep 2017 15:03:21 -0000
@@ -34,8 +34,7 @@
void logmsg(int priority, const char *message, ...);
/* Privilege separation */
-void priv_init(int, char **);
-__dead void priv_exec(int, int, char **);
+void priv_init(int, int, char **);
int priv_init_pcap(int);
int priv_set_snaplen(int snaplen);
int priv_open_log(void);
Index: privsep.c
===================================================================
RCS file: /cvs/src/sbin/pflogd/privsep.c,v
retrieving revision 1.29
diff -u -p -u -r1.29 privsep.c
--- sbin/pflogd/privsep.c 6 Sep 2017 12:43:16 -0000 1.29
+++ sbin/pflogd/privsep.c 8 Sep 2017 15:03:21 -0000
@@ -63,26 +63,20 @@ extern pcap_t *hpcap;
/* based on syslogd privsep */
void
-priv_init(int argc, char *argv[])
+priv_init(int Pflag, int argc, char *argv[])
{
- int i, nargc, socks[2];
+ int i, fd = -1, bpfd = -1, nargc, socks[2], cmd;
+ int snaplen, ret, olderrno;
struct passwd *pw;
- char childnum[11], **privargv;
-
- /* Create sockets */
- if (socketpair(AF_LOCAL, SOCK_STREAM, PF_UNSPEC, socks) == -1)
- err(1, "socketpair() failed");
+ char **nargv;
+ unsigned int buflen;
pw = getpwnam("_pflogd");
if (pw == NULL)
errx(1, "unknown user _pflogd");
endpwent();
- child_pid = fork();
- if (child_pid < 0)
- err(1, "fork() failed");
-
- if (!child_pid) {
+ if (Pflag) {
gid_t gidset[1];
/* Child - drop privileges and return */
@@ -98,47 +92,36 @@ priv_init(int argc, char *argv[])
err(1, "setgroups() failed");
if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1)
err(1, "setresuid() failed");
- close(socks[0]);
- priv_fd = socks[1];
+ priv_fd = 3;
return;
}
- close(socks[1]);
-
- if (dup2(socks[0], 3) == -1)
- err(1, "dup2 priv sock failed");
- closefrom(4);
-
- snprintf(childnum, sizeof(childnum), "%d", child_pid);
- if ((privargv = reallocarray(NULL, argc + 3, sizeof(char *))) == NULL)
- err(1, "alloc priv argv failed");
- nargc = 0;
- privargv[nargc++] = argv[0];
- privargv[nargc++] = "-P";
- privargv[nargc++] = childnum;
- 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 child, int argc, char *argv[])
-{
- int i, fd = -1, bpfd = -1, sock, cmd;
- int snaplen, ret, olderrno;
- unsigned int buflen;
-
- if (argc <= 2 || strcmp("-P", argv[1]) != 0)
- errx(1, "exec without priv");
-
- sock = 3;
- closefrom(4);
+ /* Create sockets */
+ if (socketpair(AF_LOCAL, SOCK_STREAM, PF_UNSPEC, socks) == -1)
+ err(1, "socketpair() failed");
- child_pid = child;
+ child_pid = fork();
+ if (child_pid < 0)
+ err(1, "fork() failed");
- for (i = 1; i < _NSIG; i++)
- signal(i, SIG_DFL);
+ if (!child_pid) {
+ close(socks[0]);
+ if (dup2(socks[1], 3) == -1)
+ err(1, "dup2 unpriv sock failed");
+ close(socks[1]);
+ if ((nargv = reallocarray(NULL, argc + 2,
+ sizeof(char *))) == NULL)
+ err(1, "alloc unpriv argv failed");
+ nargc = 0;
+ nargv[nargc++] = argv[0];
+ nargv[nargc++] = "-P";
+ for (i = 1; i < argc; i++)
+ nargv[nargc++] = argv[i];
+ nargv[nargc] = NULL;
+ execvp(nargv[0], nargv);
+ err(1, "exec unpriv '%s' failed", nargv[0]);
+ }
+ close(socks[1]);
/* Father */
/* Pass ALRM/TERM/HUP/INT/QUIT through to child, and accept CHLD */
@@ -157,7 +140,7 @@ BROKEN if (pledge("stdio rpath wpath cpa
#endif
while (1) {
- if (may_read(sock, &cmd, sizeof(int)))
+ if (may_read(socks[0], &cmd, sizeof(int)))
break;
switch (cmd) {
case PRIV_INIT_PCAP:
@@ -169,9 +152,9 @@ BROKEN if (pledge("stdio rpath wpath cpa
_exit(1);
}
buflen = hpcap->bufsize; /* BIOCGBLEN for unpriv proc */
- must_write(sock, &buflen, sizeof(unsigned int));
+ must_write(socks[0], &buflen, sizeof(unsigned int));
fd = pcap_fileno(hpcap);
- send_fd(sock, fd);
+ send_fd(socks[0], fd);
if (fd < 0) {
logmsg(LOG_ERR, "[priv]: Exiting, init failed");
_exit(1);
@@ -181,7 +164,7 @@ BROKEN if (pledge("stdio rpath wpath cpa
case PRIV_SET_SNAPLEN:
logmsg(LOG_DEBUG,
"[priv]: msg PRIV_SET_SNAPLENGTH received");
- must_read(sock, &snaplen, sizeof(int));
+ must_read(socks[0], &snaplen, sizeof(int));
ret = set_snaplen(snaplen);
if (ret) {
@@ -190,7 +173,7 @@ BROKEN if (pledge("stdio rpath wpath cpa
snaplen);
}
- must_write(sock, &ret, sizeof(int));
+ must_write(socks[0], &ret, sizeof(int));
break;
case PRIV_OPEN_LOG:
@@ -205,7 +188,7 @@ BROKEN if (pledge("stdio rpath wpath cpa
O_RDWR|O_CREAT|O_APPEND|O_NONBLOCK|O_NOFOLLOW,
0600);
olderrno = errno;
- send_fd(sock, bpfd);
+ send_fd(socks[0], bpfd);
if (bpfd < 0)
logmsg(LOG_NOTICE,
"[priv]: failed to open %s: %s",
@@ -216,7 +199,7 @@ BROKEN if (pledge("stdio rpath wpath cpa
logmsg(LOG_DEBUG,
"[priv]: msg PRIV_MOVE_LOG received");
ret = move_log(filename);
- must_write(sock, &ret, sizeof(int));
+ must_write(socks[0], &ret, sizeof(int));
break;
default: