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