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