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