xref: /freebsd/contrib/pf/pflogd/privsep.c (revision 0baf7c8675952c843665097288cc573a11b143b9)
10baf7c86SMax Laier /*	$OpenBSD: privsep.c,v 1.13 2004/12/22 09:21:02 otto 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 
2022ac3eadSMax Laier #include <sys/cdefs.h>
2122ac3eadSMax Laier __FBSDID("$FreeBSD$");
2222ac3eadSMax Laier 
2322ac3eadSMax Laier #include <sys/param.h>
24abff3868SMax Laier #include <sys/time.h>
25abff3868SMax Laier #include <sys/socket.h>
26abff3868SMax Laier 
27abff3868SMax Laier #include <net/if.h>
28abff3868SMax Laier #include <net/bpf.h>
29abff3868SMax Laier 
30abff3868SMax Laier #include <err.h>
31abff3868SMax Laier #include <errno.h>
32abff3868SMax Laier #include <fcntl.h>
33abff3868SMax Laier #include <pwd.h>
34abff3868SMax Laier #include <signal.h>
35abff3868SMax Laier #include <stdio.h>
36abff3868SMax Laier #include <stdlib.h>
37abff3868SMax Laier #include <string.h>
3822ac3eadSMax Laier #include <pcap.h>
3922ac3eadSMax Laier #include <pcap-int.h>
40abff3868SMax Laier #include <syslog.h>
41abff3868SMax Laier #include <unistd.h>
42abff3868SMax Laier #include "pflogd.h"
43abff3868SMax Laier 
44abff3868SMax Laier enum cmd_types {
45abff3868SMax Laier 	PRIV_SET_SNAPLEN,	/* set the snaplength */
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);
60abff3868SMax Laier 
61abff3868SMax Laier /* bpf filter expression common to parent and child */
62abff3868SMax Laier extern char *filter;
63abff3868SMax Laier extern char *errbuf;
64abff3868SMax Laier extern char *filename;
65abff3868SMax Laier extern pcap_t *hpcap;
66abff3868SMax Laier 
67abff3868SMax Laier /* based on syslogd privsep */
68abff3868SMax Laier int
69abff3868SMax Laier priv_init(void)
70abff3868SMax Laier {
71abff3868SMax Laier 	int i, fd, socks[2], cmd;
720baf7c86SMax Laier 	int snaplen, ret, olderrno;
73abff3868SMax Laier 	struct passwd *pw;
74abff3868SMax Laier 
7522ac3eadSMax Laier #ifdef __FreeBSD__
7622ac3eadSMax Laier 	for (i = 1; i < NSIG; i++)
7722ac3eadSMax Laier #else
78abff3868SMax Laier 	for (i = 1; i < _NSIG; i++)
7922ac3eadSMax Laier #endif
80abff3868SMax Laier 		signal(i, SIG_DFL);
81abff3868SMax Laier 
82abff3868SMax Laier 	/* Create sockets */
83abff3868SMax Laier 	if (socketpair(AF_LOCAL, SOCK_STREAM, PF_UNSPEC, socks) == -1)
84abff3868SMax Laier 		err(1, "socketpair() failed");
85abff3868SMax Laier 
86abff3868SMax Laier 	pw = getpwnam("_pflogd");
87abff3868SMax Laier 	if (pw == NULL)
88abff3868SMax Laier 		errx(1, "unknown user _pflogd");
89abff3868SMax Laier 	endpwent();
90abff3868SMax Laier 
91abff3868SMax Laier 	child_pid = fork();
92abff3868SMax Laier 	if (child_pid < 0)
93abff3868SMax Laier 		err(1, "fork() failed");
94abff3868SMax Laier 
95abff3868SMax Laier 	if (!child_pid) {
96abff3868SMax Laier 		gid_t gidset[1];
97abff3868SMax Laier 
98abff3868SMax Laier 		/* Child - drop privileges and return */
99abff3868SMax Laier 		if (chroot(pw->pw_dir) != 0)
100abff3868SMax Laier 			err(1, "unable to chroot");
101abff3868SMax Laier 		if (chdir("/") != 0)
102abff3868SMax Laier 			err(1, "unable to chdir");
103abff3868SMax Laier 
104abff3868SMax Laier 		gidset[0] = pw->pw_gid;
105abff3868SMax Laier 		if (setgroups(1, gidset) == -1)
106abff3868SMax Laier 			err(1, "setgroups() failed");
107abff3868SMax Laier 		if (setegid(pw->pw_gid) == -1)
108abff3868SMax Laier 			err(1, "setegid() failed");
109abff3868SMax Laier 		if (setgid(pw->pw_gid) == -1)
110abff3868SMax Laier 			err(1, "setgid() failed");
111abff3868SMax Laier 		if (seteuid(pw->pw_uid) == -1)
112abff3868SMax Laier 			err(1, "seteuid() failed");
113abff3868SMax Laier 		if (setuid(pw->pw_uid) == -1)
114abff3868SMax Laier 			err(1, "setuid() failed");
115abff3868SMax Laier 		close(socks[0]);
116abff3868SMax Laier 		priv_fd = socks[1];
117abff3868SMax Laier 		return 0;
118abff3868SMax Laier 	}
119abff3868SMax Laier 
120abff3868SMax Laier 	/* Father */
1210baf7c86SMax Laier 	/* Pass ALRM/TERM/HUP/INT/QUIT through to child, and accept CHLD */
122abff3868SMax Laier 	signal(SIGALRM, sig_pass_to_chld);
123abff3868SMax Laier 	signal(SIGTERM, sig_pass_to_chld);
124abff3868SMax Laier 	signal(SIGHUP,  sig_pass_to_chld);
1250baf7c86SMax Laier 	signal(SIGINT,  sig_pass_to_chld);
1260baf7c86SMax Laier 	signal(SIGQUIT,  sig_pass_to_chld);
127abff3868SMax Laier 	signal(SIGCHLD, sig_chld);
128abff3868SMax Laier 
129abff3868SMax Laier 	setproctitle("[priv]");
130abff3868SMax Laier 	close(socks[1]);
131abff3868SMax Laier 
132abff3868SMax Laier 	while (!gotsig_chld) {
133abff3868SMax Laier 		if (may_read(socks[0], &cmd, sizeof(int)))
134abff3868SMax Laier 			break;
135abff3868SMax Laier 		switch (cmd) {
136abff3868SMax Laier 		case PRIV_SET_SNAPLEN:
137abff3868SMax Laier 			logmsg(LOG_DEBUG,
138abff3868SMax Laier 			    "[priv]: msg PRIV_SET_SNAPLENGTH received");
139abff3868SMax Laier 			must_read(socks[0], &snaplen, sizeof(int));
140abff3868SMax Laier 
141abff3868SMax Laier 			ret = set_snaplen(snaplen);
142abff3868SMax Laier 			if (ret) {
143abff3868SMax Laier 				logmsg(LOG_NOTICE,
144abff3868SMax Laier 				   "[priv]: set_snaplen failed for snaplen %d",
145abff3868SMax Laier 				   snaplen);
146abff3868SMax Laier 			}
147abff3868SMax Laier 
148abff3868SMax Laier 			must_write(socks[0], &ret, sizeof(int));
149abff3868SMax Laier 			break;
150abff3868SMax Laier 
151abff3868SMax Laier 		case PRIV_OPEN_LOG:
152abff3868SMax Laier 			logmsg(LOG_DEBUG,
153abff3868SMax Laier 			    "[priv]: msg PRIV_OPEN_LOG received");
154abff3868SMax Laier 			/* create or append logs but do not follow symlinks */
155abff3868SMax Laier 			fd = open(filename,
156abff3868SMax Laier 			    O_RDWR|O_CREAT|O_APPEND|O_NONBLOCK|O_NOFOLLOW,
157abff3868SMax Laier 			    0600);
1580baf7c86SMax Laier 			olderrno = errno;
1590baf7c86SMax Laier 			send_fd(socks[0], fd);
160abff3868SMax Laier 			if (fd < 0)
161abff3868SMax Laier 				logmsg(LOG_NOTICE,
162abff3868SMax Laier 				    "[priv]: failed to open %s: %s",
1630baf7c86SMax Laier 				    filename, strerror(olderrno));
1640baf7c86SMax Laier 			else
165abff3868SMax Laier 				close(fd);
166abff3868SMax Laier 			break;
167abff3868SMax Laier 
168abff3868SMax Laier 		default:
169abff3868SMax Laier 			logmsg(LOG_ERR, "[priv]: unknown command %d", cmd);
170abff3868SMax Laier 			_exit(1);
171abff3868SMax Laier 			/* NOTREACHED */
172abff3868SMax Laier 		}
173abff3868SMax Laier 	}
174abff3868SMax Laier 
175abff3868SMax Laier 	_exit(1);
176abff3868SMax Laier }
177abff3868SMax Laier 
178abff3868SMax Laier /* this is called from parent */
179abff3868SMax Laier static int
180abff3868SMax Laier set_snaplen(int snap)
181abff3868SMax Laier {
182abff3868SMax Laier 	if (hpcap == NULL)
183abff3868SMax Laier 		return (1);
184abff3868SMax Laier 
185abff3868SMax Laier 	hpcap->snapshot = snap;
186abff3868SMax Laier 	set_pcap_filter();
187abff3868SMax Laier 
188abff3868SMax Laier 	return 0;
189abff3868SMax Laier }
190abff3868SMax Laier 
191abff3868SMax Laier 
192abff3868SMax Laier /*
193abff3868SMax Laier  * send the snaplength to privileged process
194abff3868SMax Laier  */
195abff3868SMax Laier int
196abff3868SMax Laier priv_set_snaplen(int snaplen)
197abff3868SMax Laier {
198abff3868SMax Laier 	int cmd, ret;
199abff3868SMax Laier 
200abff3868SMax Laier 	if (priv_fd < 0)
201abff3868SMax Laier 		errx(1, "%s: called from privileged portion", __func__);
202abff3868SMax Laier 
203abff3868SMax Laier 	cmd = PRIV_SET_SNAPLEN;
204abff3868SMax Laier 
205abff3868SMax Laier 	must_write(priv_fd, &cmd, sizeof(int));
206abff3868SMax Laier 	must_write(priv_fd, &snaplen, sizeof(int));
207abff3868SMax Laier 
208abff3868SMax Laier 	must_read(priv_fd, &ret, sizeof(int));
209abff3868SMax Laier 
210abff3868SMax Laier 	/* also set hpcap->snapshot in child */
211abff3868SMax Laier 	if (ret == 0)
212abff3868SMax Laier 		hpcap->snapshot = snaplen;
213abff3868SMax Laier 
214abff3868SMax Laier 	return (ret);
215abff3868SMax Laier }
216abff3868SMax Laier 
217abff3868SMax Laier /* Open log-file */
218abff3868SMax Laier int
219abff3868SMax Laier priv_open_log(void)
220abff3868SMax Laier {
221abff3868SMax Laier 	int cmd, fd;
222abff3868SMax Laier 
223abff3868SMax Laier 	if (priv_fd < 0)
2240baf7c86SMax Laier 		errx(1, "%s: called from privileged portion", __func__);
225abff3868SMax Laier 
226abff3868SMax Laier 	cmd = PRIV_OPEN_LOG;
227abff3868SMax Laier 	must_write(priv_fd, &cmd, sizeof(int));
228abff3868SMax Laier 	fd = receive_fd(priv_fd);
229abff3868SMax Laier 
230abff3868SMax Laier 	return (fd);
231abff3868SMax Laier }
232abff3868SMax Laier 
233abff3868SMax Laier /* If priv parent gets a TERM or HUP, pass it through to child instead */
234abff3868SMax Laier static void
235abff3868SMax Laier sig_pass_to_chld(int sig)
236abff3868SMax Laier {
237abff3868SMax Laier 	int oerrno = errno;
238abff3868SMax Laier 
239abff3868SMax Laier 	if (child_pid != -1)
240abff3868SMax Laier 		kill(child_pid, sig);
241abff3868SMax Laier 	errno = oerrno;
242abff3868SMax Laier }
243abff3868SMax Laier 
244abff3868SMax Laier /* if parent gets a SIGCHLD, it will exit */
245abff3868SMax Laier static void
246abff3868SMax Laier sig_chld(int sig)
247abff3868SMax Laier {
248abff3868SMax Laier 	gotsig_chld = 1;
249abff3868SMax Laier }
250abff3868SMax Laier 
251abff3868SMax Laier /* Read all data or return 1 for error.  */
252abff3868SMax Laier static int
253abff3868SMax Laier may_read(int fd, void *buf, size_t n)
254abff3868SMax Laier {
255abff3868SMax Laier 	char *s = buf;
256abff3868SMax Laier 	ssize_t res, pos = 0;
257abff3868SMax Laier 
258abff3868SMax Laier 	while (n > pos) {
259abff3868SMax Laier 		res = read(fd, s + pos, n - pos);
260abff3868SMax Laier 		switch (res) {
261abff3868SMax Laier 		case -1:
262abff3868SMax Laier 			if (errno == EINTR || errno == EAGAIN)
263abff3868SMax Laier 				continue;
264abff3868SMax Laier 		case 0:
265abff3868SMax Laier 			return (1);
266abff3868SMax Laier 		default:
267abff3868SMax Laier 			pos += res;
268abff3868SMax Laier 		}
269abff3868SMax Laier 	}
270abff3868SMax Laier 	return (0);
271abff3868SMax Laier }
272abff3868SMax Laier 
273abff3868SMax Laier /* Read data with the assertion that it all must come through, or
274abff3868SMax Laier  * else abort the process.  Based on atomicio() from openssh. */
275abff3868SMax Laier static void
276abff3868SMax Laier must_read(int fd, void *buf, size_t n)
277abff3868SMax Laier {
278abff3868SMax Laier 	char *s = buf;
279abff3868SMax Laier 	ssize_t res, pos = 0;
280abff3868SMax Laier 
281abff3868SMax Laier 	while (n > pos) {
282abff3868SMax Laier 		res = read(fd, s + pos, n - pos);
283abff3868SMax Laier 		switch (res) {
284abff3868SMax Laier 		case -1:
285abff3868SMax Laier 			if (errno == EINTR || errno == EAGAIN)
286abff3868SMax Laier 				continue;
287abff3868SMax Laier 		case 0:
288abff3868SMax Laier 			_exit(0);
289abff3868SMax Laier 		default:
290abff3868SMax Laier 			pos += res;
291abff3868SMax Laier 		}
292abff3868SMax Laier 	}
293abff3868SMax Laier }
294abff3868SMax Laier 
295abff3868SMax Laier /* Write data with the assertion that it all has to be written, or
296abff3868SMax Laier  * else abort the process.  Based on atomicio() from openssh. */
297abff3868SMax Laier static void
298abff3868SMax Laier must_write(int fd, void *buf, size_t n)
299abff3868SMax Laier {
300abff3868SMax Laier 	char *s = buf;
301abff3868SMax Laier 	ssize_t res, pos = 0;
302abff3868SMax Laier 
303abff3868SMax Laier 	while (n > pos) {
304abff3868SMax Laier 		res = write(fd, s + pos, n - pos);
305abff3868SMax Laier 		switch (res) {
306abff3868SMax Laier 		case -1:
307abff3868SMax Laier 			if (errno == EINTR || errno == EAGAIN)
308abff3868SMax Laier 				continue;
309abff3868SMax Laier 		case 0:
310abff3868SMax Laier 			_exit(0);
311abff3868SMax Laier 		default:
312abff3868SMax Laier 			pos += res;
313abff3868SMax Laier 		}
314abff3868SMax Laier 	}
315abff3868SMax Laier }
316