GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/tcpdump/privsep_pcap.c Lines: 0 247 0.0 %
Date: 2017-11-13 Branches: 0 114 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: privsep_pcap.c,v 1.22 2017/04/19 05:36:13 natano Exp $ */
2
3
/*
4
 * Copyright (c) 2004 Can Erkin Acar
5
 * All rights reserved.
6
 *
7
 * Redistribution and use in source and binary forms, with or without
8
 * modification, are permitted provided that the following conditions
9
 * are met:
10
 *
11
 *    - Redistributions of source code must retain the above copyright
12
 *      notice, this list of conditions and the following disclaimer.
13
 *    - Redistributions in binary form must reproduce the above
14
 *      copyright notice, this list of conditions and the following
15
 *      disclaimer in the documentation and/or other materials provided
16
 *      with the distribution.
17
 *
18
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22
 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29
 * POSSIBILITY OF SUCH DAMAGE.
30
 */
31
32
#include <sys/types.h>
33
#include <sys/ioctl.h>
34
#include <sys/socket.h>
35
#include <net/if.h>
36
37
#include <err.h>
38
#include <errno.h>
39
#include <fcntl.h>
40
#include <pcap-int.h>
41
#include <pcap.h>
42
#include <stdio.h>
43
#include <stdlib.h>
44
#include <string.h>
45
#include <unistd.h>
46
47
#include "privsep.h"
48
49
/*
50
 * privileged part of priv_pcap_setfilter, compile the filter
51
 * expression, and return it to the parent. Note that we fake an hpcap
52
 * and use it to capture the error messages, and pass the error back
53
 * to client.
54
 */
55
int
56
setfilter(int bpfd, int sock, char *filter)
57
{
58
	struct bpf_program fcode;
59
	int oflag, snap, link;
60
	u_int32_t netmask;
61
	pcap_t hpcap;
62
63
	must_read(sock, &oflag, sizeof(oflag));
64
	must_read(sock, &netmask, sizeof(netmask));
65
	must_read(sock, &snap, sizeof(snap));
66
	must_read(sock, &link, sizeof(link));
67
68
	if (snap < 0) {
69
		snprintf(hpcap.errbuf, PCAP_ERRBUF_SIZE, "invalid snaplen");
70
		goto err;
71
	}
72
73
	/* fake hpcap, it only needs errbuf, snaplen, and linktype to
74
	 * compile a filter expression */
75
	/* XXX messing with pcap internals */
76
	hpcap.snapshot = snap;
77
	hpcap.linktype = link;
78
	if (pcap_compile(&hpcap, &fcode, filter, oflag, netmask))
79
		goto err;
80
81
	/* if bpf descriptor is open, set the filter XXX check oflag? */
82
	if (bpfd >= 0 && ioctl(bpfd, BIOCSETF, &fcode)) {
83
		snprintf(hpcap.errbuf, PCAP_ERRBUF_SIZE,
84
		    "ioctl: BIOCSETF: %s", strerror(errno));
85
		pcap_freecode(&fcode);
86
		goto err;
87
	}
88
	if (fcode.bf_len > 0) {
89
		/* write the filter */
90
		must_write(sock, &fcode.bf_len, sizeof(fcode.bf_len));
91
		must_write(sock, fcode.bf_insns,
92
		    fcode.bf_len * sizeof(struct bpf_insn));
93
	} else {
94
		snprintf(hpcap.errbuf, PCAP_ERRBUF_SIZE, "Invalid filter size");
95
		pcap_freecode(&fcode);
96
		goto err;
97
	}
98
99
100
	pcap_freecode(&fcode);
101
	return (0);
102
103
 err:
104
	fcode.bf_len = 0;
105
	must_write(sock, &fcode.bf_len, sizeof(fcode.bf_len));
106
107
	/* write back the error string */
108
	write_string(sock, hpcap.errbuf);
109
	return (1);
110
}
111
112
/*
113
 * filter is compiled and set in the privileged process.
114
 * get the compiled output and set it locally for filtering dumps etc.
115
 */
116
struct bpf_program *
117
priv_pcap_setfilter(pcap_t *hpcap, int oflag, u_int32_t netmask)
118
{
119
	struct bpf_program *fcode = NULL;
120
	int snap, link;
121
	char *ebuf;
122
123
	if (priv_fd < 0)
124
		errx(1, "%s: called from privileged portion", __func__);
125
126
	ebuf = pcap_geterr(hpcap);
127
	snap = pcap_snapshot(hpcap);
128
	link = pcap_datalink(hpcap);
129
130
	fcode = calloc(1, sizeof(*fcode));
131
	if (fcode == NULL) {
132
		snprintf(ebuf, PCAP_ERRBUF_SIZE, "out of memory");
133
		return (NULL);
134
	}
135
136
	write_command(priv_fd, PRIV_SETFILTER);
137
138
	/* send oflag, netmask, snaplen and linktype */
139
	must_write(priv_fd, &oflag, sizeof(oflag));
140
	must_write(priv_fd, &netmask, sizeof(netmask));
141
	must_write(priv_fd, &snap, sizeof(snap));
142
	must_write(priv_fd, &link, sizeof(link));
143
144
	/* receive compiled filter */
145
	must_read(priv_fd, &fcode->bf_len, sizeof(fcode->bf_len));
146
	if (fcode->bf_len <= 0) {
147
		int len;
148
149
		len = read_string(priv_fd, ebuf, PCAP_ERRBUF_SIZE, __func__);
150
		if (len == 0)
151
			snprintf(ebuf, PCAP_ERRBUF_SIZE, "pcap compile error");
152
		goto err;
153
	}
154
155
	fcode->bf_insns = calloc(fcode->bf_len, sizeof(struct bpf_insn));
156
	if (fcode->bf_insns == NULL) {
157
		snprintf(ebuf, PCAP_ERRBUF_SIZE, "out of memory");
158
		goto err;
159
	}
160
161
	must_read(priv_fd, fcode->bf_insns,
162
	    fcode->bf_len * sizeof(struct bpf_insn));
163
164
	pcap_setfilter(hpcap, fcode);
165
	return (fcode);
166
167
 err:
168
	free(fcode);
169
	return (NULL);
170
}
171
172
173
/* privileged part of priv_pcap_live */
174
int
175
pcap_live(const char *device, int snaplen, int promisc, u_int dlt,
176
    u_int dirfilt)
177
{
178
	int		fd;
179
	struct ifreq	ifr;
180
	unsigned	v;
181
182
	if (device == NULL || snaplen <= 0)
183
		return (-1);
184
185
	if ((fd = open("/dev/bpf", O_RDONLY)) == -1)
186
		return (-1);
187
188
	v = 32768;	/* XXX this should be a user-accessible hook */
189
	ioctl(fd, BIOCSBLEN, &v);
190
191
	strlcpy(ifr.ifr_name, device, sizeof(ifr.ifr_name));
192
	if (ioctl(fd, BIOCSETIF, &ifr) < 0)
193
		goto error;
194
195
	if (dlt != (u_int) -1 && ioctl(fd, BIOCSDLT, &dlt))
196
		goto error;
197
198
	if (promisc)
199
		/* this is allowed to fail */
200
		ioctl(fd, BIOCPROMISC, NULL);
201
	if (ioctl(fd, BIOCSDIRFILT, &dirfilt) < 0)
202
		goto error;
203
204
	/* lock the descriptor */
205
	if (ioctl(fd, BIOCLOCK, NULL) < 0)
206
		goto error;
207
	return (fd);
208
209
 error:
210
	close(fd);
211
	return (-1);
212
}
213
214
215
/*
216
 * XXX reimplement pcap_open_live with privsep, this is the
217
 * unprivileged part.
218
 */
219
pcap_t *
220
priv_pcap_live(const char *dev, int slen, int prom, int to_ms,
221
    char *ebuf, u_int dlt, u_int dirfilt)
222
{
223
	int fd, err;
224
	struct bpf_version bv;
225
	u_int v;
226
	pcap_t *p;
227
228
	if (priv_fd < 0)
229
		errx(1, "%s: called from privileged portion", __func__);
230
231
	if (dev == NULL) {
232
		snprintf(ebuf, PCAP_ERRBUF_SIZE, "No interface specified");
233
		return (NULL);
234
	}
235
236
	p = malloc(sizeof(*p));
237
	if (p == NULL) {
238
		snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s",
239
		    pcap_strerror(errno));
240
		return (NULL);
241
	}
242
243
	bzero(p, sizeof(*p));
244
245
	write_command(priv_fd, PRIV_OPEN_BPF);
246
	must_write(priv_fd, &slen, sizeof(int));
247
	must_write(priv_fd, &prom, sizeof(int));
248
	must_write(priv_fd, &dlt, sizeof(u_int));
249
	must_write(priv_fd, &dirfilt, sizeof(u_int));
250
	write_string(priv_fd, dev);
251
252
	fd = receive_fd(priv_fd);
253
	must_read(priv_fd, &err, sizeof(int));
254
	if (fd < 0) {
255
		snprintf(ebuf, PCAP_ERRBUF_SIZE,
256
		    "Failed to open bpf device for %s: %s",
257
		    dev, strerror(err));
258
		goto bad;
259
	}
260
261
	/* fd is locked, can only use 'safe' ioctls */
262
	if (ioctl(fd, BIOCVERSION, &bv) < 0) {
263
		snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCVERSION: %s",
264
		    pcap_strerror(errno));
265
		goto bad;
266
	}
267
268
	if (bv.bv_major != BPF_MAJOR_VERSION ||
269
	    bv.bv_minor < BPF_MINOR_VERSION) {
270
		snprintf(ebuf, PCAP_ERRBUF_SIZE,
271
		    "kernel bpf filter out of date");
272
		goto bad;
273
	}
274
275
	p->fd = fd;
276
	p->snapshot = slen;
277
278
	/* Get the data link layer type. */
279
	if (ioctl(fd, BIOCGDLT, &v) < 0) {
280
		snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCGDLT: %s",
281
		    pcap_strerror(errno));
282
		goto bad;
283
	}
284
	p->linktype = v;
285
286
	/* XXX hack */
287
	if (p->linktype == DLT_PFLOG && p->snapshot < 160)
288
		p->snapshot = 160;
289
290
	/* set timeout */
291
	if (to_ms != 0) {
292
		struct timeval to;
293
		to.tv_sec = to_ms / 1000;
294
		to.tv_usec = (to_ms * 1000) % 1000000;
295
		if (ioctl(p->fd, BIOCSRTIMEOUT, &to) < 0) {
296
			snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCSRTIMEOUT: %s",
297
			    pcap_strerror(errno));
298
			goto bad;
299
		}
300
	}
301
302
	if (ioctl(fd, BIOCGBLEN, &v) < 0) {
303
		snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCGBLEN: %s",
304
		    pcap_strerror(errno));
305
		goto bad;
306
	}
307
	p->bufsize = v;
308
	p->buffer = malloc(p->bufsize);
309
	if (p->buffer == NULL) {
310
		snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s",
311
		    pcap_strerror(errno));
312
		goto bad;
313
	}
314
	return (p);
315
316
 bad:
317
	if (fd >= 0)
318
		close(fd);
319
	free(p);
320
	return (NULL);
321
}
322
323
324
325
/*
326
 * reimplement pcap_open_offline with privsep, this is the
327
 * unprivileged part.
328
 * XXX merge with above?
329
 */
330
static void
331
swap_hdr(struct pcap_file_header *hp)
332
{
333
	hp->version_major = swap16(hp->version_major);
334
	hp->version_minor = swap16(hp->version_minor);
335
	hp->thiszone = swap32(hp->thiszone);
336
	hp->sigfigs = swap32(hp->sigfigs);
337
	hp->snaplen = swap32(hp->snaplen);
338
	hp->linktype = swap32(hp->linktype);
339
}
340
341
pcap_t *
342
priv_pcap_offline(const char *fname, char *errbuf)
343
{
344
	pcap_t *p;
345
	FILE *fp = NULL;
346
	struct pcap_file_header hdr;
347
	int linklen, err;
348
349
	if (priv_fd < 0)
350
		errx(1, "%s: called from privileged portion", __func__);
351
352
	p = malloc(sizeof(*p));
353
	if (p == NULL) {
354
		strlcpy(errbuf, "out of swap", PCAP_ERRBUF_SIZE);
355
		return (NULL);
356
	}
357
358
	memset((char *)p, 0, sizeof(*p));
359
360
	if (fname[0] == '-' && fname[1] == '\0') {
361
		p->fd = -1;
362
		fp = stdin;
363
	} else {
364
		write_command(priv_fd, PRIV_OPEN_DUMP);
365
		p->fd = receive_fd(priv_fd);
366
		must_read(priv_fd, &err, sizeof(int));
367
		if (p->fd < 0) {
368
			snprintf(errbuf, PCAP_ERRBUF_SIZE,
369
			    "Failed to open input file %s: %s",
370
			    fname, strerror(err));
371
			goto bad;
372
		}
373
374
		fp = fdopen(p->fd, "r");
375
		if (fp == NULL) {
376
			snprintf(errbuf, PCAP_ERRBUF_SIZE, "%s: %s", fname,
377
			    pcap_strerror(errno));
378
			close(p->fd);
379
			p->fd = -1;
380
			goto bad;
381
		}
382
	}
383
	if (fread((char *)&hdr, sizeof(hdr), 1, fp) != 1) {
384
		snprintf(errbuf, PCAP_ERRBUF_SIZE, "fread: %s",
385
		    pcap_strerror(errno));
386
		goto bad;
387
	}
388
389
	if (hdr.magic != TCPDUMP_MAGIC) {
390
		if (swap32(hdr.magic) != TCPDUMP_MAGIC) {
391
			snprintf(errbuf, PCAP_ERRBUF_SIZE,
392
			    "bad dump file format");
393
			goto bad;
394
		}
395
		p->sf.swapped = 1;
396
		swap_hdr(&hdr);
397
	}
398
	if (hdr.version_major < PCAP_VERSION_MAJOR) {
399
		snprintf(errbuf, PCAP_ERRBUF_SIZE, "archaic file format");
400
		goto bad;
401
	}
402
403
	p->tzoff = hdr.thiszone;
404
	p->snapshot = hdr.snaplen;
405
	p->linktype = hdr.linktype;
406
	p->sf.rfile = fp;
407
	p->bufsize = hdr.snaplen;
408
409
	/* Align link header as required for proper data alignment */
410
	/* XXX should handle all types */
411
	switch (p->linktype) {
412
413
	case DLT_EN10MB:
414
		linklen = 14;
415
		break;
416
417
	case DLT_FDDI:
418
		linklen = 13 + 8;	/* fddi_header + llc */
419
		break;
420
421
	case DLT_NULL:
422
	default:
423
		linklen = 0;
424
		break;
425
	}
426
427
	if (p->bufsize < 0)
428
		p->bufsize = BPF_MAXBUFSIZE;
429
	p->sf.base = malloc(p->bufsize + BPF_ALIGNMENT);
430
	if (p->sf.base == NULL) {
431
		strlcpy(errbuf, "out of swap", PCAP_ERRBUF_SIZE);
432
		goto bad;
433
	}
434
	p->buffer = p->sf.base + BPF_ALIGNMENT - (linklen % BPF_ALIGNMENT);
435
	p->sf.version_major = hdr.version_major;
436
	p->sf.version_minor = hdr.version_minor;
437
#ifdef PCAP_FDDIPAD
438
	/* XXX what to do with this? */
439
	/* XXX padding only needed for kernel fcode */
440
	pcap_fddipad = 0;
441
#endif
442
	return (p);
443
444
 bad:
445
	if (fp != NULL && p->fd != -1)
446
		fclose(fp);
447
	free(p);
448
	return (NULL);
449
}
450
451
452
static int
453
sf_write_header(FILE *fp, int linktype, int thiszone, int snaplen)
454
{
455
	struct pcap_file_header hdr;
456
457
	bzero(&hdr, sizeof hdr);
458
	hdr.magic = TCPDUMP_MAGIC;
459
	hdr.version_major = PCAP_VERSION_MAJOR;
460
	hdr.version_minor = PCAP_VERSION_MINOR;
461
462
	hdr.thiszone = thiszone;
463
	hdr.snaplen = snaplen;
464
	hdr.sigfigs = 0;
465
	hdr.linktype = linktype;
466
467
	if (fwrite((char *)&hdr, sizeof(hdr), 1, fp) != 1)
468
		return (-1);
469
470
	return (0);
471
}
472
473
pcap_dumper_t *
474
priv_pcap_dump_open(pcap_t *p, char *fname)
475
{
476
	int fd, err;
477
	FILE *f;
478
479
	if (priv_fd < 0)
480
		errx(1, "%s: called from privileged portion", __func__);
481
482
	if (fname[0] == '-' && fname[1] == '\0') {
483
		f = stdout;
484
		priv_init_done();
485
	} else {
486
		write_command(priv_fd, PRIV_OPEN_OUTPUT);
487
		fd = receive_fd(priv_fd);
488
		must_read(priv_fd, &err, sizeof(err));
489
		if (fd < 0)  {
490
			snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
491
			    "Failed to open output file %s: %s",
492
			    fname, strerror(err));
493
			return (NULL);
494
		}
495
		f = fdopen(fd, "w");
496
		if (f == NULL) {
497
			snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "%s: %s",
498
			    fname, pcap_strerror(errno));
499
			close(fd);
500
			return (NULL);
501
		}
502
	}
503
504
	(void)sf_write_header(f, p->linktype, p->tzoff, p->snapshot);
505
	return ((pcap_dumper_t *)f);
506
}