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