118316Swollman/*	$NetBSD: privsep.c,v 1.8 2019/02/03 10:48:47 mrg Exp $	*/
218316Swollman/*	$OpenBSD: privsep.c,v 1.16 2006/10/25 20:55:04 moritz Exp $	*/
318316Swollman
418316Swollman/*
518316Swollman * Copyright (c) 2003 Can Erkin Acar
618316Swollman * Copyright (c) 2003 Anil Madhavapeddy <anil@recoil.org>
718316Swollman *
818316Swollman * Permission to use, copy, modify, and distribute this software for any
918316Swollman * purpose with or without fee is hereby granted, provided that the above
1018316Swollman * copyright notice and this permission notice appear in all copies.
1118316Swollman *
1218316Swollman * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1318316Swollman * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1418316Swollman * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1518316Swollman * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1618316Swollman * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1718316Swollman * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1818316Swollman * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1918316Swollman */
2018316Swollman#include <sys/types.h>
2118316Swollman#include <sys/time.h>
2218316Swollman#include <sys/socket.h>
2318316Swollman#include <sys/ioctl.h>
2418316Swollman
2518316Swollman#include <net/if.h>
2618316Swollman#include <net/bpf.h>
2718316Swollman
2818316Swollman#include <string.h>
2918316Swollman
3046303Smarkm#include <err.h>
3150476Speter#include <errno.h>
3218316Swollman#include <fcntl.h>
3318316Swollman#include <limits.h>
3418316Swollman#include <pcap.h>
3518316Swollman/*
3618316Swollman * If we're going to include parts of the libpcap internals we MUST
3718316Swollman * set the feature-test macros they expect, or they may misbehave.
3818316Swollman */
3918316Swollman#define HAVE_STRLCPY
4018316Swollman#define HAVE_SNPRINTF
4118316Swollman#define HAVE_VSNPRINTF
4218316Swollman#include <pcap-int.h>
4318316Swollman#include <pwd.h>
4418316Swollman#include <signal.h>
4518316Swollman#include <stdio.h>
4618316Swollman#include <stdlib.h>
4718316Swollman#include <syslog.h>
4818316Swollman#include <unistd.h>
4918316Swollman#include "pflogd.h"
5018316Swollman
5118316Swollmanenum cmd_types {
5218316Swollman	PRIV_SET_SNAPLEN,	/* set the snaplength */
5318316Swollman	PRIV_MOVE_LOG,		/* move logfile away */
5418316Swollman	PRIV_OPEN_LOG		/* open logfile for appending */
5518316Swollman};
5618316Swollman
5718316Swollmanstatic int priv_fd = -1;
5818316Swollmanstatic volatile pid_t child_pid = -1;
5918316Swollman
6018316Swollmanvolatile sig_atomic_t gotsig_chld = 0;
6118316Swollman
6218316Swollmanstatic void sig_pass_to_chld(int);
6318316Swollmanstatic void sig_chld(int);
6418316Swollmanstatic int  may_read(int, void *, size_t);
6518316Swollmanstatic void must_read(int, void *, size_t);
6618316Swollmanstatic void must_write(int, void *, size_t);
6718316Swollmanstatic int  set_snaplen(uint32_t);
6818316Swollmanstatic int  move_log(const char *);
6918316Swollman
7018316Swollmanextern char *filename;
7118316Swollmanextern pcap_t *hpcap;
7218316Swollman
7318316Swollman/* based on syslogd privsep */
7418316Swollmanint
7518316Swollmanpriv_init(void)
7618316Swollman{
7718316Swollman	int i, fd, socks[2], cmd;
7818316Swollman	int snaplen, ret, olderrno;
7918316Swollman	struct passwd *pw;
8018316Swollman
8118316Swollman	for (i = 1; i < _NSIG; i++)
8218316Swollman		signal(i, SIG_DFL);
83190715Sphk
8418316Swollman	/* Create sockets */
8518316Swollman	if (socketpair(AF_LOCAL, SOCK_STREAM, PF_UNSPEC, socks) == -1)
8618316Swollman		err(1, "socketpair() failed");
8718316Swollman
8818316Swollman	pw = getpwnam("_pflogd");
8918316Swollman	if (pw == NULL)
9018316Swollman		errx(1, "unknown user _pflogd");
9118316Swollman	endpwent();
9218316Swollman
93190715Sphk	child_pid = fork();
9418316Swollman	if (child_pid < 0)
9518316Swollman		err(1, "fork() failed");
9618316Swollman
9718316Swollman	if (!child_pid) {
9818316Swollman		gid_t gidset[1];
9918316Swollman
10018316Swollman		/* Child - drop privileges and return */
10118316Swollman		if (chroot(pw->pw_dir) != 0)
10218316Swollman			err(1, "unable to chroot");
10346303Smarkm		if (chdir("/") != 0)
10418316Swollman			err(1, "unable to chdir");
10518316Swollman
10618316Swollman		gidset[0] = pw->pw_gid;
10718316Swollman#ifdef __OpenBSD__
10818316Swollman		if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1)
10918316Swollman			err(1, "setresgid() failed");
11018316Swollman#else
11118316Swollman		if (setgid(pw->pw_gid) == -1)
11292883Simp			err(1, "setgid() failed");
11392883Simp#endif
11418316Swollman		if (setgroups(1, gidset) == -1)
11592883Simp			err(1, "setgroups() failed");
11692883Simp#ifdef __OpenBSD__
11718316Swollman		if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1)
11892883Simp			err(1, "setresuid() failed");
11918316Swollman#else
12092883Simp		if (setuid(pw->pw_uid) == -1)
12118316Swollman			err(1, "setuid() failed");
12292883Simp#endif
12318316Swollman		close(socks[0]);
12492883Simp		priv_fd = socks[1];
12518316Swollman		return 0;
12692883Simp	}
12718316Swollman
12818316Swollman	/* Father */
12918316Swollman	/* Pass ALRM/TERM/HUP/INT/QUIT through to child, and accept CHLD */
13018316Swollman	signal(SIGALRM, sig_pass_to_chld);
13118316Swollman	signal(SIGTERM, sig_pass_to_chld);
13218316Swollman	signal(SIGHUP,  sig_pass_to_chld);
13318316Swollman	signal(SIGINT,  sig_pass_to_chld);
13418316Swollman	signal(SIGQUIT,  sig_pass_to_chld);
13546303Smarkm	signal(SIGCHLD, sig_chld);
13646303Smarkm
13746303Smarkm	setproctitle("[priv]");
13818316Swollman	close(socks[1]);
13992883Simp
140190712Sphk	while (!gotsig_chld) {
14192883Simp		if (may_read(socks[0], &cmd, sizeof(int)))
14292883Simp			break;
14392883Simp		switch (cmd) {
14446303Smarkm		case PRIV_SET_SNAPLEN:
14518316Swollman			logmsg(LOG_DEBUG,
146			    "[priv]: msg PRIV_SET_SNAPLENGTH received");
147			must_read(socks[0], &snaplen, sizeof(int));
148
149			ret = set_snaplen(snaplen);
150			if (ret) {
151				logmsg(LOG_NOTICE,
152				   "[priv]: set_snaplen failed for snaplen %d",
153				   snaplen);
154			}
155
156			must_write(socks[0], &ret, sizeof(int));
157			break;
158
159		case PRIV_OPEN_LOG:
160			logmsg(LOG_DEBUG,
161			    "[priv]: msg PRIV_OPEN_LOG received");
162			/* create or append logs but do not follow symlinks */
163			fd = open(filename,
164			    O_RDWR|O_CREAT|O_APPEND|O_NONBLOCK|O_NOFOLLOW,
165			    0600);
166			olderrno = errno;
167			send_fd(socks[0], fd);
168			if (fd < 0)
169				logmsg(LOG_NOTICE,
170				    "[priv]: failed to open %s: %s",
171				    filename, strerror(olderrno));
172			else
173				close(fd);
174			break;
175
176		case PRIV_MOVE_LOG:
177			logmsg(LOG_DEBUG,
178			    "[priv]: msg PRIV_MOVE_LOG received");
179			ret = move_log(filename);
180			must_write(socks[0], &ret, sizeof(int));
181			break;
182
183		default:
184			logmsg(LOG_ERR, "[priv]: unknown command %d", cmd);
185			_exit(1);
186			/* NOTREACHED */
187		}
188	}
189
190	_exit(1);
191}
192
193/* this is called from parent */
194static int
195set_snaplen(uint32_t snap)
196{
197	if (hpcap == NULL)
198		return (1);
199
200	hpcap->snapshot = snap;
201	set_pcap_filter();
202
203	return 0;
204}
205
206static int
207move_log(const char *name)
208{
209	char ren[PATH_MAX];
210	int len;
211
212	for (;;) {
213		int fd;
214
215		len = snprintf(ren, sizeof(ren), "%s.bad.%08x",
216		    name, arc4random());
217		if ((size_t)len >= sizeof(ren)) {
218			logmsg(LOG_ERR, "[priv] new name too long");
219			return (1);
220		}
221
222		/* lock destinanion */
223		fd = open(ren, O_CREAT|O_EXCL, 0);
224		if (fd >= 0) {
225			close(fd);
226			break;
227		}
228		/* if file exists, try another name */
229		if (errno != EEXIST && errno != EINTR) {
230			logmsg(LOG_ERR, "[priv] failed to create new name: %s",
231			    strerror(errno));
232			return (1);
233		}
234	}
235
236	if (rename(name, ren)) {
237		logmsg(LOG_ERR, "[priv] failed to rename %s to %s: %s",
238		    name, ren, strerror(errno));
239		return (1);
240	}
241
242	logmsg(LOG_NOTICE,
243	       "[priv]: log file %s moved to %s", name, ren);
244
245	return (0);
246}
247
248/*
249 * send the snaplength to privileged process
250 */
251int
252priv_set_snaplen(int snaplen)
253{
254	int cmd, ret;
255
256	if (priv_fd < 0)
257		errx(1, "%s: called from privileged portion", __func__);
258
259	cmd = PRIV_SET_SNAPLEN;
260
261	must_write(priv_fd, &cmd, sizeof(int));
262	must_write(priv_fd, &snaplen, sizeof(int));
263
264	must_read(priv_fd, &ret, sizeof(int));
265
266	/* also set hpcap->snapshot in child */
267	if (ret == 0)
268		hpcap->snapshot = snaplen;
269
270	return (ret);
271}
272
273/* Open log-file */
274int
275priv_open_log(void)
276{
277	int cmd, fd;
278
279	if (priv_fd < 0)
280		errx(1, "%s: called from privileged portion", __func__);
281
282	cmd = PRIV_OPEN_LOG;
283	must_write(priv_fd, &cmd, sizeof(int));
284	fd = receive_fd(priv_fd);
285
286	return (fd);
287}
288/* Move-away and reopen log-file */
289int
290priv_move_log(void)
291{
292	int cmd, ret;
293
294	if (priv_fd < 0)
295		errx(1, "%s: called from privileged portion\n", __func__);
296
297	cmd = PRIV_MOVE_LOG;
298	must_write(priv_fd, &cmd, sizeof(int));
299	must_read(priv_fd, &ret, sizeof(int));
300
301	return (ret);
302}
303
304/* If priv parent gets a TERM or HUP, pass it through to child instead */
305static void
306sig_pass_to_chld(int sig)
307{
308	int oerrno = errno;
309
310	if (child_pid != -1)
311		kill(child_pid, sig);
312	errno = oerrno;
313}
314
315/* if parent gets a SIGCHLD, it will exit */
316static void
317sig_chld(int sig)
318{
319	gotsig_chld = 1;
320}
321
322/* Read all data or return 1 for error.  */
323static int
324may_read(int fd, void *buf, size_t n)
325{
326	char *s = buf;
327	ssize_t res, pos = 0;
328
329	while (n > (size_t)pos) {
330		res = read(fd, s + pos, n - pos);
331		switch (res) {
332		case -1:
333			if (errno == EINTR || errno == EAGAIN)
334				continue;
335			/* FALLTHROUGH */
336		case 0:
337			return (1);
338		default:
339			pos += res;
340		}
341	}
342	return (0);
343}
344
345/* Read data with the assertion that it all must come through, or
346 * else abort the process.  Based on atomicio() from openssh. */
347static void
348must_read(int fd, void *buf, size_t n)
349{
350	char *s = buf;
351	ssize_t res, pos = 0;
352
353	while (n > (size_t)pos) {
354		res = read(fd, s + pos, n - pos);
355		switch (res) {
356		case -1:
357			if (errno == EINTR || errno == EAGAIN)
358				continue;
359			/* FALLTHROUGH */
360		case 0:
361			_exit(0);
362		default:
363			pos += res;
364		}
365	}
366}
367
368/* Write data with the assertion that it all has to be written, or
369 * else abort the process.  Based on atomicio() from openssh. */
370static void
371must_write(int fd, void *buf, size_t n)
372{
373	char *s = buf;
374	ssize_t res, pos = 0;
375
376	while (n > (size_t)pos) {
377		res = write(fd, s + pos, n - pos);
378		switch (res) {
379		case -1:
380			if (errno == EINTR || errno == EAGAIN)
381				continue;
382			/* FALLTHROUGH */
383		case 0:
384			_exit(0);
385		default:
386			pos += res;
387		}
388	}
389}
390