xref: /openbsd/usr.sbin/tcpdump/privsep.c (revision 9b7c3dbb)
1 /*	$OpenBSD: privsep.c,v 1.43 2016/07/25 02:35:26 deraadt 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 
20 #include <sys/types.h>
21 #include <sys/socket.h>
22 #include <sys/wait.h>
23 #include <sys/ioctl.h>
24 
25 #include <netinet/in.h>
26 #include <net/if.h>
27 #include <netinet/if_ether.h>
28 #include <net/bpf.h>
29 #include <net/pfvar.h>
30 
31 #include <rpc/rpc.h>
32 
33 #include <err.h>
34 #include <errno.h>
35 #include <fcntl.h>
36 #include <netdb.h>
37 #include <paths.h>
38 #include <pwd.h>
39 #include <signal.h>
40 #include <stdarg.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <syslog.h>
45 #include <unistd.h>
46 
47 #include "interface.h"
48 #include "privsep.h"
49 #include "pfctl_parser.h"
50 
51 /*
52  * tcpdump goes through four states: STATE_INIT is where the
53  * bpf device and the input file is opened. In STATE_BPF, the
54  * pcap filter gets set. STATE_FILTER is used for parsing
55  * /etc/services and /etc/protocols and opening the output
56  * file. STATE_RUN is the packet processing part.
57  */
58 
59 enum priv_state {
60 	STATE_INIT,		/* initial state */
61 	STATE_BPF,		/* input file/device opened */
62 	STATE_FILTER,		/* filter applied */
63 	STATE_RUN,		/* running and accepting network traffic */
64 	STATE_EXIT		/* in the process of dying */
65 };
66 
67 #define ALLOW(action)	(1 << (action))
68 
69 /*
70  * Set of maximum allowed actions.
71  */
72 static const int allowed_max[] = {
73 	/* INIT */	ALLOW(PRIV_OPEN_BPF) | ALLOW(PRIV_OPEN_DUMP) |
74 			ALLOW(PRIV_SETFILTER),
75 	/* BPF */	ALLOW(PRIV_SETFILTER),
76 	/* FILTER */	ALLOW(PRIV_OPEN_OUTPUT) | ALLOW(PRIV_GETSERVENTRIES) |
77 			ALLOW(PRIV_GETPROTOENTRIES) |
78 			ALLOW(PRIV_ETHER_NTOHOST) | ALLOW(PRIV_INIT_DONE),
79 	/* RUN */	ALLOW(PRIV_GETHOSTBYADDR) | ALLOW(PRIV_ETHER_NTOHOST) |
80 			ALLOW(PRIV_GETRPCBYNUMBER) | ALLOW(PRIV_GETLINES) |
81 			ALLOW(PRIV_LOCALTIME) | ALLOW(PRIV_PCAP_STATS),
82 	/* EXIT */	0
83 };
84 
85 /*
86  * Default set of allowed actions. More actions get added
87  * later depending on the supplied parameters.
88  */
89 static int allowed_ext[] = {
90 	/* INIT */	ALLOW(PRIV_SETFILTER),
91 	/* BPF */	ALLOW(PRIV_SETFILTER),
92 	/* FILTER */	ALLOW(PRIV_GETSERVENTRIES),
93 	/* RUN */	ALLOW(PRIV_GETLINES) | ALLOW(PRIV_LOCALTIME) |
94 			ALLOW(PRIV_PCAP_STATS),
95 	/* EXIT */	0
96 };
97 
98 struct ftab {
99 	char *name;
100 	int max;
101 	int count;
102 };
103 
104 static struct ftab file_table[] = {{"/etc/appletalk.names", 1, 0},
105 				   {PF_OSFP_FILE, 1, 0}};
106 
107 #define NUM_FILETAB (sizeof(file_table) / sizeof(struct ftab))
108 
109 int		debug_level = LOG_INFO;
110 int		priv_fd = -1;
111 volatile	pid_t child_pid = -1;
112 static volatile	sig_atomic_t cur_state = STATE_INIT;
113 
114 extern void	set_slave_signals(void);
115 
116 static void	impl_open_bpf(int, int *);
117 static void	impl_open_dump(int, const char *);
118 static void	impl_open_output(int, const char *);
119 static void	impl_setfilter(int, char *, int *);
120 static void	impl_init_done(int, int *);
121 static void	impl_gethostbyaddr(int);
122 static void	impl_ether_ntohost(int);
123 static void	impl_getrpcbynumber(int);
124 static void	impl_getserventries(int);
125 static void	impl_getprotoentries(int);
126 static void	impl_localtime(int fd);
127 static void	impl_getlines(int);
128 static void	impl_pcap_stats(int, int *);
129 
130 static void	test_state(int, int);
131 static void	logmsg(int, const char *, ...);
132 
133 int
134 priv_init(int argc, char **argv)
135 {
136 	int bpfd = -1;
137 	int i, socks[2], cmd, nflag = 0;
138 	struct passwd *pw;
139 	char *cmdbuf, *infile = NULL;
140 	char *RFileName = NULL;
141 	char *WFileName = NULL;
142 	sigset_t allsigs, oset;
143 
144 	closefrom(STDERR_FILENO + 1);
145 	for (i = 1; i < _NSIG; i++)
146 		signal(i, SIG_DFL);
147 
148 	/* Create sockets */
149 	if (socketpair(AF_LOCAL, SOCK_STREAM, PF_UNSPEC, socks) == -1)
150 		err(1, "socketpair() failed");
151 
152 	sigfillset(&allsigs);
153 	sigprocmask(SIG_BLOCK, &allsigs, &oset);
154 
155 	child_pid = fork();
156 	if (child_pid < 0)
157 		err(1, "fork() failed");
158 
159 	if (child_pid) {
160 		close(socks[0]);
161 		priv_fd = socks[1];
162 
163 		set_slave_signals();
164 		sigprocmask(SIG_SETMASK, &oset, NULL);
165 
166 		/*
167 		 * If run as regular user, packet parser will rely on
168 		 * pledge(2). If we are root, we want to chroot also..
169 		 */
170 		if (getuid() != 0)
171 			return (0);
172 
173 		pw = getpwnam("_tcpdump");
174 		if (pw == NULL)
175 			errx(1, "unknown user _tcpdump");
176 
177 		if (chroot(pw->pw_dir) == -1)
178 			err(1, "unable to chroot");
179 		if (chdir("/") == -1)
180 			err(1, "unable to chdir");
181 
182 		/* drop to _tcpdump */
183 		if (setgroups(1, &pw->pw_gid) == -1)
184 			err(1, "setgroups() failed");
185 		if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1)
186 			err(1, "setresgid() failed");
187 		if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1)
188 			err(1, "setresuid() failed");
189 
190 		return (0);
191 	}
192 
193 	sigprocmask(SIG_SETMASK, &oset, NULL);
194 	signal(SIGINT, SIG_IGN);
195 
196 	/* parse the arguments for required options */
197 	opterr = 0;
198 	while ((i = getopt(argc, argv,
199 	    "ac:D:deE:fF:i:lLnNOopqr:s:StT:vw:xXy:Y")) != -1) {
200 		switch (i) {
201 		case 'n':
202 			nflag++;
203 			break;
204 
205 		case 'r':
206 			RFileName = optarg;
207 			break;
208 
209 		case 'w':
210 			WFileName = optarg;
211 			break;
212 
213 		case 'F':
214 			infile = optarg;
215 			break;
216 
217 		default:
218 			/* nothing */
219 			break;
220 		}
221 	}
222 
223 	if (RFileName != NULL) {
224 		if (strcmp(RFileName, "-") != 0)
225 			allowed_ext[STATE_INIT] |= ALLOW(PRIV_OPEN_DUMP);
226 	} else
227 		allowed_ext[STATE_INIT] |= ALLOW(PRIV_OPEN_BPF);
228 	if (WFileName != NULL) {
229 		if (strcmp(WFileName, "-") != 0)
230 			allowed_ext[STATE_FILTER] |= ALLOW(PRIV_OPEN_OUTPUT);
231 		else
232 			allowed_ext[STATE_FILTER] |= ALLOW(PRIV_INIT_DONE);
233 	} else
234 		allowed_ext[STATE_FILTER] |= ALLOW(PRIV_INIT_DONE);
235 	if (!nflag) {
236 		allowed_ext[STATE_RUN] |= ALLOW(PRIV_GETHOSTBYADDR);
237 		allowed_ext[STATE_FILTER] |= ALLOW(PRIV_ETHER_NTOHOST);
238 		allowed_ext[STATE_RUN] |= ALLOW(PRIV_ETHER_NTOHOST);
239 		allowed_ext[STATE_RUN] |= ALLOW(PRIV_GETRPCBYNUMBER);
240 		allowed_ext[STATE_FILTER] |= ALLOW(PRIV_GETPROTOENTRIES);
241 	}
242 
243 	if (infile)
244 		cmdbuf = read_infile(infile);
245 	else
246 		cmdbuf = copy_argv(&argv[optind]);
247 
248 	setproctitle("[priv]");
249 	close(socks[1]);
250 
251 	for (;;) {
252 		if (may_read(socks[0], &cmd, sizeof(int)))
253 			break;
254 		switch (cmd) {
255 		case PRIV_OPEN_BPF:
256 			test_state(cmd, STATE_BPF);
257 			impl_open_bpf(socks[0], &bpfd);
258 			break;
259 		case PRIV_OPEN_DUMP:
260 			test_state(cmd, STATE_BPF);
261 			impl_open_dump(socks[0], RFileName);
262 			break;
263 		case PRIV_OPEN_OUTPUT:
264 			test_state(cmd, STATE_RUN);
265 			impl_open_output(socks[0], WFileName);
266 			break;
267 		case PRIV_SETFILTER:
268 			test_state(cmd, STATE_FILTER);
269 			impl_setfilter(socks[0], cmdbuf, &bpfd);
270 			break;
271 		case PRIV_INIT_DONE:
272 			test_state(cmd, STATE_RUN);
273 			impl_init_done(socks[0], &bpfd);
274 
275 			if (pledge("stdio rpath inet unix ioctl dns recvfd", NULL) == -1)
276 				err(1, "pledge");
277 
278 			break;
279 		case PRIV_GETHOSTBYADDR:
280 			test_state(cmd, STATE_RUN);
281 			impl_gethostbyaddr(socks[0]);
282 			break;
283 		case PRIV_ETHER_NTOHOST:
284 			test_state(cmd, cur_state);
285 			impl_ether_ntohost(socks[0]);
286 			break;
287 		case PRIV_GETRPCBYNUMBER:
288 			test_state(cmd, STATE_RUN);
289 			impl_getrpcbynumber(socks[0]);
290 			break;
291 		case PRIV_GETSERVENTRIES:
292 			test_state(cmd, STATE_FILTER);
293 			impl_getserventries(socks[0]);
294 			break;
295 		case PRIV_GETPROTOENTRIES:
296 			test_state(cmd, STATE_FILTER);
297 			impl_getprotoentries(socks[0]);
298 			break;
299 		case PRIV_LOCALTIME:
300 			test_state(cmd, STATE_RUN);
301 			impl_localtime(socks[0]);
302 			break;
303 		case PRIV_GETLINES:
304 			test_state(cmd, STATE_RUN);
305 			impl_getlines(socks[0]);
306 			break;
307 		case PRIV_PCAP_STATS:
308 			test_state(cmd, STATE_RUN);
309 			impl_pcap_stats(socks[0], &bpfd);
310 			break;
311 		default:
312 			logmsg(LOG_ERR, "[priv]: unknown command %d", cmd);
313 			_exit(1);
314 			/* NOTREACHED */
315 		}
316 	}
317 
318 	/* NOTREACHED */
319 	_exit(0);
320 }
321 
322 static void
323 impl_open_bpf(int fd, int *bpfd)
324 {
325 	int snaplen, promisc, err;
326 	u_int dlt, dirfilt;
327 	char device[IFNAMSIZ];
328 	size_t iflen;
329 
330 	logmsg(LOG_DEBUG, "[priv]: msg PRIV_OPEN_BPF received");
331 
332 	must_read(fd, &snaplen, sizeof(int));
333 	must_read(fd, &promisc, sizeof(int));
334 	must_read(fd, &dlt, sizeof(u_int));
335 	must_read(fd, &dirfilt, sizeof(u_int));
336 	iflen = read_string(fd, device, sizeof(device), __func__);
337 	if (iflen == 0)
338 		errx(1, "Invalid interface size specified");
339 	*bpfd = pcap_live(device, snaplen, promisc, dlt, dirfilt);
340 	err = errno;
341 	if (*bpfd < 0)
342 		logmsg(LOG_DEBUG,
343 		    "[priv]: failed to open bpf device for %s: %s",
344 		    device, strerror(errno));
345 	send_fd(fd, *bpfd);
346 	must_write(fd, &err, sizeof(int));
347 	/* do not close bpfd until filter is set */
348 }
349 
350 static void
351 impl_open_dump(int fd, const char *RFileName)
352 {
353 	int file, err = 0;
354 
355 	logmsg(LOG_DEBUG, "[priv]: msg PRIV_OPEN_DUMP received");
356 
357 	if (RFileName == NULL) {
358 		file = -1;
359 		logmsg(LOG_ERR, "[priv]: No offline file specified");
360 	} else {
361 		file = open(RFileName, O_RDONLY, 0);
362 		err = errno;
363 		if (file < 0)
364 			logmsg(LOG_DEBUG, "[priv]: failed to open %s: %s",
365 			    RFileName, strerror(errno));
366 	}
367 	send_fd(fd, file);
368 	must_write(fd, &err, sizeof(int));
369 	if (file >= 0)
370 		close(file);
371 }
372 
373 static void
374 impl_open_output(int fd, const char *WFileName)
375 {
376 	int file, err;
377 
378 	logmsg(LOG_DEBUG, "[priv]: msg PRIV_OPEN_OUTPUT received");
379 
380 	file = open(WFileName, O_WRONLY|O_CREAT|O_TRUNC, 0666);
381 	err = errno;
382 	send_fd(fd, file);
383 	must_write(fd, &err, sizeof(int));
384 	if (file < 0)
385 		logmsg(LOG_DEBUG, "[priv]: failed to open %s: %s",
386 		    WFileName, strerror(err));
387 	else
388 		close(file);
389 }
390 
391 static void
392 impl_setfilter(int fd, char *cmdbuf, int *bpfd)
393 {
394 	logmsg(LOG_DEBUG, "[priv]: msg PRIV_SETFILTER received");
395 
396 	if (setfilter(*bpfd, fd, cmdbuf))
397 		logmsg(LOG_DEBUG, "[priv]: setfilter() failed");
398 }
399 
400 static void
401 impl_init_done(int fd, int *bpfd)
402 {
403 	int ret;
404 
405 	logmsg(LOG_DEBUG, "[priv]: msg PRIV_INIT_DONE received");
406 
407 	ret = 0;
408 	must_write(fd, &ret, sizeof(ret));
409 }
410 
411 static void
412 impl_gethostbyaddr(int fd)
413 {
414 	char hostname[HOST_NAME_MAX+1];
415 	size_t hostname_len;
416 	int addr_af;
417 	struct hostent *hp;
418 
419 	logmsg(LOG_DEBUG, "[priv]: msg PRIV_GETHOSTBYADDR received");
420 
421 	/* Expecting: address block, address family */
422 	hostname_len = read_block(fd, hostname, sizeof(hostname), __func__);
423 	if (hostname_len == 0)
424 		_exit(1);
425 	must_read(fd, &addr_af, sizeof(int));
426 	hp = gethostbyaddr(hostname, hostname_len, addr_af);
427 	if (hp == NULL)
428 		write_zero(fd);
429 	else
430 		write_string(fd, hp->h_name);
431 }
432 
433 static void
434 impl_ether_ntohost(int fd)
435 {
436 	struct ether_addr ether;
437 	char hostname[HOST_NAME_MAX+1];
438 
439 	logmsg(LOG_DEBUG, "[priv]: msg PRIV_ETHER_NTOHOST received");
440 
441 	/* Expecting: ethernet address */
442 	must_read(fd, &ether, sizeof(ether));
443 	if (ether_ntohost(hostname, &ether) == -1)
444 		write_zero(fd);
445 	else
446 		write_string(fd, hostname);
447 }
448 
449 static void
450 impl_getrpcbynumber(int fd)
451 {
452 	int rpc;
453 	struct rpcent *rpce;
454 
455 	logmsg(LOG_DEBUG, "[priv]: msg PRIV_GETRPCBYNUMBER received");
456 
457 	must_read(fd, &rpc, sizeof(int));
458 	rpce = getrpcbynumber(rpc);
459 	if (rpce == NULL)
460 		write_zero(fd);
461 	else
462 		write_string(fd, rpce->r_name);
463 }
464 
465 static void
466 impl_getserventries(int fd)
467 {
468 	struct servent *sp;
469 
470 	logmsg(LOG_DEBUG, "[priv]: msg PRIV_GETSERVENTRIES received");
471 
472 	for (;;) {
473 		sp = getservent();
474 		if (sp == NULL) {
475 			write_zero(fd);
476 			break;
477 		} else {
478 			write_string(fd, sp->s_name);
479 			must_write(fd, &sp->s_port, sizeof(int));
480 			write_string(fd, sp->s_proto);
481 		}
482 	}
483 	endservent();
484 }
485 
486 static void
487 impl_getprotoentries(int fd)
488 {
489 	struct protoent *pe;
490 
491 	logmsg(LOG_DEBUG, "[priv]: msg PRIV_GETPROTOENTRIES received");
492 
493 	for (;;) {
494 		pe = getprotoent();
495 		if (pe == NULL) {
496 			write_zero(fd);
497 			break;
498 		} else {
499 			write_string(fd, pe->p_name);
500 			must_write(fd, &pe->p_proto, sizeof(int));
501 		}
502 	}
503 	endprotoent();
504 }
505 
506 /* read the time and send the corresponding localtime and gmtime
507  * results back to the unprivileged process */
508 static void
509 impl_localtime(int fd)
510 {
511 	struct tm *lt, *gt;
512 	time_t t;
513 
514 	logmsg(LOG_DEBUG, "[priv]: msg PRIV_LOCALTIME received");
515 
516 	must_read(fd, &t, sizeof(time_t));
517 
518 	/* this must be done separately, since they apparently use the
519 	 * same local buffer */
520 	if ((lt = localtime(&t)) == NULL)
521 		errx(1, "localtime()");
522 	must_write(fd, lt, sizeof(*lt));
523 
524 	if ((gt = gmtime(&t)) == NULL)
525 		errx(1, "gmtime()");
526 	must_write(fd, gt, sizeof(*gt));
527 
528 	if (lt->tm_zone == NULL)
529 		write_zero(fd);
530 	else
531 		write_string(fd, lt->tm_zone);
532 }
533 
534 static void
535 impl_getlines(int fd)
536 {
537 	FILE *fp;
538 	char *buf, *lbuf, *file;
539 	size_t len, fid;
540 
541 	logmsg(LOG_DEBUG, "[priv]: msg PRIV_GETLINES received");
542 
543 	must_read(fd, &fid, sizeof(size_t));
544 	if (fid >= NUM_FILETAB)
545 		errx(1, "invalid file id");
546 
547 	file = file_table[fid].name;
548 
549 	if (file == NULL)
550 		errx(1, "invalid file referenced");
551 
552 	if (file_table[fid].count >= file_table[fid].max)
553 		errx(1, "maximum open count exceeded for %s", file);
554 
555 	file_table[fid].count++;
556 
557 	if ((fp = fopen(file, "r")) == NULL) {
558 		write_zero(fd);
559 		return;
560 	}
561 
562 	lbuf = NULL;
563 	while ((buf = fgetln(fp, &len))) {
564 		if (buf[len - 1] == '\n')
565 			buf[len - 1] = '\0';
566 		else {
567 			if ((lbuf = malloc(len + 1)) == NULL)
568 				err(1, NULL);
569 			memcpy(lbuf, buf, len);
570 			lbuf[len] = '\0';
571 			buf = lbuf;
572 		}
573 
574 		write_string(fd, buf);
575 
576 		free(lbuf);
577 		lbuf = NULL;
578 	}
579 	write_zero(fd);
580 	fclose(fp);
581 }
582 
583 static void
584 impl_pcap_stats(int fd, int *bpfd)
585 {
586 	struct pcap_stat stats;
587 
588 	logmsg(LOG_DEBUG, "[priv]: msg PRIV_PCAP_STATS received");
589 
590 	if (ioctl(*bpfd, BIOCGSTATS, &stats) == -1)
591 		write_zero(fd);
592 	else
593 		must_write(fd, &stats, sizeof(stats));
594 }
595 
596 void
597 priv_init_done(void)
598 {
599 	int ret;
600 
601 	if (priv_fd < 0)
602 		errx(1, "%s: called from privileged portion", __func__);
603 
604 	write_command(priv_fd, PRIV_INIT_DONE);
605 	must_read(priv_fd, &ret, sizeof(int));
606 }
607 
608 /* Reverse address resolution; response is placed into res, and length of
609  * response is returned (zero on error) */
610 size_t
611 priv_gethostbyaddr(char *addr, size_t addr_len, int af, char *res, size_t res_len)
612 {
613 	if (priv_fd < 0)
614 		errx(1, "%s called from privileged portion", __func__);
615 
616 	write_command(priv_fd, PRIV_GETHOSTBYADDR);
617 	write_block(priv_fd, addr_len, addr);
618 	must_write(priv_fd, &af, sizeof(int));
619 
620 	return (read_string(priv_fd, res, res_len, __func__));
621 }
622 
623 size_t
624 priv_ether_ntohost(char *name, size_t name_len, struct ether_addr *e)
625 {
626 	if (priv_fd < 0)
627 		errx(1, "%s called from privileged portion", __func__);
628 
629 	write_command(priv_fd, PRIV_ETHER_NTOHOST);
630 	must_write(priv_fd, e, sizeof(*e));
631 
632 	/* Read the host name */
633 	return (read_string(priv_fd, name, name_len, __func__));
634 }
635 
636 size_t
637 priv_getrpcbynumber(int rpc, char *progname, size_t progname_len)
638 {
639 	if (priv_fd < 0)
640 		errx(1, "%s called from privileged portion", __func__);
641 
642 	write_command(priv_fd, PRIV_GETRPCBYNUMBER);
643 	must_write(priv_fd, &rpc, sizeof(int));
644 
645 	return read_string(priv_fd, progname, progname_len, __func__);
646 }
647 
648 /* start getting service entries */
649 void
650 priv_getserventries(void)
651 {
652 	if (priv_fd < 0)
653 		errx(1, "%s called from privileged portion", __func__);
654 
655 	write_command(priv_fd, PRIV_GETSERVENTRIES);
656 }
657 
658 /* retrieve a service entry, should be called repeatedly after calling
659    priv_getserventries(), until it returns zero. */
660 size_t
661 priv_getserventry(char *name, size_t name_len, int *port, char *prot,
662     size_t prot_len)
663 {
664 	if (priv_fd < 0)
665 		errx(1, "%s called from privileged portion", __func__);
666 
667 	/* read the service name */
668 	if (read_string(priv_fd, name, name_len, __func__) == 0)
669 		return 0;
670 
671 	/* read the port */
672 	must_read(priv_fd, port, sizeof(int));
673 
674 	/* read the protocol */
675 	return (read_string(priv_fd, prot, prot_len, __func__));
676 }
677 
678 /* start getting ip protocol entries */
679 void
680 priv_getprotoentries(void)
681 {
682 	if (priv_fd < 0)
683 		errx(1, "%s called from privileged portion", __func__);
684 
685 	write_command(priv_fd, PRIV_GETPROTOENTRIES);
686 }
687 
688 /* retrieve a ip protocol entry, should be called repeatedly after calling
689    priv_getprotoentries(), until it returns zero. */
690 size_t
691 priv_getprotoentry(char *name, size_t name_len, int *num)
692 {
693 	if (priv_fd < 0)
694 		errx(1, "%s called from privileged portion", __func__);
695 
696 	/* read the proto name */
697 	if (read_string(priv_fd, name, name_len, __func__) == 0)
698 		return 0;
699 
700 	/* read the num */
701 	must_read(priv_fd, num, sizeof(int));
702 
703 	return (1);
704 }
705 
706 /* localtime() replacement: ask the privileged process for localtime and
707  * gmtime, cache the localtime for about one minute i.e. until one of the
708  * fields other than seconds changes. The check is done using gmtime
709  * values since they are the same in parent and child. */
710 struct	tm *
711 priv_localtime(const time_t *t)
712 {
713 	static struct tm lt, gt0;
714 	static struct tm *gt = NULL;
715 	static char zone[PATH_MAX];
716 
717 	if (gt != NULL) {
718 		gt = gmtime(t);
719 		gt0.tm_sec = gt->tm_sec;
720 		gt0.tm_zone = gt->tm_zone;
721 
722 		if (memcmp(gt, &gt0, sizeof(struct tm)) == 0) {
723 			lt.tm_sec = gt0.tm_sec;
724 			return &lt;
725 		}
726 	}
727 
728 	write_command(priv_fd, PRIV_LOCALTIME);
729 	must_write(priv_fd, t, sizeof(time_t));
730 	must_read(priv_fd, &lt, sizeof(lt));
731 	must_read(priv_fd, &gt0, sizeof(gt0));
732 
733 	if (read_string(priv_fd, zone, sizeof(zone), __func__))
734 		lt.tm_zone = zone;
735 	else
736 		lt.tm_zone = NULL;
737 
738 	gt0.tm_zone = NULL;
739 	gt = &gt0;
740 
741 	return &lt;
742 }
743 
744 /* start getting lines from a file */
745 void
746 priv_getlines(size_t sz)
747 {
748 	if (priv_fd < 0)
749 		errx(1, "%s called from privileged portion", __func__);
750 
751 	write_command(priv_fd, PRIV_GETLINES);
752 	must_write(priv_fd, &sz, sizeof(size_t));
753 }
754 
755 int
756 priv_pcap_stats(struct pcap_stat *ps)
757 {
758 	if (priv_fd < 0)
759 		errx(1, "%s: called from privileged portion", __func__);
760 
761 	write_command(priv_fd, PRIV_PCAP_STATS);
762 	must_read(priv_fd, ps, sizeof(*ps));
763 	return (0);
764 }
765 
766 /* retrieve a line from a file, should be called repeatedly after calling
767    priv_getlines(), until it returns zero. */
768 size_t
769 priv_getline(char *line, size_t line_len)
770 {
771 	if (priv_fd < 0)
772 		errx(1, "%s called from privileged portion", __func__);
773 
774 	/* read the line */
775 	return (read_string(priv_fd, line, line_len, __func__));
776 }
777 
778 /* Read all data or return 1 for error. */
779 int
780 may_read(int fd, void *buf, size_t n)
781 {
782 	char *s = buf;
783 	ssize_t res, pos = 0;
784 
785 	while (n > pos) {
786 		res = read(fd, s + pos, n - pos);
787 		switch (res) {
788 		case -1:
789 			if (errno == EINTR || errno == EAGAIN)
790 				continue;
791 			/* FALLTHROUGH */
792 		case 0:
793 			return (1);
794 		default:
795 			pos += res;
796 		}
797 	}
798 	return (0);
799 }
800 
801 /* Read data with the assertion that it all must come through, or
802  * else abort the process.  Based on atomicio() from openssh. */
803 void
804 must_read(int fd, void *buf, size_t n)
805 {
806 	char *s = buf;
807 	ssize_t res, pos = 0;
808 
809 	while (n > pos) {
810 		res = read(fd, s + pos, n - pos);
811 		switch (res) {
812 		case -1:
813 			if (errno == EINTR || errno == EAGAIN)
814 				continue;
815 			/* FALLTHROUGH */
816 		case 0:
817 			_exit(0);
818 		default:
819 			pos += res;
820 		}
821 	}
822 }
823 
824 /* Write data with the assertion that it all has to be written, or
825  * else abort the process.  Based on atomicio() from openssh. */
826 void
827 must_write(int fd, const void *buf, size_t n)
828 {
829 	const char *s = buf;
830 	ssize_t res, pos = 0;
831 
832 	while (n > pos) {
833 		res = write(fd, s + pos, n - pos);
834 		switch (res) {
835 		case -1:
836 			if (errno == EINTR || errno == EAGAIN)
837 				continue;
838 			/* FALLTHROUGH */
839 		case 0:
840 			_exit(0);
841 		default:
842 			pos += res;
843 		}
844 	}
845 }
846 
847 /* test for a given state, and possibly increase state */
848 static void
849 test_state(int action, int next)
850 {
851 	if (cur_state < 0 || cur_state > STATE_RUN) {
852 		logmsg(LOG_ERR, "[priv] Invalid state: %d", cur_state);
853 		_exit(1);
854 	}
855 	if ((allowed_max[cur_state] & allowed_ext[cur_state]
856 	    & ALLOW(action)) == 0) {
857 		logmsg(LOG_ERR, "[priv] Invalid action %d in state %d",
858 		    action, cur_state);
859 		_exit(1);
860 	}
861 	if (next < cur_state) {
862 		logmsg(LOG_ERR, "[priv] Invalid next state: %d < %d",
863 		    next, cur_state);
864 		_exit(1);
865 	}
866 
867 	cur_state = next;
868 }
869 
870 static void
871 logmsg(int pri, const char *message, ...)
872 {
873 	va_list ap;
874 	if (pri > debug_level)
875 		return;
876 	va_start(ap, message);
877 
878 	vfprintf(stderr, message, ap);
879 	fprintf(stderr, "\n");
880 	va_end(ap);
881 }
882 
883 /* write a command to the peer */
884 void
885 write_command(int fd, int cmd)
886 {
887 	must_write(fd, &cmd, sizeof(cmd));
888 }
889 
890 /* write a zero 'length' to signal an error to read_{string|block} */
891 void
892 write_zero(int fd)
893 {
894 	size_t len = 0;
895 	must_write(fd, &len, sizeof(size_t));
896 }
897 
898 /* send a string */
899 void
900 write_string(int fd, const char *str)
901 {
902 	size_t len;
903 
904 	len = strlen(str) + 1;
905 	must_write(fd, &len, sizeof(size_t));
906 	must_write(fd, str, len);
907 }
908 
909 /* send a block of data of given size */
910 void
911 write_block(int fd, size_t size, const char *str)
912 {
913 	must_write(fd, &size, sizeof(size_t));
914 	must_write(fd, str, size);
915 }
916 
917 /* read a string from the channel, return 0 if error, or total size of
918  * the buffer, including the terminating '\0' */
919 size_t
920 read_string(int fd, char *buf, size_t size, const char *func)
921 {
922 	size_t len;
923 
924 	len = read_block(fd, buf, size, func);
925 	if (len == 0)
926 		return (0);
927 
928 	if (buf[len - 1] != '\0')
929 		errx(1, "%s: received invalid string", func);
930 
931 	return (len);
932 }
933 
934 /* read a block of data from the channel, return length of data, or 0
935  * if error */
936 size_t
937 read_block(int fd, char *buf, size_t size, const char *func)
938 {
939 	size_t len;
940 	/* Expect back an integer size, and then a string of that length */
941 	must_read(fd, &len, sizeof(size_t));
942 
943 	/* Check there was no error (indicated by a return of 0) */
944 	if (len == 0)
945 		return (0);
946 
947 	/* Make sure we aren't overflowing the passed in buffer */
948 	if (size < len)
949 		errx(1, "%s: overflow attempt in return", func);
950 
951 	/* Read the string and make sure we got all of it */
952 	must_read(fd, buf, len);
953 	return (len);
954 }
955