privsep.c revision 130617
1/*	$OpenBSD: privsep.c,v 1.8 2004/03/14 19:17:05 otto Exp $	*/
2
3/*
4 * Copyright (c) 2003 Can Erkin Acar
5 * Copyright (c) 2003 Anil Madhavapeddy <anil@recoil.org>
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include <sys/cdefs.h>
21__FBSDID("$FreeBSD: head/contrib/pf/pflogd/privsep.c 130617 2004-06-16 23:39:33Z mlaier $");
22
23#include <sys/param.h>
24#include <sys/time.h>
25#include <sys/socket.h>
26
27#include <net/if.h>
28#include <net/bpf.h>
29
30#include <err.h>
31#include <errno.h>
32#include <fcntl.h>
33#include <pwd.h>
34#include <signal.h>
35#include <stdio.h>
36#include <stdlib.h>
37#include <string.h>
38#include <pcap.h>
39#include <pcap-int.h>
40#include <syslog.h>
41#include <unistd.h>
42#include "pflogd.h"
43
44enum cmd_types {
45	PRIV_SET_SNAPLEN,	/* set the snaplength */
46	PRIV_OPEN_LOG		/* open logfile for appending */
47};
48
49static int priv_fd = -1;
50static volatile pid_t child_pid = -1;
51
52volatile sig_atomic_t gotsig_chld = 0;
53
54static void sig_pass_to_chld(int);
55static void sig_chld(int);
56static int  may_read(int, void *, size_t);
57static void must_read(int, void *, size_t);
58static void must_write(int, void *, size_t);
59static int  set_snaplen(int snap);
60
61/* bpf filter expression common to parent and child */
62extern char *filter;
63extern char *errbuf;
64extern char *filename;
65extern pcap_t *hpcap;
66
67/* based on syslogd privsep */
68int
69priv_init(void)
70{
71	int i, fd, socks[2], cmd;
72	int snaplen, ret;
73	struct passwd *pw;
74
75#ifdef __FreeBSD__
76	for (i = 1; i < NSIG; i++)
77#else
78	for (i = 1; i < _NSIG; i++)
79#endif
80		signal(i, SIG_DFL);
81
82	/* Create sockets */
83	if (socketpair(AF_LOCAL, SOCK_STREAM, PF_UNSPEC, socks) == -1)
84		err(1, "socketpair() failed");
85
86	pw = getpwnam("_pflogd");
87	if (pw == NULL)
88		errx(1, "unknown user _pflogd");
89	endpwent();
90
91	child_pid = fork();
92	if (child_pid < 0)
93		err(1, "fork() failed");
94
95	if (!child_pid) {
96		gid_t gidset[1];
97
98		/* Child - drop privileges and return */
99		if (chroot(pw->pw_dir) != 0)
100			err(1, "unable to chroot");
101		if (chdir("/") != 0)
102			err(1, "unable to chdir");
103
104		gidset[0] = pw->pw_gid;
105		if (setgroups(1, gidset) == -1)
106			err(1, "setgroups() failed");
107		if (setegid(pw->pw_gid) == -1)
108			err(1, "setegid() failed");
109		if (setgid(pw->pw_gid) == -1)
110			err(1, "setgid() failed");
111		if (seteuid(pw->pw_uid) == -1)
112			err(1, "seteuid() failed");
113		if (setuid(pw->pw_uid) == -1)
114			err(1, "setuid() failed");
115		close(socks[0]);
116		priv_fd = socks[1];
117		return 0;
118	}
119
120	/* Father */
121	/* Pass ALRM/TERM/HUP through to child, and accept CHLD */
122	signal(SIGALRM, sig_pass_to_chld);
123	signal(SIGTERM, sig_pass_to_chld);
124	signal(SIGHUP,  sig_pass_to_chld);
125	signal(SIGCHLD, sig_chld);
126
127	setproctitle("[priv]");
128	close(socks[1]);
129
130	while (!gotsig_chld) {
131		if (may_read(socks[0], &cmd, sizeof(int)))
132			break;
133		switch (cmd) {
134		case PRIV_SET_SNAPLEN:
135			logmsg(LOG_DEBUG,
136			    "[priv]: msg PRIV_SET_SNAPLENGTH received");
137			must_read(socks[0], &snaplen, sizeof(int));
138
139			ret = set_snaplen(snaplen);
140			if (ret) {
141				logmsg(LOG_NOTICE,
142				   "[priv]: set_snaplen failed for snaplen %d",
143				   snaplen);
144			}
145
146			must_write(socks[0], &ret, sizeof(int));
147			break;
148
149		case PRIV_OPEN_LOG:
150			logmsg(LOG_DEBUG,
151			    "[priv]: msg PRIV_OPEN_LOG received");
152			/* create or append logs but do not follow symlinks */
153			fd = open(filename,
154			    O_RDWR|O_CREAT|O_APPEND|O_NONBLOCK|O_NOFOLLOW,
155			    0600);
156			if (fd < 0)
157				logmsg(LOG_NOTICE,
158				    "[priv]: failed to open %s: %s",
159				    filename, strerror(errno));
160			send_fd(socks[0], fd);
161			close(fd);
162			break;
163
164		default:
165			logmsg(LOG_ERR, "[priv]: unknown command %d", cmd);
166			_exit(1);
167			/* NOTREACHED */
168		}
169	}
170
171	_exit(1);
172}
173
174/* this is called from parent */
175static int
176set_snaplen(int snap)
177{
178	if (hpcap == NULL)
179		return (1);
180
181	hpcap->snapshot = snap;
182	set_pcap_filter();
183
184	return 0;
185}
186
187
188/*
189 * send the snaplength to privileged process
190 */
191int
192priv_set_snaplen(int snaplen)
193{
194	int cmd, ret;
195
196	if (priv_fd < 0)
197		errx(1, "%s: called from privileged portion", __func__);
198
199	cmd = PRIV_SET_SNAPLEN;
200
201	must_write(priv_fd, &cmd, sizeof(int));
202	must_write(priv_fd, &snaplen, sizeof(int));
203
204	must_read(priv_fd, &ret, sizeof(int));
205
206	/* also set hpcap->snapshot in child */
207	if (ret == 0)
208		hpcap->snapshot = snaplen;
209
210	return (ret);
211}
212
213/* Open log-file */
214int
215priv_open_log(void)
216{
217	int cmd, fd;
218
219	if (priv_fd < 0)
220		errx(1, "%s: called from privileged portion\n", __func__);
221
222	cmd = PRIV_OPEN_LOG;
223	must_write(priv_fd, &cmd, sizeof(int));
224	fd = receive_fd(priv_fd);
225
226	return (fd);
227}
228
229/* If priv parent gets a TERM or HUP, pass it through to child instead */
230static void
231sig_pass_to_chld(int sig)
232{
233	int oerrno = errno;
234
235	if (child_pid != -1)
236		kill(child_pid, sig);
237	errno = oerrno;
238}
239
240/* if parent gets a SIGCHLD, it will exit */
241static void
242sig_chld(int sig)
243{
244	gotsig_chld = 1;
245}
246
247/* Read all data or return 1 for error.  */
248static int
249may_read(int fd, void *buf, size_t n)
250{
251	char *s = buf;
252	ssize_t res, pos = 0;
253
254	while (n > pos) {
255		res = read(fd, s + pos, n - pos);
256		switch (res) {
257		case -1:
258			if (errno == EINTR || errno == EAGAIN)
259				continue;
260		case 0:
261			return (1);
262		default:
263			pos += res;
264		}
265	}
266	return (0);
267}
268
269/* Read data with the assertion that it all must come through, or
270 * else abort the process.  Based on atomicio() from openssh. */
271static void
272must_read(int fd, void *buf, size_t n)
273{
274	char *s = buf;
275	ssize_t res, pos = 0;
276
277	while (n > pos) {
278		res = read(fd, s + pos, n - pos);
279		switch (res) {
280		case -1:
281			if (errno == EINTR || errno == EAGAIN)
282				continue;
283		case 0:
284			_exit(0);
285		default:
286			pos += res;
287		}
288	}
289}
290
291/* Write data with the assertion that it all has to be written, or
292 * else abort the process.  Based on atomicio() from openssh. */
293static void
294must_write(int fd, void *buf, size_t n)
295{
296	char *s = buf;
297	ssize_t res, pos = 0;
298
299	while (n > pos) {
300		res = write(fd, s + pos, n - pos);
301		switch (res) {
302		case -1:
303			if (errno == EINTR || errno == EAGAIN)
304				continue;
305		case 0:
306			_exit(0);
307		default:
308			pos += res;
309		}
310	}
311}
312