15ee7cd21SMax Laier /* $OpenBSD: privsep.c,v 1.16 2006/10/25 20:55:04 moritz Exp $ */
2abff3868SMax Laier
3abff3868SMax Laier /*
4abff3868SMax Laier * Copyright (c) 2003 Can Erkin Acar
5abff3868SMax Laier * Copyright (c) 2003 Anil Madhavapeddy <anil@recoil.org>
6abff3868SMax Laier *
7abff3868SMax Laier * Permission to use, copy, modify, and distribute this software for any
8abff3868SMax Laier * purpose with or without fee is hereby granted, provided that the above
9abff3868SMax Laier * copyright notice and this permission notice appear in all copies.
10abff3868SMax Laier *
11abff3868SMax Laier * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12abff3868SMax Laier * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13abff3868SMax Laier * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14abff3868SMax Laier * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15abff3868SMax Laier * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16abff3868SMax Laier * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17abff3868SMax Laier * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18abff3868SMax Laier */
1922ac3eadSMax Laier
205ee7cd21SMax Laier #include <sys/types.h>
21abff3868SMax Laier #include <sys/time.h>
22abff3868SMax Laier #include <sys/socket.h>
23*e0bfbfceSBjoern A. Zeeb #include <sys/ioctl.h>
24abff3868SMax Laier
25abff3868SMax Laier #include <net/if.h>
26abff3868SMax Laier #include <net/bpf.h>
27abff3868SMax Laier
28abff3868SMax Laier #include <err.h>
29abff3868SMax Laier #include <errno.h>
30abff3868SMax Laier #include <fcntl.h>
315ee7cd21SMax Laier #include <limits.h>
325ee7cd21SMax Laier #include <pcap.h>
335ee7cd21SMax Laier #include <pcap-int.h>
34abff3868SMax Laier #include <pwd.h>
35abff3868SMax Laier #include <signal.h>
36abff3868SMax Laier #include <stdio.h>
37abff3868SMax Laier #include <stdlib.h>
38abff3868SMax Laier #include <string.h>
39abff3868SMax Laier #include <syslog.h>
40abff3868SMax Laier #include <unistd.h>
41abff3868SMax Laier #include "pflogd.h"
42abff3868SMax Laier
43abff3868SMax Laier enum cmd_types {
44abff3868SMax Laier PRIV_SET_SNAPLEN, /* set the snaplength */
455ee7cd21SMax Laier PRIV_MOVE_LOG, /* move logfile away */
46abff3868SMax Laier PRIV_OPEN_LOG /* open logfile for appending */
47abff3868SMax Laier };
48abff3868SMax Laier
49abff3868SMax Laier static int priv_fd = -1;
50abff3868SMax Laier static volatile pid_t child_pid = -1;
51abff3868SMax Laier
52abff3868SMax Laier volatile sig_atomic_t gotsig_chld = 0;
53abff3868SMax Laier
54abff3868SMax Laier static void sig_pass_to_chld(int);
55abff3868SMax Laier static void sig_chld(int);
56abff3868SMax Laier static int may_read(int, void *, size_t);
57abff3868SMax Laier static void must_read(int, void *, size_t);
58abff3868SMax Laier static void must_write(int, void *, size_t);
59abff3868SMax Laier static int set_snaplen(int snap);
605ee7cd21SMax Laier static int move_log(const char *name);
61abff3868SMax Laier
62abff3868SMax Laier extern char *filename;
63abff3868SMax Laier extern pcap_t *hpcap;
64abff3868SMax Laier
65abff3868SMax Laier /* based on syslogd privsep */
66abff3868SMax Laier int
priv_init(void)67abff3868SMax Laier priv_init(void)
68abff3868SMax Laier {
69abff3868SMax Laier int i, fd, socks[2], cmd;
700baf7c86SMax Laier int snaplen, ret, olderrno;
71abff3868SMax Laier struct passwd *pw;
72abff3868SMax Laier
7322ac3eadSMax Laier #ifdef __FreeBSD__
7422ac3eadSMax Laier for (i = 1; i < NSIG; i++)
7522ac3eadSMax Laier #else
76abff3868SMax Laier for (i = 1; i < _NSIG; i++)
7722ac3eadSMax Laier #endif
78abff3868SMax Laier signal(i, SIG_DFL);
79abff3868SMax Laier
80abff3868SMax Laier /* Create sockets */
81abff3868SMax Laier if (socketpair(AF_LOCAL, SOCK_STREAM, PF_UNSPEC, socks) == -1)
82abff3868SMax Laier err(1, "socketpair() failed");
83abff3868SMax Laier
84abff3868SMax Laier pw = getpwnam("_pflogd");
85abff3868SMax Laier if (pw == NULL)
86abff3868SMax Laier errx(1, "unknown user _pflogd");
87abff3868SMax Laier endpwent();
88abff3868SMax Laier
89abff3868SMax Laier child_pid = fork();
90abff3868SMax Laier if (child_pid < 0)
91abff3868SMax Laier err(1, "fork() failed");
92abff3868SMax Laier
93abff3868SMax Laier if (!child_pid) {
94abff3868SMax Laier gid_t gidset[1];
95abff3868SMax Laier
96abff3868SMax Laier /* Child - drop privileges and return */
97abff3868SMax Laier if (chroot(pw->pw_dir) != 0)
98abff3868SMax Laier err(1, "unable to chroot");
99abff3868SMax Laier if (chdir("/") != 0)
100abff3868SMax Laier err(1, "unable to chdir");
101abff3868SMax Laier
102abff3868SMax Laier gidset[0] = pw->pw_gid;
1035ee7cd21SMax Laier if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1)
1045ee7cd21SMax Laier err(1, "setresgid() failed");
105abff3868SMax Laier if (setgroups(1, gidset) == -1)
106abff3868SMax Laier err(1, "setgroups() failed");
1075ee7cd21SMax Laier if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1)
1085ee7cd21SMax Laier err(1, "setresuid() failed");
109abff3868SMax Laier close(socks[0]);
110abff3868SMax Laier priv_fd = socks[1];
111abff3868SMax Laier return 0;
112abff3868SMax Laier }
113abff3868SMax Laier
114abff3868SMax Laier /* Father */
1150baf7c86SMax Laier /* Pass ALRM/TERM/HUP/INT/QUIT through to child, and accept CHLD */
116abff3868SMax Laier signal(SIGALRM, sig_pass_to_chld);
117abff3868SMax Laier signal(SIGTERM, sig_pass_to_chld);
118abff3868SMax Laier signal(SIGHUP, sig_pass_to_chld);
1190baf7c86SMax Laier signal(SIGINT, sig_pass_to_chld);
1200baf7c86SMax Laier signal(SIGQUIT, sig_pass_to_chld);
121abff3868SMax Laier signal(SIGCHLD, sig_chld);
122abff3868SMax Laier
123abff3868SMax Laier setproctitle("[priv]");
124abff3868SMax Laier close(socks[1]);
125abff3868SMax Laier
126abff3868SMax Laier while (!gotsig_chld) {
127abff3868SMax Laier if (may_read(socks[0], &cmd, sizeof(int)))
128abff3868SMax Laier break;
129abff3868SMax Laier switch (cmd) {
130abff3868SMax Laier case PRIV_SET_SNAPLEN:
131abff3868SMax Laier logmsg(LOG_DEBUG,
132abff3868SMax Laier "[priv]: msg PRIV_SET_SNAPLENGTH received");
133abff3868SMax Laier must_read(socks[0], &snaplen, sizeof(int));
134abff3868SMax Laier
135abff3868SMax Laier ret = set_snaplen(snaplen);
136abff3868SMax Laier if (ret) {
137abff3868SMax Laier logmsg(LOG_NOTICE,
138abff3868SMax Laier "[priv]: set_snaplen failed for snaplen %d",
139abff3868SMax Laier snaplen);
140abff3868SMax Laier }
141abff3868SMax Laier
142abff3868SMax Laier must_write(socks[0], &ret, sizeof(int));
143abff3868SMax Laier break;
144abff3868SMax Laier
145abff3868SMax Laier case PRIV_OPEN_LOG:
146abff3868SMax Laier logmsg(LOG_DEBUG,
147abff3868SMax Laier "[priv]: msg PRIV_OPEN_LOG received");
148abff3868SMax Laier /* create or append logs but do not follow symlinks */
149abff3868SMax Laier fd = open(filename,
150abff3868SMax Laier O_RDWR|O_CREAT|O_APPEND|O_NONBLOCK|O_NOFOLLOW,
151abff3868SMax Laier 0600);
1520baf7c86SMax Laier olderrno = errno;
1530baf7c86SMax Laier send_fd(socks[0], fd);
154abff3868SMax Laier if (fd < 0)
155abff3868SMax Laier logmsg(LOG_NOTICE,
156abff3868SMax Laier "[priv]: failed to open %s: %s",
1570baf7c86SMax Laier filename, strerror(olderrno));
1580baf7c86SMax Laier else
159abff3868SMax Laier close(fd);
160abff3868SMax Laier break;
161abff3868SMax Laier
1625ee7cd21SMax Laier case PRIV_MOVE_LOG:
1635ee7cd21SMax Laier logmsg(LOG_DEBUG,
1645ee7cd21SMax Laier "[priv]: msg PRIV_MOVE_LOG received");
1655ee7cd21SMax Laier ret = move_log(filename);
1665ee7cd21SMax Laier must_write(socks[0], &ret, sizeof(int));
1675ee7cd21SMax Laier break;
1685ee7cd21SMax Laier
169abff3868SMax Laier default:
170abff3868SMax Laier logmsg(LOG_ERR, "[priv]: unknown command %d", cmd);
171abff3868SMax Laier _exit(1);
172abff3868SMax Laier /* NOTREACHED */
173abff3868SMax Laier }
174abff3868SMax Laier }
175abff3868SMax Laier
176abff3868SMax Laier _exit(1);
177abff3868SMax Laier }
178abff3868SMax Laier
179abff3868SMax Laier /* this is called from parent */
180abff3868SMax Laier static int
set_snaplen(int snap)181abff3868SMax Laier set_snaplen(int snap)
182abff3868SMax Laier {
183abff3868SMax Laier if (hpcap == NULL)
184abff3868SMax Laier return (1);
185abff3868SMax Laier
186abff3868SMax Laier hpcap->snapshot = snap;
187abff3868SMax Laier set_pcap_filter();
188abff3868SMax Laier
189abff3868SMax Laier return 0;
190abff3868SMax Laier }
191abff3868SMax Laier
1925ee7cd21SMax Laier static int
move_log(const char * name)1935ee7cd21SMax Laier move_log(const char *name)
1945ee7cd21SMax Laier {
1955ee7cd21SMax Laier char ren[PATH_MAX];
1965ee7cd21SMax Laier int len;
1975ee7cd21SMax Laier
1985ee7cd21SMax Laier for (;;) {
1995ee7cd21SMax Laier int fd;
2005ee7cd21SMax Laier
2015ee7cd21SMax Laier len = snprintf(ren, sizeof(ren), "%s.bad.%08x",
2025ee7cd21SMax Laier name, arc4random());
2035ee7cd21SMax Laier if (len >= sizeof(ren)) {
2045ee7cd21SMax Laier logmsg(LOG_ERR, "[priv] new name too long");
2055ee7cd21SMax Laier return (1);
2065ee7cd21SMax Laier }
2075ee7cd21SMax Laier
2085ee7cd21SMax Laier /* lock destinanion */
2095ee7cd21SMax Laier fd = open(ren, O_CREAT|O_EXCL, 0);
2105ee7cd21SMax Laier if (fd >= 0) {
2115ee7cd21SMax Laier close(fd);
2125ee7cd21SMax Laier break;
2135ee7cd21SMax Laier }
2145ee7cd21SMax Laier /* if file exists, try another name */
2155ee7cd21SMax Laier if (errno != EEXIST && errno != EINTR) {
2165ee7cd21SMax Laier logmsg(LOG_ERR, "[priv] failed to create new name: %s",
2175ee7cd21SMax Laier strerror(errno));
2185ee7cd21SMax Laier return (1);
2195ee7cd21SMax Laier }
2205ee7cd21SMax Laier }
2215ee7cd21SMax Laier
2225ee7cd21SMax Laier if (rename(name, ren)) {
2235ee7cd21SMax Laier logmsg(LOG_ERR, "[priv] failed to rename %s to %s: %s",
2245ee7cd21SMax Laier name, ren, strerror(errno));
2255ee7cd21SMax Laier return (1);
2265ee7cd21SMax Laier }
2275ee7cd21SMax Laier
2285ee7cd21SMax Laier logmsg(LOG_NOTICE,
2295ee7cd21SMax Laier "[priv]: log file %s moved to %s", name, ren);
2305ee7cd21SMax Laier
2315ee7cd21SMax Laier return (0);
2325ee7cd21SMax Laier }
233abff3868SMax Laier
234abff3868SMax Laier /*
235abff3868SMax Laier * send the snaplength to privileged process
236abff3868SMax Laier */
237abff3868SMax Laier int
priv_set_snaplen(int snaplen)238abff3868SMax Laier priv_set_snaplen(int snaplen)
239abff3868SMax Laier {
240abff3868SMax Laier int cmd, ret;
241abff3868SMax Laier
242abff3868SMax Laier if (priv_fd < 0)
243abff3868SMax Laier errx(1, "%s: called from privileged portion", __func__);
244abff3868SMax Laier
245abff3868SMax Laier cmd = PRIV_SET_SNAPLEN;
246abff3868SMax Laier
247abff3868SMax Laier must_write(priv_fd, &cmd, sizeof(int));
248abff3868SMax Laier must_write(priv_fd, &snaplen, sizeof(int));
249abff3868SMax Laier
250abff3868SMax Laier must_read(priv_fd, &ret, sizeof(int));
251abff3868SMax Laier
252abff3868SMax Laier /* also set hpcap->snapshot in child */
253abff3868SMax Laier if (ret == 0)
254abff3868SMax Laier hpcap->snapshot = snaplen;
255abff3868SMax Laier
256abff3868SMax Laier return (ret);
257abff3868SMax Laier }
258abff3868SMax Laier
259abff3868SMax Laier /* Open log-file */
260abff3868SMax Laier int
priv_open_log(void)261abff3868SMax Laier priv_open_log(void)
262abff3868SMax Laier {
263abff3868SMax Laier int cmd, fd;
264abff3868SMax Laier
265abff3868SMax Laier if (priv_fd < 0)
2660baf7c86SMax Laier errx(1, "%s: called from privileged portion", __func__);
267abff3868SMax Laier
268abff3868SMax Laier cmd = PRIV_OPEN_LOG;
269abff3868SMax Laier must_write(priv_fd, &cmd, sizeof(int));
270abff3868SMax Laier fd = receive_fd(priv_fd);
271abff3868SMax Laier
272abff3868SMax Laier return (fd);
273abff3868SMax Laier }
2745ee7cd21SMax Laier /* Move-away and reopen log-file */
2755ee7cd21SMax Laier int
priv_move_log(void)2765ee7cd21SMax Laier priv_move_log(void)
2775ee7cd21SMax Laier {
2785ee7cd21SMax Laier int cmd, ret;
2795ee7cd21SMax Laier
2805ee7cd21SMax Laier if (priv_fd < 0)
2815ee7cd21SMax Laier errx(1, "%s: called from privileged portion\n", __func__);
2825ee7cd21SMax Laier
2835ee7cd21SMax Laier cmd = PRIV_MOVE_LOG;
2845ee7cd21SMax Laier must_write(priv_fd, &cmd, sizeof(int));
2855ee7cd21SMax Laier must_read(priv_fd, &ret, sizeof(int));
2865ee7cd21SMax Laier
2875ee7cd21SMax Laier return (ret);
2885ee7cd21SMax Laier }
289abff3868SMax Laier
290abff3868SMax Laier /* If priv parent gets a TERM or HUP, pass it through to child instead */
291abff3868SMax Laier static void
sig_pass_to_chld(int sig)292abff3868SMax Laier sig_pass_to_chld(int sig)
293abff3868SMax Laier {
294abff3868SMax Laier int oerrno = errno;
295abff3868SMax Laier
296abff3868SMax Laier if (child_pid != -1)
297abff3868SMax Laier kill(child_pid, sig);
298abff3868SMax Laier errno = oerrno;
299abff3868SMax Laier }
300abff3868SMax Laier
301abff3868SMax Laier /* if parent gets a SIGCHLD, it will exit */
302abff3868SMax Laier static void
sig_chld(int sig)303abff3868SMax Laier sig_chld(int sig)
304abff3868SMax Laier {
305abff3868SMax Laier gotsig_chld = 1;
306abff3868SMax Laier }
307abff3868SMax Laier
308abff3868SMax Laier /* Read all data or return 1 for error. */
309abff3868SMax Laier static int
may_read(int fd,void * buf,size_t n)310abff3868SMax Laier may_read(int fd, void *buf, size_t n)
311abff3868SMax Laier {
312abff3868SMax Laier char *s = buf;
313abff3868SMax Laier ssize_t res, pos = 0;
314abff3868SMax Laier
315abff3868SMax Laier while (n > pos) {
316abff3868SMax Laier res = read(fd, s + pos, n - pos);
317abff3868SMax Laier switch (res) {
318abff3868SMax Laier case -1:
319abff3868SMax Laier if (errno == EINTR || errno == EAGAIN)
320abff3868SMax Laier continue;
321abff3868SMax Laier case 0:
322abff3868SMax Laier return (1);
323abff3868SMax Laier default:
324abff3868SMax Laier pos += res;
325abff3868SMax Laier }
326abff3868SMax Laier }
327abff3868SMax Laier return (0);
328abff3868SMax Laier }
329abff3868SMax Laier
330abff3868SMax Laier /* Read data with the assertion that it all must come through, or
331abff3868SMax Laier * else abort the process. Based on atomicio() from openssh. */
332abff3868SMax Laier static void
must_read(int fd,void * buf,size_t n)333abff3868SMax Laier must_read(int fd, void *buf, size_t n)
334abff3868SMax Laier {
335abff3868SMax Laier char *s = buf;
336abff3868SMax Laier ssize_t res, pos = 0;
337abff3868SMax Laier
338abff3868SMax Laier while (n > pos) {
339abff3868SMax Laier res = read(fd, s + pos, n - pos);
340abff3868SMax Laier switch (res) {
341abff3868SMax Laier case -1:
342abff3868SMax Laier if (errno == EINTR || errno == EAGAIN)
343abff3868SMax Laier continue;
344abff3868SMax Laier case 0:
345abff3868SMax Laier _exit(0);
346abff3868SMax Laier default:
347abff3868SMax Laier pos += res;
348abff3868SMax Laier }
349abff3868SMax Laier }
350abff3868SMax Laier }
351abff3868SMax Laier
352abff3868SMax Laier /* Write data with the assertion that it all has to be written, or
353abff3868SMax Laier * else abort the process. Based on atomicio() from openssh. */
354abff3868SMax Laier static void
must_write(int fd,void * buf,size_t n)355abff3868SMax Laier must_write(int fd, void *buf, size_t n)
356abff3868SMax Laier {
357abff3868SMax Laier char *s = buf;
358abff3868SMax Laier ssize_t res, pos = 0;
359abff3868SMax Laier
360abff3868SMax Laier while (n > pos) {
361abff3868SMax Laier res = write(fd, s + pos, n - pos);
362abff3868SMax Laier switch (res) {
363abff3868SMax Laier case -1:
364abff3868SMax Laier if (errno == EINTR || errno == EAGAIN)
365abff3868SMax Laier continue;
366abff3868SMax Laier case 0:
367abff3868SMax Laier _exit(0);
368abff3868SMax Laier default:
369abff3868SMax Laier pos += res;
370abff3868SMax Laier }
371abff3868SMax Laier }
372abff3868SMax Laier }
373