xref: /openbsd/usr.sbin/tcpdump/privsep.c (revision 404b540a)
1 /*	$OpenBSD: privsep.c,v 1.28 2009/04/17 22:31:24 jmc 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 <netdb.h>
36 #include <paths.h>
37 #include <pwd.h>
38 #include <signal.h>
39 #include <stdarg.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <syslog.h>
44 #include <tzfile.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 };
65 
66 #define ALLOW(action)	(1 << (action))
67 
68 /*
69  * Set of maximum allowed actions.
70  */
71 static const int allowed_max[] = {
72 	/* INIT */	ALLOW(PRIV_OPEN_BPF) | ALLOW(PRIV_OPEN_DUMP) |
73 			ALLOW(PRIV_SETFILTER),
74 	/* BPF */	ALLOW(PRIV_SETFILTER),
75 	/* FILTER */	ALLOW(PRIV_OPEN_OUTPUT) | ALLOW(PRIV_GETSERVENTRIES) |
76 			ALLOW(PRIV_GETPROTOENTRIES) |
77 			ALLOW(PRIV_ETHER_NTOHOST) | ALLOW(PRIV_INIT_DONE),
78 	/* RUN */	ALLOW(PRIV_GETHOSTBYADDR) | ALLOW(PRIV_ETHER_NTOHOST) |
79 			ALLOW(PRIV_GETRPCBYNUMBER) | ALLOW(PRIV_GETLINES) |
80 			ALLOW(PRIV_LOCALTIME)
81 };
82 
83 /*
84  * Default set of allowed actions. More actions get added
85  * later depending on the supplied parameters.
86  */
87 static int allowed_ext[] = {
88 	/* INIT */	ALLOW(PRIV_SETFILTER),
89 	/* BPF */	ALLOW(PRIV_SETFILTER),
90 	/* FILTER */	ALLOW(PRIV_GETSERVENTRIES),
91 	/* RUN */	ALLOW(PRIV_GETLINES) | ALLOW(PRIV_LOCALTIME)
92 };
93 
94 struct ftab {
95 	char *name;
96 	int max;
97 	int count;
98 };
99 
100 static struct ftab file_table[] = {{"/etc/appletalk.names", 1, 0},
101 				   {PF_OSFP_FILE, 1, 0}};
102 
103 #define NUM_FILETAB (sizeof(file_table) / sizeof(struct ftab))
104 
105 int		debug_level = LOG_INFO;
106 int		priv_fd = -1;
107 volatile	pid_t child_pid = -1;
108 static volatile	sig_atomic_t cur_state = STATE_INIT;
109 
110 extern void	set_slave_signals(void);
111 
112 static void	impl_open_bpf(int, int *);
113 static void	impl_open_dump(int, const char *);
114 static void	impl_open_output(int, const char *);
115 static void	impl_setfilter(int, char *, int *);
116 static void	impl_init_done(int, int *);
117 static void	impl_gethostbyaddr(int);
118 static void	impl_ether_ntohost(int);
119 static void	impl_getrpcbynumber(int);
120 static void	impl_getserventries(int);
121 static void	impl_getprotoentries(int);
122 static void	impl_localtime(int fd);
123 static void	impl_getlines(int);
124 
125 static void	test_state(int, int);
126 static void	logmsg(int, const char *, ...);
127 
128 int
129 priv_init(int argc, char **argv)
130 {
131 	int bpfd = -1;
132 	int i, socks[2], cmd, nflag = 0;
133 	struct passwd *pw;
134 	uid_t uid;
135 	gid_t gid;
136 	char *cmdbuf, *infile = NULL;
137 	char *RFileName = NULL;
138 	char *WFileName = NULL;
139 	sigset_t allsigs, oset;
140 
141 	if (geteuid() != 0)
142 		errx(1, "need root privileges");
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 		/* Parent, drop privileges to _tcpdump */
161 		pw = getpwnam("_tcpdump");
162 		if (pw == NULL)
163 			errx(1, "unknown user _tcpdump");
164 
165 		/* chroot, drop privs and return */
166 		if (chroot(pw->pw_dir) != 0)
167 			err(1, "unable to chroot");
168 		if (chdir("/") != 0)
169 			err(1, "unable to chdir");
170 
171 		/* drop to _tcpdump */
172 		if (setgroups(1, &pw->pw_gid) == -1)
173 			err(1, "setgroups() failed");
174 		if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1)
175 			err(1, "setresgid() failed");
176 		if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1)
177 			err(1, "setresuid() failed");
178 		endpwent();
179 
180 		close(socks[0]);
181 		priv_fd = socks[1];
182 
183 		set_slave_signals();
184 		sigprocmask(SIG_SETMASK, &oset, NULL);
185 
186 		return (0);
187 	}
188 
189 	sigprocmask(SIG_SETMASK, &oset, NULL);
190 
191 	/* Child - drop suid privileges */
192 	gid = getgid();
193 	uid = getuid();
194 
195 	if (setresgid(gid, gid, gid) == -1)
196 		err(1, "setresgid() failed");
197 	if (setresuid(uid, uid, uid) == -1)
198 		err(1, "setresuid() failed");
199 
200 	/* parse the arguments for required options */
201 	opterr = 0;
202 	while ((i = getopt(argc, argv,
203 	    "ac:D:deE:fF:i:lLnNOopqr:s:StT:vw:xXy:Y")) != -1) {
204 		switch (i) {
205 		case 'n':
206 			nflag++;
207 			break;
208 
209 		case 'r':
210 			RFileName = optarg;
211 			break;
212 
213 		case 'w':
214 			WFileName = optarg;
215 			break;
216 
217 		case 'F':
218 			infile = optarg;
219 			break;
220 
221 		default:
222 			/* nothing */
223 			break;
224 		}
225 	}
226 
227 	if (RFileName != NULL) {
228 		if (strcmp(RFileName, "-") != 0)
229 			allowed_ext[STATE_INIT] |= ALLOW(PRIV_OPEN_DUMP);
230 	} else
231 		allowed_ext[STATE_INIT] |= ALLOW(PRIV_OPEN_BPF);
232 	if (WFileName != NULL) {
233 		if (strcmp(WFileName, "-") != 0)
234 			allowed_ext[STATE_FILTER] |= ALLOW(PRIV_OPEN_OUTPUT);
235 		else
236 			allowed_ext[STATE_FILTER] |= ALLOW(PRIV_INIT_DONE);
237 	} else
238 		allowed_ext[STATE_FILTER] |= ALLOW(PRIV_INIT_DONE);
239 	if (!nflag) {
240 		allowed_ext[STATE_RUN] |= ALLOW(PRIV_GETHOSTBYADDR);
241 		allowed_ext[STATE_FILTER] |= ALLOW(PRIV_ETHER_NTOHOST);
242 		allowed_ext[STATE_RUN] |= ALLOW(PRIV_ETHER_NTOHOST);
243 		allowed_ext[STATE_RUN] |= ALLOW(PRIV_GETRPCBYNUMBER);
244 		allowed_ext[STATE_FILTER] |= ALLOW(PRIV_GETPROTOENTRIES);
245 	}
246 
247 	if (infile)
248 		cmdbuf = read_infile(infile);
249 	else
250 		cmdbuf = copy_argv(&argv[optind]);
251 
252 	setproctitle("[priv]");
253 	close(socks[1]);
254 
255 	for (;;) {
256 		if (may_read(socks[0], &cmd, sizeof(int)))
257 			break;
258 		switch (cmd) {
259 		case PRIV_OPEN_BPF:
260 			test_state(cmd, STATE_BPF);
261 			impl_open_bpf(socks[0], &bpfd);
262 			break;
263 		case PRIV_OPEN_DUMP:
264 			test_state(cmd, STATE_BPF);
265 			impl_open_dump(socks[0], RFileName);
266 			break;
267 		case PRIV_OPEN_OUTPUT:
268 			test_state(cmd, STATE_RUN);
269 			impl_open_output(socks[0], WFileName);
270 			break;
271 		case PRIV_SETFILTER:
272 			test_state(cmd, STATE_FILTER);
273 			impl_setfilter(socks[0], cmdbuf, &bpfd);
274 			break;
275 		case PRIV_INIT_DONE:
276 			test_state(cmd, STATE_RUN);
277 			impl_init_done(socks[0], &bpfd);
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 		default:
308 			logmsg(LOG_ERR, "[priv]: unknown command %d", cmd);
309 			_exit(1);
310 			/* NOTREACHED */
311 		}
312 	}
313 
314 	/* NOTREACHED */
315 	_exit(0);
316 }
317 
318 static void
319 impl_open_bpf(int fd, int *bpfd)
320 {
321 	int snaplen, promisc, err;
322 	u_int dlt, dirfilt;
323 	char device[IFNAMSIZ];
324 	size_t iflen;
325 
326 	logmsg(LOG_DEBUG, "[priv]: msg PRIV_OPEN_BPF received");
327 
328 	must_read(fd, &snaplen, sizeof(int));
329 	must_read(fd, &promisc, sizeof(int));
330 	must_read(fd, &dlt, sizeof(u_int));
331 	must_read(fd, &dirfilt, sizeof(u_int));
332 	iflen = read_string(fd, device, sizeof(device), __func__);
333 	if (iflen == 0)
334 		errx(1, "Invalid interface size specified");
335 	*bpfd = pcap_live(device, snaplen, promisc, dlt, dirfilt);
336 	err = errno;
337 	if (*bpfd < 0)
338 		logmsg(LOG_DEBUG,
339 		    "[priv]: failed to open bpf device for %s: %s",
340 		    device, strerror(errno));
341 	send_fd(fd, *bpfd);
342 	must_write(fd, &err, sizeof(int));
343 	/* do not close bpfd until filter is set */
344 }
345 
346 static void
347 impl_open_dump(int fd, const char *RFileName)
348 {
349 	int file, err = 0;
350 
351 	logmsg(LOG_DEBUG, "[priv]: msg PRIV_OPEN_DUMP received");
352 
353 	if (RFileName == NULL) {
354 		file = -1;
355 		logmsg(LOG_ERR, "[priv]: No offline file specified");
356 	} else {
357 		file = open(RFileName, O_RDONLY, 0);
358 		err = errno;
359 		if (file < 0)
360 			logmsg(LOG_DEBUG, "[priv]: failed to open %s: %s",
361 			    RFileName, strerror(errno));
362 	}
363 	send_fd(fd, file);
364 	must_write(fd, &err, sizeof(int));
365 	if (file >= 0)
366 		close(file);
367 }
368 
369 static void
370 impl_open_output(int fd, const char *WFileName)
371 {
372 	int file, err;
373 
374 	logmsg(LOG_DEBUG, "[priv]: msg PRIV_OPEN_OUTPUT received");
375 
376 	file = open(WFileName, O_WRONLY|O_CREAT|O_TRUNC, 0666);
377 	err = errno;
378 	send_fd(fd, file);
379 	must_write(fd, &err, sizeof(int));
380 	if (file < 0)
381 		logmsg(LOG_DEBUG, "[priv]: failed to open %s: %s",
382 		    WFileName, strerror(err));
383 	else
384 		close(file);
385 }
386 
387 static void
388 impl_setfilter(int fd, char *cmdbuf, int *bpfd)
389 {
390 	logmsg(LOG_DEBUG, "[priv]: msg PRIV_SETFILTER received");
391 
392 	if (setfilter(*bpfd, fd, cmdbuf))
393 		logmsg(LOG_DEBUG, "[priv]: setfilter() failed");
394 	close(*bpfd);	/* done with bpf descriptor */
395 	*bpfd = -1;
396 }
397 
398 static void
399 impl_init_done(int fd, int *bpfd)
400 {
401 	int ret;
402 
403 	logmsg(LOG_DEBUG, "[priv]: msg PRIV_INIT_DONE received");
404 
405 	close(*bpfd);	/* done with bpf descriptor */
406 	*bpfd = -1;
407 	ret = 0;
408 	must_write(fd, &ret, sizeof(ret));
409 }
410 
411 static void
412 impl_gethostbyaddr(int fd)
413 {
414 	char hostname[MAXHOSTNAMELEN];
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[MAXHOSTNAMELEN];
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 = (char *)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 		if (lbuf != NULL) {
577 			free(lbuf);
578 			lbuf = NULL;
579 		}
580 	}
581 	write_zero(fd);
582 	fclose(fp);
583 }
584 
585 void
586 priv_init_done(void)
587 {
588 	int ret;
589 
590 	if (priv_fd < 0)
591 		errx(1, "%s: called from privileged portion", __func__);
592 
593 	write_command(priv_fd, PRIV_INIT_DONE);
594 	must_read(priv_fd, &ret, sizeof(int));
595 }
596 
597 /* Reverse address resolution; response is placed into res, and length of
598  * response is returned (zero on error) */
599 size_t
600 priv_gethostbyaddr(char *addr, size_t addr_len, int af, char *res, size_t res_len)
601 {
602 	if (priv_fd < 0)
603 		errx(1, "%s called from privileged portion", __func__);
604 
605 	write_command(priv_fd, PRIV_GETHOSTBYADDR);
606 	write_block(priv_fd, addr_len, addr);
607 	must_write(priv_fd, &af, sizeof(int));
608 
609 	return (read_string(priv_fd, res, res_len, __func__));
610 }
611 
612 size_t
613 priv_ether_ntohost(char *name, size_t name_len, struct ether_addr *e)
614 {
615 	if (priv_fd < 0)
616 		errx(1, "%s called from privileged portion", __func__);
617 
618 	write_command(priv_fd, PRIV_ETHER_NTOHOST);
619 	must_write(priv_fd, e, sizeof(*e));
620 
621 	/* Read the host name */
622 	return (read_string(priv_fd, name, name_len, __func__));
623 }
624 
625 size_t
626 priv_getrpcbynumber(int rpc, char *progname, size_t progname_len)
627 {
628 	if (priv_fd < 0)
629 		errx(1, "%s called from privileged portion", __func__);
630 
631 	write_command(priv_fd, PRIV_GETRPCBYNUMBER);
632 	must_write(priv_fd, &rpc, sizeof(int));
633 
634 	return read_string(priv_fd, progname, progname_len, __func__);
635 }
636 
637 /* start getting service entries */
638 void
639 priv_getserventries(void)
640 {
641 	if (priv_fd < 0)
642 		errx(1, "%s called from privileged portion", __func__);
643 
644 	write_command(priv_fd, PRIV_GETSERVENTRIES);
645 }
646 
647 /* retrieve a service entry, should be called repeatedly after calling
648    priv_getserventries(), until it returns zero. */
649 size_t
650 priv_getserventry(char *name, size_t name_len, int *port, char *prot,
651     size_t prot_len)
652 {
653 	if (priv_fd < 0)
654 		errx(1, "%s called from privileged portion", __func__);
655 
656 	/* read the service name */
657 	if (read_string(priv_fd, name, name_len, __func__) == 0)
658 		return 0;
659 
660 	/* read the port */
661 	must_read(priv_fd, port, sizeof(int));
662 
663 	/* read the protocol */
664 	return (read_string(priv_fd, prot, prot_len, __func__));
665 }
666 
667 /* start getting ip protocol entries */
668 void
669 priv_getprotoentries(void)
670 {
671 	if (priv_fd < 0)
672 		errx(1, "%s called from privileged portion", __func__);
673 
674 	write_command(priv_fd, PRIV_GETPROTOENTRIES);
675 }
676 
677 /* retrieve a ip protocol entry, should be called repeatedly after calling
678    priv_getprotoentries(), until it returns zero. */
679 size_t
680 priv_getprotoentry(char *name, size_t name_len, int *num)
681 {
682 	if (priv_fd < 0)
683 		errx(1, "%s called from privileged portion", __func__);
684 
685 	/* read the proto name */
686 	if (read_string(priv_fd, name, name_len, __func__) == 0)
687 		return 0;
688 
689 	/* read the num */
690 	must_read(priv_fd, num, sizeof(int));
691 
692 	return (1);
693 }
694 
695 /* localtime() replacement: ask the privileged process for localtime and
696  * gmtime, cache the localtime for about one minute i.e. until one of the
697  * fields other than seconds changes. The check is done using gmtime
698  * values since they are the same in parent and child. */
699 struct	tm *
700 priv_localtime(const time_t *t)
701 {
702 	static struct tm lt, gt0;
703 	static struct tm *gt = NULL;
704 	static char zone[TZ_MAX_CHARS];
705 
706 	if (gt != NULL) {
707 		gt = gmtime(t);
708 		gt0.tm_sec = gt->tm_sec;
709 		gt0.tm_zone = gt->tm_zone;
710 
711 		if (memcmp(gt, &gt0, sizeof(struct tm)) == 0) {
712 			lt.tm_sec = gt0.tm_sec;
713 			return &lt;
714 		}
715 	}
716 
717 	write_command(priv_fd, PRIV_LOCALTIME);
718 	must_write(priv_fd, t, sizeof(time_t));
719 	must_read(priv_fd, &lt, sizeof(lt));
720 	must_read(priv_fd, &gt0, sizeof(gt0));
721 
722 	if (read_string(priv_fd, zone, sizeof(zone), __func__))
723 		lt.tm_zone = zone;
724 	else
725 		lt.tm_zone = NULL;
726 
727 	gt0.tm_zone = NULL;
728 	gt = &gt0;
729 
730 	return &lt;
731 }
732 
733 /* start getting lines from a file */
734 void
735 priv_getlines(size_t sz)
736 {
737 	if (priv_fd < 0)
738 		errx(1, "%s called from privileged portion", __func__);
739 
740 	write_command(priv_fd, PRIV_GETLINES);
741 	must_write(priv_fd, &sz, sizeof(size_t));
742 }
743 
744 /* retrieve a line from a file, should be called repeatedly after calling
745    priv_getlines(), until it returns zero. */
746 size_t
747 priv_getline(char *line, size_t line_len)
748 {
749 	if (priv_fd < 0)
750 		errx(1, "%s called from privileged portion", __func__);
751 
752 	/* read the line */
753 	return (read_string(priv_fd, line, line_len, __func__));
754 }
755 
756 /* Read all data or return 1 for error. */
757 int
758 may_read(int fd, void *buf, size_t n)
759 {
760 	char *s = buf;
761 	ssize_t res, pos = 0;
762 
763 	while (n > pos) {
764 		res = read(fd, s + pos, n - pos);
765 		switch (res) {
766 		case -1:
767 			if (errno == EINTR || errno == EAGAIN)
768 				continue;
769 			/* FALLTHROUGH */
770 		case 0:
771 			return (1);
772 		default:
773 			pos += res;
774 		}
775 	}
776 	return (0);
777 }
778 
779 /* Read data with the assertion that it all must come through, or
780  * else abort the process.  Based on atomicio() from openssh. */
781 void
782 must_read(int fd, void *buf, size_t n)
783 {
784 	char *s = buf;
785 	ssize_t res, pos = 0;
786 
787 	while (n > pos) {
788 		res = read(fd, s + pos, n - pos);
789 		switch (res) {
790 		case -1:
791 			if (errno == EINTR || errno == EAGAIN)
792 				continue;
793 			/* FALLTHROUGH */
794 		case 0:
795 			_exit(0);
796 		default:
797 			pos += res;
798 		}
799 	}
800 }
801 
802 /* Write data with the assertion that it all has to be written, or
803  * else abort the process.  Based on atomicio() from openssh. */
804 void
805 must_write(int fd, const void *buf, size_t n)
806 {
807 	const char *s = buf;
808 	ssize_t res, pos = 0;
809 
810 	while (n > pos) {
811 		res = write(fd, s + pos, n - pos);
812 		switch (res) {
813 		case -1:
814 			if (errno == EINTR || errno == EAGAIN)
815 				continue;
816 			/* FALLTHROUGH */
817 		case 0:
818 			_exit(0);
819 		default:
820 			pos += res;
821 		}
822 	}
823 }
824 
825 /* test for a given state, and possibly increase state */
826 static void
827 test_state(int action, int next)
828 {
829 	if (cur_state < 0 || cur_state > STATE_RUN) {
830 		logmsg(LOG_ERR, "[priv] Invalid state: %d", cur_state);
831 		_exit(1);
832 	}
833 	if ((allowed_max[cur_state] & allowed_ext[cur_state]
834 	    & ALLOW(action)) == 0) {
835 		logmsg(LOG_ERR, "[priv] Invalid action %d in state %d",
836 		    action, cur_state);
837 		_exit(1);
838 	}
839 	if (next < cur_state) {
840 		logmsg(LOG_ERR, "[priv] Invalid next state: %d < %d",
841 		    next, cur_state);
842 		_exit(1);
843 	}
844 
845 	cur_state = next;
846 }
847 
848 static void
849 logmsg(int pri, const char *message, ...)
850 {
851 	va_list ap;
852 	if (pri > debug_level)
853 		return;
854 	va_start(ap, message);
855 
856 	vfprintf(stderr, message, ap);
857 	fprintf(stderr, "\n");
858 	va_end(ap);
859 }
860 
861 /* write a command to the peer */
862 void
863 write_command(int fd, int cmd)
864 {
865 	must_write(fd, &cmd, sizeof(cmd));
866 }
867 
868 /* write a zero 'length' to signal an error to read_{string|block} */
869 void
870 write_zero(int fd)
871 {
872 	size_t len = 0;
873 	must_write(fd, &len, sizeof(size_t));
874 }
875 
876 /* send a string */
877 void
878 write_string(int fd, const char *str)
879 {
880 	size_t len;
881 
882 	len = strlen(str) + 1;
883 	must_write(fd, &len, sizeof(size_t));
884 	must_write(fd, str, len);
885 }
886 
887 /* send a block of data of given size */
888 void
889 write_block(int fd, size_t size, const char *str)
890 {
891 	must_write(fd, &size, sizeof(size_t));
892 	must_write(fd, str, size);
893 }
894 
895 /* read a string from the channel, return 0 if error, or total size of
896  * the buffer, including the terminating '\0' */
897 size_t
898 read_string(int fd, char *buf, size_t size, const char *func)
899 {
900 	size_t len;
901 
902 	len = read_block(fd, buf, size, func);
903 	if (len == 0)
904 		return (0);
905 
906 	if (buf[len - 1] != '\0')
907 		errx(1, "%s: received invalid string", func);
908 
909 	return (len);
910 }
911 
912 /* read a block of data from the channel, return length of data, or 0
913  * if error */
914 size_t
915 read_block(int fd, char *buf, size_t size, const char *func)
916 {
917 	size_t len;
918 	/* Expect back an integer size, and then a string of that length */
919 	must_read(fd, &len, sizeof(size_t));
920 
921 	/* Check there was no error (indicated by a return of 0) */
922 	if (len == 0)
923 		return (0);
924 
925 	/* Make sure we aren't overflowing the passed in buffer */
926 	if (size < len)
927 		errx(1, "%s: overflow attempt in return", func);
928 
929 	/* Read the string and make sure we got all of it */
930 	must_read(fd, buf, len);
931 	return (len);
932 }
933