xref: /freebsd/contrib/pf/pflogd/privsep.c (revision bbde5c0725a5b34c456b3818cc69d17f22cef9f8)
15ee7cd21SMax Laier /*	$OpenBSD: privsep.c,v 1.16 2006/10/25 20:55:04 moritz Exp $	*/
2abff3868SMax Laier 
3abff3868SMax Laier /*
4abff3868SMax Laier  * Copyright (c) 2003 Can Erkin Acar
5abff3868SMax Laier  * Copyright (c) 2003 Anil Madhavapeddy <anil@recoil.org>
6abff3868SMax Laier  *
7abff3868SMax Laier  * Permission to use, copy, modify, and distribute this software for any
8abff3868SMax Laier  * purpose with or without fee is hereby granted, provided that the above
9abff3868SMax Laier  * copyright notice and this permission notice appear in all copies.
10abff3868SMax Laier  *
11abff3868SMax Laier  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12abff3868SMax Laier  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13abff3868SMax Laier  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14abff3868SMax Laier  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15abff3868SMax Laier  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16abff3868SMax Laier  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17abff3868SMax Laier  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18abff3868SMax Laier  */
1922ac3eadSMax Laier 
205ee7cd21SMax Laier #include <sys/types.h>
21abff3868SMax Laier #include <sys/time.h>
22abff3868SMax Laier #include <sys/socket.h>
23*e0bfbfceSBjoern A. Zeeb #include <sys/ioctl.h>
24abff3868SMax Laier 
25abff3868SMax Laier #include <net/if.h>
26abff3868SMax Laier #include <net/bpf.h>
27abff3868SMax Laier 
28abff3868SMax Laier #include <err.h>
29abff3868SMax Laier #include <errno.h>
30abff3868SMax Laier #include <fcntl.h>
315ee7cd21SMax Laier #include <limits.h>
325ee7cd21SMax Laier #include <pcap.h>
335ee7cd21SMax Laier #include <pcap-int.h>
34abff3868SMax Laier #include <pwd.h>
35abff3868SMax Laier #include <signal.h>
36abff3868SMax Laier #include <stdio.h>
37abff3868SMax Laier #include <stdlib.h>
38abff3868SMax Laier #include <string.h>
39abff3868SMax Laier #include <syslog.h>
40abff3868SMax Laier #include <unistd.h>
41abff3868SMax Laier #include "pflogd.h"
42abff3868SMax Laier 
43abff3868SMax Laier enum cmd_types {
44abff3868SMax Laier 	PRIV_SET_SNAPLEN,	/* set the snaplength */
455ee7cd21SMax Laier 	PRIV_MOVE_LOG,		/* move logfile away */
46abff3868SMax Laier 	PRIV_OPEN_LOG		/* open logfile for appending */
47abff3868SMax Laier };
48abff3868SMax Laier 
49abff3868SMax Laier static int priv_fd = -1;
50abff3868SMax Laier static volatile pid_t child_pid = -1;
51abff3868SMax Laier 
52abff3868SMax Laier volatile sig_atomic_t gotsig_chld = 0;
53abff3868SMax Laier 
54abff3868SMax Laier static void sig_pass_to_chld(int);
55abff3868SMax Laier static void sig_chld(int);
56abff3868SMax Laier static int  may_read(int, void *, size_t);
57abff3868SMax Laier static void must_read(int, void *, size_t);
58abff3868SMax Laier static void must_write(int, void *, size_t);
59abff3868SMax Laier static int  set_snaplen(int snap);
605ee7cd21SMax Laier static int  move_log(const char *name);
61abff3868SMax Laier 
62abff3868SMax Laier extern char *filename;
63abff3868SMax Laier extern pcap_t *hpcap;
64abff3868SMax Laier 
65abff3868SMax Laier /* based on syslogd privsep */
66abff3868SMax Laier int
priv_init(void)67abff3868SMax Laier priv_init(void)
68abff3868SMax Laier {
69abff3868SMax Laier 	int i, fd, socks[2], cmd;
700baf7c86SMax Laier 	int snaplen, ret, olderrno;
71abff3868SMax Laier 	struct passwd *pw;
72abff3868SMax Laier 
7322ac3eadSMax Laier #ifdef __FreeBSD__
7422ac3eadSMax Laier 	for (i = 1; i < NSIG; i++)
7522ac3eadSMax Laier #else
76abff3868SMax Laier 	for (i = 1; i < _NSIG; i++)
7722ac3eadSMax Laier #endif
78abff3868SMax Laier 		signal(i, SIG_DFL);
79abff3868SMax Laier 
80abff3868SMax Laier 	/* Create sockets */
81abff3868SMax Laier 	if (socketpair(AF_LOCAL, SOCK_STREAM, PF_UNSPEC, socks) == -1)
82abff3868SMax Laier 		err(1, "socketpair() failed");
83abff3868SMax Laier 
84abff3868SMax Laier 	pw = getpwnam("_pflogd");
85abff3868SMax Laier 	if (pw == NULL)
86abff3868SMax Laier 		errx(1, "unknown user _pflogd");
87abff3868SMax Laier 	endpwent();
88abff3868SMax Laier 
89abff3868SMax Laier 	child_pid = fork();
90abff3868SMax Laier 	if (child_pid < 0)
91abff3868SMax Laier 		err(1, "fork() failed");
92abff3868SMax Laier 
93abff3868SMax Laier 	if (!child_pid) {
94abff3868SMax Laier 		gid_t gidset[1];
95abff3868SMax Laier 
96abff3868SMax Laier 		/* Child - drop privileges and return */
97abff3868SMax Laier 		if (chroot(pw->pw_dir) != 0)
98abff3868SMax Laier 			err(1, "unable to chroot");
99abff3868SMax Laier 		if (chdir("/") != 0)
100abff3868SMax Laier 			err(1, "unable to chdir");
101abff3868SMax Laier 
102abff3868SMax Laier 		gidset[0] = pw->pw_gid;
1035ee7cd21SMax Laier 		if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1)
1045ee7cd21SMax Laier 			err(1, "setresgid() failed");
105abff3868SMax Laier 		if (setgroups(1, gidset) == -1)
106abff3868SMax Laier 			err(1, "setgroups() failed");
1075ee7cd21SMax Laier 		if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1)
1085ee7cd21SMax Laier 			err(1, "setresuid() failed");
109abff3868SMax Laier 		close(socks[0]);
110abff3868SMax Laier 		priv_fd = socks[1];
111abff3868SMax Laier 		return 0;
112abff3868SMax Laier 	}
113abff3868SMax Laier 
114abff3868SMax Laier 	/* Father */
1150baf7c86SMax Laier 	/* Pass ALRM/TERM/HUP/INT/QUIT through to child, and accept CHLD */
116abff3868SMax Laier 	signal(SIGALRM, sig_pass_to_chld);
117abff3868SMax Laier 	signal(SIGTERM, sig_pass_to_chld);
118abff3868SMax Laier 	signal(SIGHUP,  sig_pass_to_chld);
1190baf7c86SMax Laier 	signal(SIGINT,  sig_pass_to_chld);
1200baf7c86SMax Laier 	signal(SIGQUIT,  sig_pass_to_chld);
121abff3868SMax Laier 	signal(SIGCHLD, sig_chld);
122abff3868SMax Laier 
123abff3868SMax Laier 	setproctitle("[priv]");
124abff3868SMax Laier 	close(socks[1]);
125abff3868SMax Laier 
126abff3868SMax Laier 	while (!gotsig_chld) {
127abff3868SMax Laier 		if (may_read(socks[0], &cmd, sizeof(int)))
128abff3868SMax Laier 			break;
129abff3868SMax Laier 		switch (cmd) {
130abff3868SMax Laier 		case PRIV_SET_SNAPLEN:
131abff3868SMax Laier 			logmsg(LOG_DEBUG,
132abff3868SMax Laier 			    "[priv]: msg PRIV_SET_SNAPLENGTH received");
133abff3868SMax Laier 			must_read(socks[0], &snaplen, sizeof(int));
134abff3868SMax Laier 
135abff3868SMax Laier 			ret = set_snaplen(snaplen);
136abff3868SMax Laier 			if (ret) {
137abff3868SMax Laier 				logmsg(LOG_NOTICE,
138abff3868SMax Laier 				   "[priv]: set_snaplen failed for snaplen %d",
139abff3868SMax Laier 				   snaplen);
140abff3868SMax Laier 			}
141abff3868SMax Laier 
142abff3868SMax Laier 			must_write(socks[0], &ret, sizeof(int));
143abff3868SMax Laier 			break;
144abff3868SMax Laier 
145abff3868SMax Laier 		case PRIV_OPEN_LOG:
146abff3868SMax Laier 			logmsg(LOG_DEBUG,
147abff3868SMax Laier 			    "[priv]: msg PRIV_OPEN_LOG received");
148abff3868SMax Laier 			/* create or append logs but do not follow symlinks */
149abff3868SMax Laier 			fd = open(filename,
150abff3868SMax Laier 			    O_RDWR|O_CREAT|O_APPEND|O_NONBLOCK|O_NOFOLLOW,
151abff3868SMax Laier 			    0600);
1520baf7c86SMax Laier 			olderrno = errno;
1530baf7c86SMax Laier 			send_fd(socks[0], fd);
154abff3868SMax Laier 			if (fd < 0)
155abff3868SMax Laier 				logmsg(LOG_NOTICE,
156abff3868SMax Laier 				    "[priv]: failed to open %s: %s",
1570baf7c86SMax Laier 				    filename, strerror(olderrno));
1580baf7c86SMax Laier 			else
159abff3868SMax Laier 				close(fd);
160abff3868SMax Laier 			break;
161abff3868SMax Laier 
1625ee7cd21SMax Laier 		case PRIV_MOVE_LOG:
1635ee7cd21SMax Laier 			logmsg(LOG_DEBUG,
1645ee7cd21SMax Laier 			    "[priv]: msg PRIV_MOVE_LOG received");
1655ee7cd21SMax Laier 			ret = move_log(filename);
1665ee7cd21SMax Laier 			must_write(socks[0], &ret, sizeof(int));
1675ee7cd21SMax Laier 			break;
1685ee7cd21SMax Laier 
169abff3868SMax Laier 		default:
170abff3868SMax Laier 			logmsg(LOG_ERR, "[priv]: unknown command %d", cmd);
171abff3868SMax Laier 			_exit(1);
172abff3868SMax Laier 			/* NOTREACHED */
173abff3868SMax Laier 		}
174abff3868SMax Laier 	}
175abff3868SMax Laier 
176abff3868SMax Laier 	_exit(1);
177abff3868SMax Laier }
178abff3868SMax Laier 
179abff3868SMax Laier /* this is called from parent */
180abff3868SMax Laier static int
set_snaplen(int snap)181abff3868SMax Laier set_snaplen(int snap)
182abff3868SMax Laier {
183abff3868SMax Laier 	if (hpcap == NULL)
184abff3868SMax Laier 		return (1);
185abff3868SMax Laier 
186abff3868SMax Laier 	hpcap->snapshot = snap;
187abff3868SMax Laier 	set_pcap_filter();
188abff3868SMax Laier 
189abff3868SMax Laier 	return 0;
190abff3868SMax Laier }
191abff3868SMax Laier 
1925ee7cd21SMax Laier static int
move_log(const char * name)1935ee7cd21SMax Laier move_log(const char *name)
1945ee7cd21SMax Laier {
1955ee7cd21SMax Laier 	char ren[PATH_MAX];
1965ee7cd21SMax Laier 	int len;
1975ee7cd21SMax Laier 
1985ee7cd21SMax Laier 	for (;;) {
1995ee7cd21SMax Laier 		int fd;
2005ee7cd21SMax Laier 
2015ee7cd21SMax Laier 		len = snprintf(ren, sizeof(ren), "%s.bad.%08x",
2025ee7cd21SMax Laier 		    name, arc4random());
2035ee7cd21SMax Laier 		if (len >= sizeof(ren)) {
2045ee7cd21SMax Laier 			logmsg(LOG_ERR, "[priv] new name too long");
2055ee7cd21SMax Laier 			return (1);
2065ee7cd21SMax Laier 		}
2075ee7cd21SMax Laier 
2085ee7cd21SMax Laier 		/* lock destinanion */
2095ee7cd21SMax Laier 		fd = open(ren, O_CREAT|O_EXCL, 0);
2105ee7cd21SMax Laier 		if (fd >= 0) {
2115ee7cd21SMax Laier 			close(fd);
2125ee7cd21SMax Laier 			break;
2135ee7cd21SMax Laier 		}
2145ee7cd21SMax Laier 		/* if file exists, try another name */
2155ee7cd21SMax Laier 		if (errno != EEXIST && errno != EINTR) {
2165ee7cd21SMax Laier 			logmsg(LOG_ERR, "[priv] failed to create new name: %s",
2175ee7cd21SMax Laier 			    strerror(errno));
2185ee7cd21SMax Laier 			return (1);
2195ee7cd21SMax Laier 		}
2205ee7cd21SMax Laier 	}
2215ee7cd21SMax Laier 
2225ee7cd21SMax Laier 	if (rename(name, ren)) {
2235ee7cd21SMax Laier 		logmsg(LOG_ERR, "[priv] failed to rename %s to %s: %s",
2245ee7cd21SMax Laier 		    name, ren, strerror(errno));
2255ee7cd21SMax Laier 		return (1);
2265ee7cd21SMax Laier 	}
2275ee7cd21SMax Laier 
2285ee7cd21SMax Laier 	logmsg(LOG_NOTICE,
2295ee7cd21SMax Laier 	       "[priv]: log file %s moved to %s", name, ren);
2305ee7cd21SMax Laier 
2315ee7cd21SMax Laier 	return (0);
2325ee7cd21SMax Laier }
233abff3868SMax Laier 
234abff3868SMax Laier /*
235abff3868SMax Laier  * send the snaplength to privileged process
236abff3868SMax Laier  */
237abff3868SMax Laier int
priv_set_snaplen(int snaplen)238abff3868SMax Laier priv_set_snaplen(int snaplen)
239abff3868SMax Laier {
240abff3868SMax Laier 	int cmd, ret;
241abff3868SMax Laier 
242abff3868SMax Laier 	if (priv_fd < 0)
243abff3868SMax Laier 		errx(1, "%s: called from privileged portion", __func__);
244abff3868SMax Laier 
245abff3868SMax Laier 	cmd = PRIV_SET_SNAPLEN;
246abff3868SMax Laier 
247abff3868SMax Laier 	must_write(priv_fd, &cmd, sizeof(int));
248abff3868SMax Laier 	must_write(priv_fd, &snaplen, sizeof(int));
249abff3868SMax Laier 
250abff3868SMax Laier 	must_read(priv_fd, &ret, sizeof(int));
251abff3868SMax Laier 
252abff3868SMax Laier 	/* also set hpcap->snapshot in child */
253abff3868SMax Laier 	if (ret == 0)
254abff3868SMax Laier 		hpcap->snapshot = snaplen;
255abff3868SMax Laier 
256abff3868SMax Laier 	return (ret);
257abff3868SMax Laier }
258abff3868SMax Laier 
259abff3868SMax Laier /* Open log-file */
260abff3868SMax Laier int
priv_open_log(void)261abff3868SMax Laier priv_open_log(void)
262abff3868SMax Laier {
263abff3868SMax Laier 	int cmd, fd;
264abff3868SMax Laier 
265abff3868SMax Laier 	if (priv_fd < 0)
2660baf7c86SMax Laier 		errx(1, "%s: called from privileged portion", __func__);
267abff3868SMax Laier 
268abff3868SMax Laier 	cmd = PRIV_OPEN_LOG;
269abff3868SMax Laier 	must_write(priv_fd, &cmd, sizeof(int));
270abff3868SMax Laier 	fd = receive_fd(priv_fd);
271abff3868SMax Laier 
272abff3868SMax Laier 	return (fd);
273abff3868SMax Laier }
2745ee7cd21SMax Laier /* Move-away and reopen log-file */
2755ee7cd21SMax Laier int
priv_move_log(void)2765ee7cd21SMax Laier priv_move_log(void)
2775ee7cd21SMax Laier {
2785ee7cd21SMax Laier 	int cmd, ret;
2795ee7cd21SMax Laier 
2805ee7cd21SMax Laier 	if (priv_fd < 0)
2815ee7cd21SMax Laier 		errx(1, "%s: called from privileged portion\n", __func__);
2825ee7cd21SMax Laier 
2835ee7cd21SMax Laier 	cmd = PRIV_MOVE_LOG;
2845ee7cd21SMax Laier 	must_write(priv_fd, &cmd, sizeof(int));
2855ee7cd21SMax Laier 	must_read(priv_fd, &ret, sizeof(int));
2865ee7cd21SMax Laier 
2875ee7cd21SMax Laier 	return (ret);
2885ee7cd21SMax Laier }
289abff3868SMax Laier 
290abff3868SMax Laier /* If priv parent gets a TERM or HUP, pass it through to child instead */
291abff3868SMax Laier static void
sig_pass_to_chld(int sig)292abff3868SMax Laier sig_pass_to_chld(int sig)
293abff3868SMax Laier {
294abff3868SMax Laier 	int oerrno = errno;
295abff3868SMax Laier 
296abff3868SMax Laier 	if (child_pid != -1)
297abff3868SMax Laier 		kill(child_pid, sig);
298abff3868SMax Laier 	errno = oerrno;
299abff3868SMax Laier }
300abff3868SMax Laier 
301abff3868SMax Laier /* if parent gets a SIGCHLD, it will exit */
302abff3868SMax Laier static void
sig_chld(int sig)303abff3868SMax Laier sig_chld(int sig)
304abff3868SMax Laier {
305abff3868SMax Laier 	gotsig_chld = 1;
306abff3868SMax Laier }
307abff3868SMax Laier 
308abff3868SMax Laier /* Read all data or return 1 for error.  */
309abff3868SMax Laier static int
may_read(int fd,void * buf,size_t n)310abff3868SMax Laier may_read(int fd, void *buf, size_t n)
311abff3868SMax Laier {
312abff3868SMax Laier 	char *s = buf;
313abff3868SMax Laier 	ssize_t res, pos = 0;
314abff3868SMax Laier 
315abff3868SMax Laier 	while (n > pos) {
316abff3868SMax Laier 		res = read(fd, s + pos, n - pos);
317abff3868SMax Laier 		switch (res) {
318abff3868SMax Laier 		case -1:
319abff3868SMax Laier 			if (errno == EINTR || errno == EAGAIN)
320abff3868SMax Laier 				continue;
321abff3868SMax Laier 		case 0:
322abff3868SMax Laier 			return (1);
323abff3868SMax Laier 		default:
324abff3868SMax Laier 			pos += res;
325abff3868SMax Laier 		}
326abff3868SMax Laier 	}
327abff3868SMax Laier 	return (0);
328abff3868SMax Laier }
329abff3868SMax Laier 
330abff3868SMax Laier /* Read data with the assertion that it all must come through, or
331abff3868SMax Laier  * else abort the process.  Based on atomicio() from openssh. */
332abff3868SMax Laier static void
must_read(int fd,void * buf,size_t n)333abff3868SMax Laier must_read(int fd, void *buf, size_t n)
334abff3868SMax Laier {
335abff3868SMax Laier 	char *s = buf;
336abff3868SMax Laier 	ssize_t res, pos = 0;
337abff3868SMax Laier 
338abff3868SMax Laier 	while (n > pos) {
339abff3868SMax Laier 		res = read(fd, s + pos, n - pos);
340abff3868SMax Laier 		switch (res) {
341abff3868SMax Laier 		case -1:
342abff3868SMax Laier 			if (errno == EINTR || errno == EAGAIN)
343abff3868SMax Laier 				continue;
344abff3868SMax Laier 		case 0:
345abff3868SMax Laier 			_exit(0);
346abff3868SMax Laier 		default:
347abff3868SMax Laier 			pos += res;
348abff3868SMax Laier 		}
349abff3868SMax Laier 	}
350abff3868SMax Laier }
351abff3868SMax Laier 
352abff3868SMax Laier /* Write data with the assertion that it all has to be written, or
353abff3868SMax Laier  * else abort the process.  Based on atomicio() from openssh. */
354abff3868SMax Laier static void
must_write(int fd,void * buf,size_t n)355abff3868SMax Laier must_write(int fd, void *buf, size_t n)
356abff3868SMax Laier {
357abff3868SMax Laier 	char *s = buf;
358abff3868SMax Laier 	ssize_t res, pos = 0;
359abff3868SMax Laier 
360abff3868SMax Laier 	while (n > pos) {
361abff3868SMax Laier 		res = write(fd, s + pos, n - pos);
362abff3868SMax Laier 		switch (res) {
363abff3868SMax Laier 		case -1:
364abff3868SMax Laier 			if (errno == EINTR || errno == EAGAIN)
365abff3868SMax Laier 				continue;
366abff3868SMax Laier 		case 0:
367abff3868SMax Laier 			_exit(0);
368abff3868SMax Laier 		default:
369abff3868SMax Laier 			pos += res;
370abff3868SMax Laier 		}
371abff3868SMax Laier 	}
372abff3868SMax Laier }
373