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