1 /* $OpenBSD: pflogd.c,v 1.62 2019/07/25 17:32:33 brynet Exp $ */ 2 3 /* 4 * Copyright (c) 2001 Theo de Raadt 5 * Copyright (c) 2001 Can Erkin Acar 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * - Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * - Redistributions in binary form must reproduce the above 15 * copyright notice, this list of conditions and the following 16 * disclaimer in the documentation and/or other materials provided 17 * with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 22 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 23 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 25 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 26 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 29 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 * POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 #include <sys/types.h> 34 #include <sys/ioctl.h> 35 #include <sys/stat.h> 36 #include <sys/socket.h> 37 #include <net/if.h> 38 #include <stdio.h> 39 #include <stdlib.h> 40 #include <string.h> 41 #include <unistd.h> 42 #include <pcap-int.h> 43 #include <pcap.h> 44 #include <syslog.h> 45 #include <signal.h> 46 #include <err.h> 47 #include <errno.h> 48 #include <stdarg.h> 49 #include <fcntl.h> 50 #include <util.h> 51 #include "pflogd.h" 52 53 pcap_t *hpcap; 54 static FILE *dpcap; 55 56 int Debug = 0; 57 static int snaplen = DEF_SNAPLEN; 58 static int cur_snaplen = DEF_SNAPLEN; 59 60 volatile sig_atomic_t gotsig_close, gotsig_alrm, gotsig_hup; 61 62 char *filename = PFLOGD_LOG_FILE; 63 char *interface = PFLOGD_DEFAULT_IF; 64 char *filter = NULL; 65 66 char errbuf[PCAP_ERRBUF_SIZE]; 67 68 int log_debug = 0; 69 unsigned int delay = FLUSH_DELAY; 70 71 char *copy_argv(char * const *); 72 void dump_packet(u_char *, const struct pcap_pkthdr *, const u_char *); 73 void dump_packet_nobuf(u_char *, const struct pcap_pkthdr *, const u_char *); 74 int flush_buffer(FILE *); 75 int if_exists(char *); 76 pcap_t *pflog_read_live(const char *, int, int, int, char *); 77 void logmsg(int, const char *, ...); 78 void purge_buffer(void); 79 int reset_dump(void); 80 int scan_dump(FILE *, off_t); 81 int set_snaplen(int); 82 void set_suspended(int); 83 void sig_alrm(int); 84 void sig_close(int); 85 void sig_hup(int); 86 void usage(void); 87 88 /* buffer must always be greater than snaplen */ 89 static int bufpkt = 0; /* number of packets in buffer */ 90 static int buflen = 0; /* allocated size of buffer */ 91 static char *buffer = NULL; /* packet buffer */ 92 static char *bufpos = NULL; /* position in buffer */ 93 static int bufleft = 0; /* bytes left in buffer */ 94 95 /* if error, stop logging but count dropped packets */ 96 static int suspended = -1; 97 static long packets_dropped = 0; 98 99 void 100 set_suspended(int s) 101 { 102 if (suspended == s) 103 return; 104 105 suspended = s; 106 setproctitle("[%s] -s %d -i %s -f %s", 107 suspended ? "suspended" : "running", 108 cur_snaplen, interface, filename); 109 } 110 111 char * 112 copy_argv(char * const *argv) 113 { 114 size_t len = 0, n; 115 char *buf; 116 117 if (argv == NULL) 118 return (NULL); 119 120 for (n = 0; argv[n]; n++) 121 len += strlen(argv[n])+1; 122 if (len == 0) 123 return (NULL); 124 125 buf = malloc(len); 126 if (buf == NULL) 127 return (NULL); 128 129 strlcpy(buf, argv[0], len); 130 for (n = 1; argv[n]; n++) { 131 strlcat(buf, " ", len); 132 strlcat(buf, argv[n], len); 133 } 134 return (buf); 135 } 136 137 void 138 logmsg(int pri, const char *message, ...) 139 { 140 va_list ap; 141 va_start(ap, message); 142 143 if (log_debug) { 144 vfprintf(stderr, message, ap); 145 fprintf(stderr, "\n"); 146 } else 147 vsyslog(pri, message, ap); 148 va_end(ap); 149 } 150 151 __dead void 152 usage(void) 153 { 154 fprintf(stderr, "usage: pflogd [-Dx] [-d delay] [-f filename]"); 155 fprintf(stderr, " [-i interface] [-s snaplen]\n"); 156 fprintf(stderr, " [expression]\n"); 157 exit(1); 158 } 159 160 void 161 sig_close(int sig) 162 { 163 gotsig_close = 1; 164 } 165 166 void 167 sig_hup(int sig) 168 { 169 gotsig_hup = 1; 170 } 171 172 void 173 sig_alrm(int sig) 174 { 175 gotsig_alrm = 1; 176 } 177 178 void 179 set_pcap_filter(void) 180 { 181 struct bpf_program bprog; 182 183 if (pcap_compile(hpcap, &bprog, filter, PCAP_OPT_FIL, 0) < 0) 184 logmsg(LOG_WARNING, "%s", pcap_geterr(hpcap)); 185 else { 186 if (pcap_setfilter(hpcap, &bprog) < 0) 187 logmsg(LOG_WARNING, "%s", pcap_geterr(hpcap)); 188 pcap_freecode(&bprog); 189 } 190 } 191 192 int 193 if_exists(char *ifname) 194 { 195 return (if_nametoindex(ifname) != 0); 196 } 197 198 pcap_t * 199 pflog_read_live(const char *source, int slen, int promisc, int to_ms, 200 char *ebuf) 201 { 202 int fd; 203 struct bpf_version bv; 204 struct ifreq ifr; 205 u_int v, dlt = DLT_PFLOG; 206 pcap_t *p; 207 208 if (source == NULL || slen <= 0) 209 return (NULL); 210 211 p = pcap_create(source, ebuf); 212 if (p == NULL) 213 return (NULL); 214 215 /* Open bpf(4) read only */ 216 if ((fd = open("/dev/bpf", O_RDONLY)) == -1) 217 return (NULL); 218 219 if (ioctl(fd, BIOCVERSION, &bv) == -1) { 220 snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCVERSION: %s", 221 pcap_strerror(errno)); 222 goto bad; 223 } 224 225 if (bv.bv_major != BPF_MAJOR_VERSION || 226 bv.bv_minor < BPF_MINOR_VERSION) { 227 snprintf(ebuf, PCAP_ERRBUF_SIZE, 228 "kernel bpf filter out of date"); 229 goto bad; 230 } 231 232 strlcpy(ifr.ifr_name, source, sizeof(ifr.ifr_name)); 233 if (ioctl(fd, BIOCSETIF, &ifr) == -1) { 234 snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCSETIF: %s", 235 pcap_strerror(errno)); 236 goto bad; 237 } 238 239 if (dlt != (u_int) -1 && ioctl(fd, BIOCSDLT, &dlt)) { 240 snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCSDLT: %s", 241 pcap_strerror(errno)); 242 goto bad; 243 } 244 245 p->fd = fd; 246 p->snapshot = slen; 247 p->linktype = dlt; 248 249 /* set timeout */ 250 if (to_ms != 0) { 251 struct timeval to; 252 to.tv_sec = to_ms / 1000; 253 to.tv_usec = (to_ms * 1000) % 1000000; 254 if (ioctl(p->fd, BIOCSRTIMEOUT, &to) == -1) { 255 snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCSRTIMEOUT: %s", 256 pcap_strerror(errno)); 257 goto bad; 258 } 259 } 260 if (promisc) 261 /* this is allowed to fail */ 262 ioctl(fd, BIOCPROMISC, NULL); 263 264 if (ioctl(fd, BIOCGBLEN, &v) == -1) { 265 snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCGBLEN: %s", 266 pcap_strerror(errno)); 267 goto bad; 268 } 269 p->bufsize = v; 270 p->buffer = malloc(p->bufsize); 271 if (p->buffer == NULL) { 272 snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s", 273 pcap_strerror(errno)); 274 goto bad; 275 } 276 p->activated = 1; 277 return (p); 278 279 bad: 280 pcap_close(p); 281 return (NULL); 282 } 283 284 int 285 init_pcap(void) 286 { 287 hpcap = pflog_read_live(interface, snaplen, 1, PCAP_TO_MS, errbuf); 288 if (hpcap == NULL) { 289 logmsg(LOG_ERR, "Failed to initialize: %s", errbuf); 290 return (-1); 291 } 292 293 if (pcap_datalink(hpcap) != DLT_PFLOG) { 294 logmsg(LOG_ERR, "Invalid datalink type"); 295 pcap_close(hpcap); 296 hpcap = NULL; 297 return (-1); 298 } 299 300 set_pcap_filter(); 301 302 /* lock */ 303 if (ioctl(pcap_fileno(hpcap), BIOCLOCK) == -1) { 304 logmsg(LOG_ERR, "BIOCLOCK: %s", strerror(errno)); 305 return (-1); 306 } 307 308 return (0); 309 } 310 311 int 312 set_snaplen(int snap) 313 { 314 if (priv_set_snaplen(snap)) 315 return (1); 316 317 if (cur_snaplen > snap) 318 purge_buffer(); 319 320 cur_snaplen = snap; 321 322 return (0); 323 } 324 325 int 326 reset_dump(void) 327 { 328 struct pcap_file_header hdr; 329 struct stat st; 330 int fd; 331 FILE *fp; 332 333 if (hpcap == NULL) 334 return (-1); 335 336 if (dpcap) { 337 flush_buffer(dpcap); 338 fclose(dpcap); 339 dpcap = NULL; 340 } 341 342 /* 343 * Basically reimplement pcap_dump_open() because it truncates 344 * files and duplicates headers and such. 345 */ 346 fd = priv_open_log(); 347 if (fd < 0) 348 return (-1); 349 350 fp = fdopen(fd, "a+"); 351 352 if (fp == NULL) { 353 logmsg(LOG_ERR, "Error: %s: %s", filename, strerror(errno)); 354 close(fd); 355 return (-1); 356 } 357 if (fstat(fileno(fp), &st) == -1) { 358 logmsg(LOG_ERR, "Error: %s: %s", filename, strerror(errno)); 359 fclose(fp); 360 return (-1); 361 } 362 363 /* set FILE unbuffered, we do our own buffering */ 364 if (setvbuf(fp, NULL, _IONBF, 0)) { 365 logmsg(LOG_ERR, "Failed to set output buffers"); 366 fclose(fp); 367 return (-1); 368 } 369 370 #define TCPDUMP_MAGIC 0xa1b2c3d4 371 372 if (st.st_size == 0) { 373 if (snaplen != cur_snaplen) { 374 logmsg(LOG_NOTICE, "Using snaplen %d", snaplen); 375 if (set_snaplen(snaplen)) 376 logmsg(LOG_WARNING, 377 "Failed, using old settings"); 378 } 379 hdr.magic = TCPDUMP_MAGIC; 380 hdr.version_major = PCAP_VERSION_MAJOR; 381 hdr.version_minor = PCAP_VERSION_MINOR; 382 hdr.thiszone = hpcap->tzoff; 383 hdr.snaplen = hpcap->snapshot; 384 hdr.sigfigs = 0; 385 hdr.linktype = hpcap->linktype; 386 387 if (fwrite((char *)&hdr, sizeof(hdr), 1, fp) != 1) { 388 fclose(fp); 389 return (-1); 390 } 391 } else if (scan_dump(fp, st.st_size)) { 392 fclose(fp); 393 logmsg(LOG_ERR, 394 "Invalid/incompatible log file, move it away"); 395 return (-1); 396 } 397 398 dpcap = fp; 399 400 set_suspended(0); 401 flush_buffer(fp); 402 403 return (0); 404 } 405 406 int 407 scan_dump(FILE *fp, off_t size) 408 { 409 struct pcap_file_header hdr; 410 struct pcap_pkthdr ph; 411 off_t pos; 412 413 /* 414 * Must read the file, compare the header against our new 415 * options (in particular, snaplen) and adjust our options so 416 * that we generate a correct file. Furthermore, check the file 417 * for consistency so that we can append safely. 418 * 419 * XXX this may take a long time for large logs. 420 */ 421 (void) fseek(fp, 0L, SEEK_SET); 422 423 if (fread((char *)&hdr, sizeof(hdr), 1, fp) != 1) { 424 logmsg(LOG_ERR, "Short file header"); 425 return (1); 426 } 427 428 if (hdr.magic != TCPDUMP_MAGIC || 429 hdr.version_major != PCAP_VERSION_MAJOR || 430 hdr.version_minor != PCAP_VERSION_MINOR || 431 hdr.linktype != hpcap->linktype || 432 hdr.snaplen > PFLOGD_MAXSNAPLEN) { 433 return (1); 434 } 435 436 pos = sizeof(hdr); 437 438 while (!feof(fp)) { 439 off_t len = fread((char *)&ph, 1, sizeof(ph), fp); 440 if (len == 0) 441 break; 442 443 if (len != sizeof(ph)) 444 goto error; 445 if (ph.caplen > hdr.snaplen || ph.caplen > PFLOGD_MAXSNAPLEN) 446 goto error; 447 pos += sizeof(ph) + ph.caplen; 448 if (pos > size) 449 goto error; 450 fseek(fp, ph.caplen, SEEK_CUR); 451 } 452 453 if (pos != size) 454 goto error; 455 456 if (hdr.snaplen != cur_snaplen) { 457 logmsg(LOG_WARNING, 458 "Existing file has different snaplen %u, using it", 459 hdr.snaplen); 460 if (set_snaplen(hdr.snaplen)) { 461 logmsg(LOG_WARNING, 462 "Failed, using old settings, offset %llu", 463 (unsigned long long) size); 464 } 465 } 466 467 return (0); 468 469 error: 470 logmsg(LOG_ERR, "Corrupted log file."); 471 return (1); 472 } 473 474 /* dump a packet directly to the stream, which is unbuffered */ 475 void 476 dump_packet_nobuf(u_char *user, const struct pcap_pkthdr *h, const u_char *sp) 477 { 478 FILE *f = (FILE *)user; 479 480 if (suspended) { 481 packets_dropped++; 482 return; 483 } 484 485 if (fwrite((char *)h, sizeof(*h), 1, f) != 1) { 486 off_t pos = ftello(f); 487 488 /* try to undo header to prevent corruption */ 489 if (pos < sizeof(*h) || 490 ftruncate(fileno(f), pos - sizeof(*h))) { 491 logmsg(LOG_ERR, "Write failed, corrupted logfile!"); 492 set_suspended(1); 493 gotsig_close = 1; 494 return; 495 } 496 goto error; 497 } 498 499 if (fwrite((char *)sp, h->caplen, 1, f) != 1) 500 goto error; 501 502 return; 503 504 error: 505 set_suspended(1); 506 packets_dropped ++; 507 logmsg(LOG_ERR, "Logging suspended: fwrite: %s", strerror(errno)); 508 } 509 510 int 511 flush_buffer(FILE *f) 512 { 513 off_t offset; 514 int len = bufpos - buffer; 515 516 if (len <= 0) 517 return (0); 518 519 offset = ftello(f); 520 if (offset == (off_t)-1) { 521 set_suspended(1); 522 logmsg(LOG_ERR, "Logging suspended: ftello: %s", 523 strerror(errno)); 524 return (1); 525 } 526 527 if (fwrite(buffer, len, 1, f) != 1) { 528 set_suspended(1); 529 logmsg(LOG_ERR, "Logging suspended: fwrite: %s", 530 strerror(errno)); 531 ftruncate(fileno(f), offset); 532 return (1); 533 } 534 535 set_suspended(0); 536 bufpos = buffer; 537 bufleft = buflen; 538 bufpkt = 0; 539 540 return (0); 541 } 542 543 void 544 purge_buffer(void) 545 { 546 packets_dropped += bufpkt; 547 548 set_suspended(0); 549 bufpos = buffer; 550 bufleft = buflen; 551 bufpkt = 0; 552 } 553 554 /* append packet to the buffer, flushing if necessary */ 555 void 556 dump_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *sp) 557 { 558 FILE *f = (FILE *)user; 559 size_t len = sizeof(*h) + h->caplen; 560 561 if (len < sizeof(*h) || h->caplen > (size_t)cur_snaplen) { 562 logmsg(LOG_NOTICE, "invalid size %zu (%d/%d), packet dropped", 563 len, cur_snaplen, snaplen); 564 packets_dropped++; 565 return; 566 } 567 568 if (len <= bufleft) 569 goto append; 570 571 if (suspended) { 572 packets_dropped++; 573 return; 574 } 575 576 if (flush_buffer(f)) { 577 packets_dropped++; 578 return; 579 } 580 581 if (len > bufleft) { 582 dump_packet_nobuf(user, h, sp); 583 return; 584 } 585 586 append: 587 memcpy(bufpos, h, sizeof(*h)); 588 memcpy(bufpos + sizeof(*h), sp, h->caplen); 589 590 bufpos += len; 591 bufleft -= len; 592 bufpkt++; 593 594 return; 595 } 596 597 int 598 main(int argc, char **argv) 599 { 600 int ch, np, ret, Pflag = 0, Xflag = 0; 601 pcap_handler phandler = dump_packet; 602 const char *errstr = NULL; 603 604 ret = 0; 605 606 while ((ch = getopt(argc, argv, "Dxd:f:i:Ps:")) != -1) { 607 switch (ch) { 608 case 'D': 609 Debug = 1; 610 break; 611 case 'd': 612 delay = strtonum(optarg, 5, 60*60, &errstr); 613 if (errstr) 614 usage(); 615 break; 616 case 'f': 617 filename = optarg; 618 break; 619 case 'i': 620 interface = optarg; 621 break; 622 case 'P': /* used internally, exec the child */ 623 if (strcmp("-P", argv[1]) == 0) 624 Pflag = 1; 625 break; 626 case 's': 627 snaplen = strtonum(optarg, 0, PFLOGD_MAXSNAPLEN, 628 &errstr); 629 if (snaplen <= 0) 630 snaplen = DEF_SNAPLEN; 631 if (errstr) 632 snaplen = PFLOGD_MAXSNAPLEN; 633 cur_snaplen = snaplen; 634 break; 635 case 'x': 636 Xflag = 1; 637 break; 638 default: 639 usage(); 640 } 641 642 } 643 644 log_debug = Debug; 645 argc -= optind; 646 argv += optind; 647 648 /* does interface exist */ 649 if (!if_exists(interface)) { 650 warn("Failed to initialize: %s", interface); 651 logmsg(LOG_ERR, "Failed to initialize: %s", interface); 652 logmsg(LOG_ERR, "Exiting, init failure"); 653 exit(1); 654 } 655 656 if (!Debug) { 657 openlog("pflogd", LOG_PID, LOG_DAEMON); 658 if (!Pflag) { 659 if (daemon(0, 0)) { 660 logmsg(LOG_WARNING, 661 "Failed to become daemon: %s", 662 strerror(errno)); 663 } 664 } 665 } 666 667 tzset(); 668 (void)umask(S_IRWXG | S_IRWXO); 669 670 /* filter will be used by the privileged process */ 671 if (argc) { 672 filter = copy_argv(argv); 673 if (filter == NULL) 674 logmsg(LOG_NOTICE, "Failed to form filter expression"); 675 } 676 argc += optind; 677 argv -= optind; 678 679 /* Privilege separation begins here */ 680 priv_init(Pflag, argc, argv); 681 682 if (pledge("stdio recvfd", NULL) == -1) 683 err(1, "pledge"); 684 685 setproctitle("[initializing]"); 686 /* Process is now unprivileged and inside a chroot */ 687 signal(SIGTERM, sig_close); 688 signal(SIGINT, sig_close); 689 signal(SIGQUIT, sig_close); 690 signal(SIGALRM, sig_alrm); 691 signal(SIGHUP, sig_hup); 692 alarm(delay); 693 694 if (priv_init_pcap(snaplen)) 695 errx(1, "priv_init_pcap failed"); 696 697 buffer = malloc(PFLOGD_BUFSIZE); 698 699 if (buffer == NULL) { 700 logmsg(LOG_WARNING, "Failed to allocate output buffer"); 701 phandler = dump_packet_nobuf; 702 } else { 703 bufleft = buflen = PFLOGD_BUFSIZE; 704 bufpos = buffer; 705 bufpkt = 0; 706 } 707 708 if (reset_dump() < 0) { 709 if (Xflag) 710 return (1); 711 712 logmsg(LOG_ERR, "Logging suspended: open error"); 713 set_suspended(1); 714 } else if (Xflag) 715 return (0); 716 717 while (1) { 718 np = pcap_dispatch(hpcap, PCAP_NUM_PKTS, 719 phandler, (u_char *)dpcap); 720 if (np < 0) { 721 if (!if_exists(interface)) { 722 logmsg(LOG_NOTICE, "interface %s went away", 723 interface); 724 ret = -1; 725 break; 726 } 727 logmsg(LOG_NOTICE, "%s", pcap_geterr(hpcap)); 728 } 729 730 if (gotsig_close) 731 break; 732 if (gotsig_hup) { 733 int was_suspended = suspended; 734 if (reset_dump()) { 735 logmsg(LOG_ERR, 736 "Logging suspended: open error"); 737 set_suspended(1); 738 } else { 739 if (was_suspended) 740 logmsg(LOG_NOTICE, "Logging resumed"); 741 } 742 gotsig_hup = 0; 743 } 744 745 if (gotsig_alrm) { 746 if (dpcap) 747 flush_buffer(dpcap); 748 else 749 gotsig_hup = 1; 750 gotsig_alrm = 0; 751 alarm(delay); 752 } 753 } 754 755 logmsg(LOG_NOTICE, "Exiting"); 756 if (dpcap) { 757 flush_buffer(dpcap); 758 fclose(dpcap); 759 } 760 purge_buffer(); 761 762 pcap_close(hpcap); 763 if (!Debug) 764 closelog(); 765 return (ret); 766 } 767