GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: sbin/pflogd/pflogd.c Lines: 0 312 0.0 %
Date: 2017-11-07 Branches: 0 184 0.0 %

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