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