1171172Smlaier/*	$OpenBSD: privsep.c,v 1.16 2006/10/25 20:55:04 moritz Exp $	*/
2130614Smlaier
3130614Smlaier/*
4130614Smlaier * Copyright (c) 2003 Can Erkin Acar
5130614Smlaier * Copyright (c) 2003 Anil Madhavapeddy <anil@recoil.org>
6130614Smlaier *
7130614Smlaier * Permission to use, copy, modify, and distribute this software for any
8130614Smlaier * purpose with or without fee is hereby granted, provided that the above
9130614Smlaier * copyright notice and this permission notice appear in all copies.
10130614Smlaier *
11130614Smlaier * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12130614Smlaier * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13130614Smlaier * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14130614Smlaier * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15130614Smlaier * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16130614Smlaier * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17130614Smlaier * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18130614Smlaier */
19130617Smlaier
20130617Smlaier#include <sys/cdefs.h>
21130617Smlaier__FBSDID("$FreeBSD$");
22130617Smlaier
23171172Smlaier#include <sys/types.h>
24130614Smlaier#include <sys/time.h>
25130614Smlaier#include <sys/socket.h>
26223637Sbz#include <sys/ioctl.h>
27130614Smlaier
28130614Smlaier#include <net/if.h>
29130614Smlaier#include <net/bpf.h>
30130614Smlaier
31130614Smlaier#include <err.h>
32130614Smlaier#include <errno.h>
33130614Smlaier#include <fcntl.h>
34171172Smlaier#include <limits.h>
35171172Smlaier#include <pcap.h>
36171172Smlaier#include <pcap-int.h>
37130614Smlaier#include <pwd.h>
38130614Smlaier#include <signal.h>
39130614Smlaier#include <stdio.h>
40130614Smlaier#include <stdlib.h>
41130614Smlaier#include <string.h>
42130614Smlaier#include <syslog.h>
43130614Smlaier#include <unistd.h>
44130614Smlaier#include "pflogd.h"
45130614Smlaier
46130614Smlaierenum cmd_types {
47130614Smlaier	PRIV_SET_SNAPLEN,	/* set the snaplength */
48171172Smlaier	PRIV_MOVE_LOG,		/* move logfile away */
49130614Smlaier	PRIV_OPEN_LOG		/* open logfile for appending */
50130614Smlaier};
51130614Smlaier
52130614Smlaierstatic int priv_fd = -1;
53130614Smlaierstatic volatile pid_t child_pid = -1;
54130614Smlaier
55130614Smlaiervolatile sig_atomic_t gotsig_chld = 0;
56130614Smlaier
57130614Smlaierstatic void sig_pass_to_chld(int);
58130614Smlaierstatic void sig_chld(int);
59130614Smlaierstatic int  may_read(int, void *, size_t);
60130614Smlaierstatic void must_read(int, void *, size_t);
61130614Smlaierstatic void must_write(int, void *, size_t);
62130614Smlaierstatic int  set_snaplen(int snap);
63171172Smlaierstatic int  move_log(const char *name);
64130614Smlaier
65130614Smlaierextern char *filename;
66130614Smlaierextern pcap_t *hpcap;
67130614Smlaier
68130614Smlaier/* based on syslogd privsep */
69130614Smlaierint
70130614Smlaierpriv_init(void)
71130614Smlaier{
72130614Smlaier	int i, fd, socks[2], cmd;
73145840Smlaier	int snaplen, ret, olderrno;
74130614Smlaier	struct passwd *pw;
75130614Smlaier
76130617Smlaier#ifdef __FreeBSD__
77130617Smlaier	for (i = 1; i < NSIG; i++)
78130617Smlaier#else
79130614Smlaier	for (i = 1; i < _NSIG; i++)
80130617Smlaier#endif
81130614Smlaier		signal(i, SIG_DFL);
82130614Smlaier
83130614Smlaier	/* Create sockets */
84130614Smlaier	if (socketpair(AF_LOCAL, SOCK_STREAM, PF_UNSPEC, socks) == -1)
85130614Smlaier		err(1, "socketpair() failed");
86130614Smlaier
87130614Smlaier	pw = getpwnam("_pflogd");
88130614Smlaier	if (pw == NULL)
89130614Smlaier		errx(1, "unknown user _pflogd");
90130614Smlaier	endpwent();
91130614Smlaier
92130614Smlaier	child_pid = fork();
93130614Smlaier	if (child_pid < 0)
94130614Smlaier		err(1, "fork() failed");
95130614Smlaier
96130614Smlaier	if (!child_pid) {
97130614Smlaier		gid_t gidset[1];
98130614Smlaier
99130614Smlaier		/* Child - drop privileges and return */
100130614Smlaier		if (chroot(pw->pw_dir) != 0)
101130614Smlaier			err(1, "unable to chroot");
102130614Smlaier		if (chdir("/") != 0)
103130614Smlaier			err(1, "unable to chdir");
104130614Smlaier
105130614Smlaier		gidset[0] = pw->pw_gid;
106171172Smlaier		if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1)
107171172Smlaier			err(1, "setresgid() failed");
108130614Smlaier		if (setgroups(1, gidset) == -1)
109130614Smlaier			err(1, "setgroups() failed");
110171172Smlaier		if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1)
111171172Smlaier			err(1, "setresuid() failed");
112130614Smlaier		close(socks[0]);
113130614Smlaier		priv_fd = socks[1];
114130614Smlaier		return 0;
115130614Smlaier	}
116130614Smlaier
117130614Smlaier	/* Father */
118145840Smlaier	/* Pass ALRM/TERM/HUP/INT/QUIT through to child, and accept CHLD */
119130614Smlaier	signal(SIGALRM, sig_pass_to_chld);
120130614Smlaier	signal(SIGTERM, sig_pass_to_chld);
121130614Smlaier	signal(SIGHUP,  sig_pass_to_chld);
122145840Smlaier	signal(SIGINT,  sig_pass_to_chld);
123145840Smlaier	signal(SIGQUIT,  sig_pass_to_chld);
124130614Smlaier	signal(SIGCHLD, sig_chld);
125130614Smlaier
126130614Smlaier	setproctitle("[priv]");
127130614Smlaier	close(socks[1]);
128130614Smlaier
129130614Smlaier	while (!gotsig_chld) {
130130614Smlaier		if (may_read(socks[0], &cmd, sizeof(int)))
131130614Smlaier			break;
132130614Smlaier		switch (cmd) {
133130614Smlaier		case PRIV_SET_SNAPLEN:
134130614Smlaier			logmsg(LOG_DEBUG,
135130614Smlaier			    "[priv]: msg PRIV_SET_SNAPLENGTH received");
136130614Smlaier			must_read(socks[0], &snaplen, sizeof(int));
137130614Smlaier
138130614Smlaier			ret = set_snaplen(snaplen);
139130614Smlaier			if (ret) {
140130614Smlaier				logmsg(LOG_NOTICE,
141130614Smlaier				   "[priv]: set_snaplen failed for snaplen %d",
142130614Smlaier				   snaplen);
143130614Smlaier			}
144130614Smlaier
145130614Smlaier			must_write(socks[0], &ret, sizeof(int));
146130614Smlaier			break;
147130614Smlaier
148130614Smlaier		case PRIV_OPEN_LOG:
149130614Smlaier			logmsg(LOG_DEBUG,
150130614Smlaier			    "[priv]: msg PRIV_OPEN_LOG received");
151130614Smlaier			/* create or append logs but do not follow symlinks */
152130614Smlaier			fd = open(filename,
153130614Smlaier			    O_RDWR|O_CREAT|O_APPEND|O_NONBLOCK|O_NOFOLLOW,
154130614Smlaier			    0600);
155145840Smlaier			olderrno = errno;
156145840Smlaier			send_fd(socks[0], fd);
157130614Smlaier			if (fd < 0)
158130614Smlaier				logmsg(LOG_NOTICE,
159130614Smlaier				    "[priv]: failed to open %s: %s",
160145840Smlaier				    filename, strerror(olderrno));
161145840Smlaier			else
162145840Smlaier				close(fd);
163130614Smlaier			break;
164130614Smlaier
165171172Smlaier		case PRIV_MOVE_LOG:
166171172Smlaier			logmsg(LOG_DEBUG,
167171172Smlaier			    "[priv]: msg PRIV_MOVE_LOG received");
168171172Smlaier			ret = move_log(filename);
169171172Smlaier			must_write(socks[0], &ret, sizeof(int));
170171172Smlaier			break;
171171172Smlaier
172130614Smlaier		default:
173130614Smlaier			logmsg(LOG_ERR, "[priv]: unknown command %d", cmd);
174130614Smlaier			_exit(1);
175130614Smlaier			/* NOTREACHED */
176130614Smlaier		}
177130614Smlaier	}
178130614Smlaier
179130614Smlaier	_exit(1);
180130614Smlaier}
181130614Smlaier
182130614Smlaier/* this is called from parent */
183130614Smlaierstatic int
184130614Smlaierset_snaplen(int snap)
185130614Smlaier{
186130614Smlaier	if (hpcap == NULL)
187130614Smlaier		return (1);
188130614Smlaier
189130614Smlaier	hpcap->snapshot = snap;
190130614Smlaier	set_pcap_filter();
191130614Smlaier
192130614Smlaier	return 0;
193130614Smlaier}
194130614Smlaier
195171172Smlaierstatic int
196171172Smlaiermove_log(const char *name)
197171172Smlaier{
198171172Smlaier	char ren[PATH_MAX];
199171172Smlaier	int len;
200130614Smlaier
201171172Smlaier	for (;;) {
202171172Smlaier		int fd;
203171172Smlaier
204171172Smlaier		len = snprintf(ren, sizeof(ren), "%s.bad.%08x",
205171172Smlaier		    name, arc4random());
206171172Smlaier		if (len >= sizeof(ren)) {
207171172Smlaier			logmsg(LOG_ERR, "[priv] new name too long");
208171172Smlaier			return (1);
209171172Smlaier		}
210171172Smlaier
211171172Smlaier		/* lock destinanion */
212171172Smlaier		fd = open(ren, O_CREAT|O_EXCL, 0);
213171172Smlaier		if (fd >= 0) {
214171172Smlaier			close(fd);
215171172Smlaier			break;
216171172Smlaier		}
217171172Smlaier		/* if file exists, try another name */
218171172Smlaier		if (errno != EEXIST && errno != EINTR) {
219171172Smlaier			logmsg(LOG_ERR, "[priv] failed to create new name: %s",
220171172Smlaier			    strerror(errno));
221171172Smlaier			return (1);
222171172Smlaier		}
223171172Smlaier	}
224171172Smlaier
225171172Smlaier	if (rename(name, ren)) {
226171172Smlaier		logmsg(LOG_ERR, "[priv] failed to rename %s to %s: %s",
227171172Smlaier		    name, ren, strerror(errno));
228171172Smlaier		return (1);
229171172Smlaier	}
230171172Smlaier
231171172Smlaier	logmsg(LOG_NOTICE,
232171172Smlaier	       "[priv]: log file %s moved to %s", name, ren);
233171172Smlaier
234171172Smlaier	return (0);
235171172Smlaier}
236171172Smlaier
237130614Smlaier/*
238130614Smlaier * send the snaplength to privileged process
239130614Smlaier */
240130614Smlaierint
241130614Smlaierpriv_set_snaplen(int snaplen)
242130614Smlaier{
243130614Smlaier	int cmd, ret;
244130614Smlaier
245130614Smlaier	if (priv_fd < 0)
246130614Smlaier		errx(1, "%s: called from privileged portion", __func__);
247130614Smlaier
248130614Smlaier	cmd = PRIV_SET_SNAPLEN;
249130614Smlaier
250130614Smlaier	must_write(priv_fd, &cmd, sizeof(int));
251130614Smlaier	must_write(priv_fd, &snaplen, sizeof(int));
252130614Smlaier
253130614Smlaier	must_read(priv_fd, &ret, sizeof(int));
254130614Smlaier
255130614Smlaier	/* also set hpcap->snapshot in child */
256130614Smlaier	if (ret == 0)
257130614Smlaier		hpcap->snapshot = snaplen;
258130614Smlaier
259130614Smlaier	return (ret);
260130614Smlaier}
261130614Smlaier
262130614Smlaier/* Open log-file */
263130614Smlaierint
264130614Smlaierpriv_open_log(void)
265130614Smlaier{
266130614Smlaier	int cmd, fd;
267130614Smlaier
268130614Smlaier	if (priv_fd < 0)
269145840Smlaier		errx(1, "%s: called from privileged portion", __func__);
270130614Smlaier
271130614Smlaier	cmd = PRIV_OPEN_LOG;
272130614Smlaier	must_write(priv_fd, &cmd, sizeof(int));
273130614Smlaier	fd = receive_fd(priv_fd);
274130614Smlaier
275130614Smlaier	return (fd);
276130614Smlaier}
277171172Smlaier/* Move-away and reopen log-file */
278171172Smlaierint
279171172Smlaierpriv_move_log(void)
280171172Smlaier{
281171172Smlaier	int cmd, ret;
282130614Smlaier
283171172Smlaier	if (priv_fd < 0)
284171172Smlaier		errx(1, "%s: called from privileged portion\n", __func__);
285171172Smlaier
286171172Smlaier	cmd = PRIV_MOVE_LOG;
287171172Smlaier	must_write(priv_fd, &cmd, sizeof(int));
288171172Smlaier	must_read(priv_fd, &ret, sizeof(int));
289171172Smlaier
290171172Smlaier	return (ret);
291171172Smlaier}
292171172Smlaier
293130614Smlaier/* If priv parent gets a TERM or HUP, pass it through to child instead */
294130614Smlaierstatic void
295130614Smlaiersig_pass_to_chld(int sig)
296130614Smlaier{
297130614Smlaier	int oerrno = errno;
298130614Smlaier
299130614Smlaier	if (child_pid != -1)
300130614Smlaier		kill(child_pid, sig);
301130614Smlaier	errno = oerrno;
302130614Smlaier}
303130614Smlaier
304130614Smlaier/* if parent gets a SIGCHLD, it will exit */
305130614Smlaierstatic void
306130614Smlaiersig_chld(int sig)
307130614Smlaier{
308130614Smlaier	gotsig_chld = 1;
309130614Smlaier}
310130614Smlaier
311130614Smlaier/* Read all data or return 1 for error.  */
312130614Smlaierstatic int
313130614Smlaiermay_read(int fd, void *buf, size_t n)
314130614Smlaier{
315130614Smlaier	char *s = buf;
316130614Smlaier	ssize_t res, pos = 0;
317130614Smlaier
318130614Smlaier	while (n > pos) {
319130614Smlaier		res = read(fd, s + pos, n - pos);
320130614Smlaier		switch (res) {
321130614Smlaier		case -1:
322130614Smlaier			if (errno == EINTR || errno == EAGAIN)
323130614Smlaier				continue;
324130614Smlaier		case 0:
325130614Smlaier			return (1);
326130614Smlaier		default:
327130614Smlaier			pos += res;
328130614Smlaier		}
329130614Smlaier	}
330130614Smlaier	return (0);
331130614Smlaier}
332130614Smlaier
333130614Smlaier/* Read data with the assertion that it all must come through, or
334130614Smlaier * else abort the process.  Based on atomicio() from openssh. */
335130614Smlaierstatic void
336130614Smlaiermust_read(int fd, void *buf, size_t n)
337130614Smlaier{
338130614Smlaier	char *s = buf;
339130614Smlaier	ssize_t res, pos = 0;
340130614Smlaier
341130614Smlaier	while (n > pos) {
342130614Smlaier		res = read(fd, s + pos, n - pos);
343130614Smlaier		switch (res) {
344130614Smlaier		case -1:
345130614Smlaier			if (errno == EINTR || errno == EAGAIN)
346130614Smlaier				continue;
347130614Smlaier		case 0:
348130614Smlaier			_exit(0);
349130614Smlaier		default:
350130614Smlaier			pos += res;
351130614Smlaier		}
352130614Smlaier	}
353130614Smlaier}
354130614Smlaier
355130614Smlaier/* Write data with the assertion that it all has to be written, or
356130614Smlaier * else abort the process.  Based on atomicio() from openssh. */
357130614Smlaierstatic void
358130614Smlaiermust_write(int fd, void *buf, size_t n)
359130614Smlaier{
360130614Smlaier	char *s = buf;
361130614Smlaier	ssize_t res, pos = 0;
362130614Smlaier
363130614Smlaier	while (n > pos) {
364130614Smlaier		res = write(fd, s + pos, n - pos);
365130614Smlaier		switch (res) {
366130614Smlaier		case -1:
367130614Smlaier			if (errno == EINTR || errno == EAGAIN)
368130614Smlaier				continue;
369130614Smlaier		case 0:
370130614Smlaier			_exit(0);
371130614Smlaier		default:
372130614Smlaier			pos += res;
373130614Smlaier		}
374130614Smlaier	}
375130614Smlaier}
376