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, ðer, sizeof(ether)); 443 if (ether_ntohost(hostname, ðer) == -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, >0, sizeof(struct tm)) == 0) { 712 lt.tm_sec = gt0.tm_sec; 713 return < 714 } 715 } 716 717 write_command(priv_fd, PRIV_LOCALTIME); 718 must_write(priv_fd, t, sizeof(time_t)); 719 must_read(priv_fd, <, sizeof(lt)); 720 must_read(priv_fd, >0, 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 = >0; 729 730 return < 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