privsep.c revision 1.2
1251881Speter/*	$OpenBSD: privsep.c,v 1.12 2004/07/14 19:07:03 henning Exp $	*/
2251881Speter
3251881Speter/*
4251881Speter * Copyright (c) 2003 Can Erkin Acar
5251881Speter * Copyright (c) 2003 Anil Madhavapeddy <anil@recoil.org>
6251881Speter *
7251881Speter * Permission to use, copy, modify, and distribute this software for any
8251881Speter * purpose with or without fee is hereby granted, provided that the above
9251881Speter * copyright notice and this permission notice appear in all copies.
10251881Speter *
11251881Speter * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12251881Speter * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13251881Speter * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14251881Speter * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15251881Speter * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16251881Speter * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17251881Speter * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18251881Speter */
19251881Speter#include <sys/ioctl.h>
20251881Speter#include <sys/types.h>
21251881Speter#include <sys/time.h>
22251881Speter#include <sys/socket.h>
23251881Speter#include <sys/ioctl.h>
24251881Speter
25251881Speter#include <net/if.h>
26251881Speter#include <net/bpf.h>
27251881Speter
28251881Speter#include <string.h>
29251881Speter
30251881Speter#include <err.h>
31251881Speter#include <errno.h>
32251881Speter#include <fcntl.h>
33251881Speter#include <pcap.h>
34251881Speter#include <pcap-int.h>
35251881Speter#include <pwd.h>
36251881Speter#include <signal.h>
37251881Speter#include <stdio.h>
38251881Speter#include <stdlib.h>
39251881Speter#include <syslog.h>
40251881Speter#include <unistd.h>
41251881Speter#include "pflogd.h"
42251881Speter
43251881Speterenum cmd_types {
44251881Speter	PRIV_SET_SNAPLEN,	/* set the snaplength */
45251881Speter	PRIV_OPEN_LOG		/* open logfile for appending */
46251881Speter};
47251881Speter
48251881Speterstatic int priv_fd = -1;
49251881Speterstatic volatile pid_t child_pid = -1;
50251881Speter
51251881Spetervolatile sig_atomic_t gotsig_chld = 0;
52251881Speter
53251881Speterstatic void sig_pass_to_chld(int);
54251881Speterstatic void sig_chld(int);
55251881Speterstatic int  may_read(int, void *, size_t);
56251881Speterstatic void must_read(int, void *, size_t);
57251881Speterstatic void must_write(int, void *, size_t);
58251881Speterstatic int  set_snaplen(int snap);
59251881Speter
60251881Speter/* bpf filter expression common to parent and child */
61251881Speterextern char *filter;
62251881Speterextern char *errbuf;
63251881Speterextern char *filename;
64251881Speterextern pcap_t *hpcap;
65251881Speter
66251881Speter/* based on syslogd privsep */
67251881Speterint
68251881Speterpriv_init(void)
69251881Speter{
70251881Speter	int i, fd, socks[2], cmd;
71251881Speter	int snaplen, ret, olderrno;
72251881Speter	struct passwd *pw;
73251881Speter
74251881Speter	for (i = 1; i < _NSIG; i++)
75251881Speter		signal(i, SIG_DFL);
76251881Speter
77251881Speter	/* Create sockets */
78251881Speter	if (socketpair(AF_LOCAL, SOCK_STREAM, PF_UNSPEC, socks) == -1)
79251881Speter		err(1, "socketpair() failed");
80251881Speter
81251881Speter	pw = getpwnam("_pflogd");
82251881Speter	if (pw == NULL)
83251881Speter		errx(1, "unknown user _pflogd");
84251881Speter	endpwent();
85251881Speter
86251881Speter	child_pid = fork();
87251881Speter	if (child_pid < 0)
88251881Speter		err(1, "fork() failed");
89251881Speter
90251881Speter	if (!child_pid) {
91251881Speter		gid_t gidset[1];
92251881Speter
93251881Speter		/* Child - drop privileges and return */
94		if (chroot(pw->pw_dir) != 0)
95			err(1, "unable to chroot");
96		if (chdir("/") != 0)
97			err(1, "unable to chdir");
98
99		gidset[0] = pw->pw_gid;
100		if (setgroups(1, gidset) == -1)
101			err(1, "setgroups() failed");
102#ifdef __OpenBSD__
103		if (setegid(pw->pw_gid) == -1)
104			err(1, "setegid() failed");
105#endif
106		if (setgid(pw->pw_gid) == -1)
107			err(1, "setgid() failed");
108#ifdef __OpenBSD__
109		if (seteuid(pw->pw_uid) == -1)
110			err(1, "seteuid() failed");
111#endif
112		if (setuid(pw->pw_uid) == -1)
113			err(1, "setuid() failed");
114		close(socks[0]);
115		priv_fd = socks[1];
116		return 0;
117	}
118
119	/* Father */
120	/* Pass ALRM/TERM/HUP through to child, and accept CHLD */
121	signal(SIGALRM, sig_pass_to_chld);
122	signal(SIGTERM, sig_pass_to_chld);
123	signal(SIGHUP,  sig_pass_to_chld);
124	signal(SIGCHLD, sig_chld);
125
126	setproctitle("[priv]");
127	close(socks[1]);
128
129	while (!gotsig_chld) {
130		if (may_read(socks[0], &cmd, sizeof(int)))
131			break;
132		switch (cmd) {
133		case PRIV_SET_SNAPLEN:
134			logmsg(LOG_DEBUG,
135			    "[priv]: msg PRIV_SET_SNAPLENGTH received");
136			must_read(socks[0], &snaplen, sizeof(int));
137
138			ret = set_snaplen(snaplen);
139			if (ret) {
140				logmsg(LOG_NOTICE,
141				   "[priv]: set_snaplen failed for snaplen %d",
142				   snaplen);
143			}
144
145			must_write(socks[0], &ret, sizeof(int));
146			break;
147
148		case PRIV_OPEN_LOG:
149			logmsg(LOG_DEBUG,
150			    "[priv]: msg PRIV_OPEN_LOG received");
151			/* create or append logs but do not follow symlinks */
152			fd = open(filename,
153			    O_RDWR|O_CREAT|O_APPEND|O_NONBLOCK|O_NOFOLLOW,
154			    0600);
155			olderrno = errno;
156			send_fd(socks[0], fd);
157			if (fd < 0)
158				logmsg(LOG_NOTICE,
159				    "[priv]: failed to open %s: %s",
160				    filename, strerror(olderrno));
161			else
162				close(fd);
163			break;
164
165		default:
166			logmsg(LOG_ERR, "[priv]: unknown command %d", cmd);
167			_exit(1);
168			/* NOTREACHED */
169		}
170	}
171
172	_exit(1);
173}
174
175/* this is called from parent */
176static int
177set_snaplen(int snap)
178{
179	if (hpcap == NULL)
180		return (1);
181
182	hpcap->snapshot = snap;
183	set_pcap_filter();
184
185	return 0;
186}
187
188
189/*
190 * send the snaplength to privileged process
191 */
192int
193priv_set_snaplen(int snaplen)
194{
195	int cmd, ret;
196
197	if (priv_fd < 0)
198		errx(1, "%s: called from privileged portion", __func__);
199
200	cmd = PRIV_SET_SNAPLEN;
201
202	must_write(priv_fd, &cmd, sizeof(int));
203	must_write(priv_fd, &snaplen, sizeof(int));
204
205	must_read(priv_fd, &ret, sizeof(int));
206
207	/* also set hpcap->snapshot in child */
208	if (ret == 0)
209		hpcap->snapshot = snaplen;
210
211	return (ret);
212}
213
214/* Open log-file */
215int
216priv_open_log(void)
217{
218	int cmd, fd;
219
220	if (priv_fd < 0)
221		errx(1, "%s: called from privileged portion", __func__);
222
223	cmd = PRIV_OPEN_LOG;
224	must_write(priv_fd, &cmd, sizeof(int));
225	fd = receive_fd(priv_fd);
226
227	return (fd);
228}
229
230/* If priv parent gets a TERM or HUP, pass it through to child instead */
231static void
232sig_pass_to_chld(int sig)
233{
234	int oerrno = errno;
235
236	if (child_pid != -1)
237		kill(child_pid, sig);
238	errno = oerrno;
239}
240
241/* if parent gets a SIGCHLD, it will exit */
242static void
243sig_chld(int sig)
244{
245	gotsig_chld = 1;
246}
247
248/* Read all data or return 1 for error.  */
249static int
250may_read(int fd, void *buf, size_t n)
251{
252	char *s = buf;
253	ssize_t res, pos = 0;
254
255	while (n > pos) {
256		res = read(fd, s + pos, n - pos);
257		switch (res) {
258		case -1:
259			if (errno == EINTR || errno == EAGAIN)
260				continue;
261		case 0:
262			return (1);
263		default:
264			pos += res;
265		}
266	}
267	return (0);
268}
269
270/* Read data with the assertion that it all must come through, or
271 * else abort the process.  Based on atomicio() from openssh. */
272static void
273must_read(int fd, void *buf, size_t n)
274{
275	char *s = buf;
276	ssize_t res, pos = 0;
277
278	while (n > pos) {
279		res = read(fd, s + pos, n - pos);
280		switch (res) {
281		case -1:
282			if (errno == EINTR || errno == EAGAIN)
283				continue;
284		case 0:
285			_exit(0);
286		default:
287			pos += res;
288		}
289	}
290}
291
292/* Write data with the assertion that it all has to be written, or
293 * else abort the process.  Based on atomicio() from openssh. */
294static void
295must_write(int fd, void *buf, size_t n)
296{
297	char *s = buf;
298	ssize_t res, pos = 0;
299
300	while (n > pos) {
301		res = write(fd, s + pos, n - pos);
302		switch (res) {
303		case -1:
304			if (errno == EINTR || errno == EAGAIN)
305				continue;
306		case 0:
307			_exit(0);
308		default:
309			pos += res;
310		}
311	}
312}
313