1 /* $OpenBSD: privsep_pcap.c,v 1.25 2019/06/28 13:32:51 deraadt 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
setfilter(int bpfd,int sock,char * filter)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) == -1) {
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 *
priv_pcap_setfilter(pcap_t * hpcap,int oflag,u_int32_t netmask)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
pcap_live(const char * device,int snaplen,int promisc,u_int dlt,u_int dirfilt,u_int fildrop)175 pcap_live(const char *device, int snaplen, int promisc, u_int dlt,
176 u_int dirfilt, u_int fildrop)
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) == -1)
193 goto error;
194
195 if (dlt != (u_int) -1 && ioctl(fd, BIOCSDLT, &dlt) == -1)
196 goto error;
197
198 if (promisc)
199 /* this is allowed to fail */
200 ioctl(fd, BIOCPROMISC, NULL);
201 if (ioctl(fd, BIOCSDIRFILT, &dirfilt) == -1)
202 goto error;
203
204 if (ioctl(fd, BIOCSFILDROP, &fildrop) == -1)
205 goto error;
206
207 /* lock the descriptor */
208 if (ioctl(fd, BIOCLOCK, NULL) == -1)
209 goto error;
210 return (fd);
211
212 error:
213 close(fd);
214 return (-1);
215 }
216
217
218 /*
219 * XXX reimplement pcap_open_live with privsep, this is the
220 * unprivileged part.
221 */
222 pcap_t *
priv_pcap_live(const char * dev,int slen,int prom,int to_ms,char * ebuf,u_int dlt,u_int dirfilt,u_int fildrop)223 priv_pcap_live(const char *dev, int slen, int prom, int to_ms,
224 char *ebuf, u_int dlt, u_int dirfilt, u_int fildrop)
225 {
226 int fd, err;
227 struct bpf_version bv;
228 u_int v;
229 pcap_t *p;
230
231 if (priv_fd < 0)
232 errx(1, "%s: called from privileged portion", __func__);
233
234 if (dev == NULL) {
235 snprintf(ebuf, PCAP_ERRBUF_SIZE, "No interface specified");
236 return (NULL);
237 }
238
239 p = malloc(sizeof(*p));
240 if (p == NULL) {
241 snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s",
242 pcap_strerror(errno));
243 return (NULL);
244 }
245
246 bzero(p, sizeof(*p));
247
248 write_command(priv_fd, PRIV_OPEN_BPF);
249 must_write(priv_fd, &slen, sizeof(int));
250 must_write(priv_fd, &prom, sizeof(int));
251 must_write(priv_fd, &dlt, sizeof(u_int));
252 must_write(priv_fd, &dirfilt, sizeof(u_int));
253 must_write(priv_fd, &fildrop, sizeof(fildrop));
254 write_string(priv_fd, dev);
255
256 fd = receive_fd(priv_fd);
257 must_read(priv_fd, &err, sizeof(int));
258 if (fd < 0) {
259 snprintf(ebuf, PCAP_ERRBUF_SIZE,
260 "Failed to open bpf device for %s: %s",
261 dev, strerror(err));
262 goto bad;
263 }
264
265 /* fd is locked, can only use 'safe' ioctls */
266 if (ioctl(fd, BIOCVERSION, &bv) == -1) {
267 snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCVERSION: %s",
268 pcap_strerror(errno));
269 goto bad;
270 }
271
272 if (bv.bv_major != BPF_MAJOR_VERSION ||
273 bv.bv_minor < BPF_MINOR_VERSION) {
274 snprintf(ebuf, PCAP_ERRBUF_SIZE,
275 "kernel bpf filter out of date");
276 goto bad;
277 }
278
279 p->fd = fd;
280 p->snapshot = slen;
281
282 /* Get the data link layer type. */
283 if (ioctl(fd, BIOCGDLT, &v) == -1) {
284 snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCGDLT: %s",
285 pcap_strerror(errno));
286 goto bad;
287 }
288 p->linktype = v;
289
290 /* XXX hack */
291 if (p->linktype == DLT_PFLOG && p->snapshot < 160)
292 p->snapshot = 160;
293
294 /* set timeout */
295 if (to_ms != 0) {
296 struct timeval to;
297 to.tv_sec = to_ms / 1000;
298 to.tv_usec = (to_ms * 1000) % 1000000;
299 if (ioctl(p->fd, BIOCSRTIMEOUT, &to) == -1) {
300 snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCSRTIMEOUT: %s",
301 pcap_strerror(errno));
302 goto bad;
303 }
304 }
305
306 if (ioctl(fd, BIOCGBLEN, &v) == -1) {
307 snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCGBLEN: %s",
308 pcap_strerror(errno));
309 goto bad;
310 }
311 p->bufsize = v;
312 p->buffer = malloc(p->bufsize);
313 if (p->buffer == NULL) {
314 snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s",
315 pcap_strerror(errno));
316 goto bad;
317 }
318 return (p);
319
320 bad:
321 if (fd >= 0)
322 close(fd);
323 free(p);
324 return (NULL);
325 }
326
327
328
329 /*
330 * reimplement pcap_open_offline with privsep, this is the
331 * unprivileged part.
332 * XXX merge with above?
333 */
334 static void
swap_hdr(struct pcap_file_header * hp)335 swap_hdr(struct pcap_file_header *hp)
336 {
337 hp->version_major = swap16(hp->version_major);
338 hp->version_minor = swap16(hp->version_minor);
339 hp->thiszone = swap32(hp->thiszone);
340 hp->sigfigs = swap32(hp->sigfigs);
341 hp->snaplen = swap32(hp->snaplen);
342 hp->linktype = swap32(hp->linktype);
343 }
344
345 pcap_t *
priv_pcap_offline(const char * fname,char * errbuf)346 priv_pcap_offline(const char *fname, char *errbuf)
347 {
348 pcap_t *p;
349 FILE *fp = NULL;
350 struct pcap_file_header hdr;
351 int linklen, err;
352
353 if (priv_fd < 0)
354 errx(1, "%s: called from privileged portion", __func__);
355
356 p = malloc(sizeof(*p));
357 if (p == NULL) {
358 strlcpy(errbuf, "out of swap", PCAP_ERRBUF_SIZE);
359 return (NULL);
360 }
361
362 memset((char *)p, 0, sizeof(*p));
363
364 if (fname[0] == '-' && fname[1] == '\0') {
365 p->fd = -1;
366 fp = stdin;
367 } else {
368 write_command(priv_fd, PRIV_OPEN_DUMP);
369 p->fd = receive_fd(priv_fd);
370 must_read(priv_fd, &err, sizeof(int));
371 if (p->fd < 0) {
372 snprintf(errbuf, PCAP_ERRBUF_SIZE,
373 "Failed to open input file %s: %s",
374 fname, strerror(err));
375 goto bad;
376 }
377
378 fp = fdopen(p->fd, "r");
379 if (fp == NULL) {
380 snprintf(errbuf, PCAP_ERRBUF_SIZE, "%s: %s", fname,
381 pcap_strerror(errno));
382 close(p->fd);
383 p->fd = -1;
384 goto bad;
385 }
386 }
387 if (fread((char *)&hdr, sizeof(hdr), 1, fp) != 1) {
388 snprintf(errbuf, PCAP_ERRBUF_SIZE, "fread: %s",
389 pcap_strerror(errno));
390 goto bad;
391 }
392
393 if (hdr.magic != TCPDUMP_MAGIC) {
394 if (swap32(hdr.magic) != TCPDUMP_MAGIC) {
395 snprintf(errbuf, PCAP_ERRBUF_SIZE,
396 "bad dump file format");
397 goto bad;
398 }
399 p->sf.swapped = 1;
400 swap_hdr(&hdr);
401 }
402 if (hdr.version_major < PCAP_VERSION_MAJOR) {
403 snprintf(errbuf, PCAP_ERRBUF_SIZE, "archaic file format");
404 goto bad;
405 }
406
407 p->tzoff = hdr.thiszone;
408 p->snapshot = hdr.snaplen;
409 p->linktype = hdr.linktype;
410 p->sf.rfile = fp;
411 p->bufsize = hdr.snaplen;
412
413 /* Align link header as required for proper data alignment */
414 /* XXX should handle all types */
415 switch (p->linktype) {
416
417 case DLT_EN10MB:
418 linklen = 14;
419 break;
420
421 case DLT_FDDI:
422 linklen = 13 + 8; /* fddi_header + llc */
423 break;
424
425 case DLT_NULL:
426 default:
427 linklen = 0;
428 break;
429 }
430
431 if (p->bufsize < 0)
432 p->bufsize = BPF_MAXBUFSIZE;
433 p->sf.base = malloc(p->bufsize + BPF_ALIGNMENT);
434 if (p->sf.base == NULL) {
435 strlcpy(errbuf, "out of swap", PCAP_ERRBUF_SIZE);
436 goto bad;
437 }
438 p->buffer = p->sf.base + BPF_ALIGNMENT - (linklen % BPF_ALIGNMENT);
439 p->sf.version_major = hdr.version_major;
440 p->sf.version_minor = hdr.version_minor;
441 #ifdef PCAP_FDDIPAD
442 /* XXX what to do with this? */
443 /* XXX padding only needed for kernel fcode */
444 pcap_fddipad = 0;
445 #endif
446 return (p);
447
448 bad:
449 if (fp != NULL && p->fd != -1)
450 fclose(fp);
451 free(p);
452 return (NULL);
453 }
454
455
456 static int
sf_write_header(FILE * fp,int linktype,int thiszone,int snaplen)457 sf_write_header(FILE *fp, int linktype, int thiszone, int snaplen)
458 {
459 struct pcap_file_header hdr;
460
461 bzero(&hdr, sizeof hdr);
462 hdr.magic = TCPDUMP_MAGIC;
463 hdr.version_major = PCAP_VERSION_MAJOR;
464 hdr.version_minor = PCAP_VERSION_MINOR;
465
466 hdr.thiszone = thiszone;
467 hdr.snaplen = snaplen;
468 hdr.sigfigs = 0;
469 hdr.linktype = linktype;
470
471 if (fwrite((char *)&hdr, sizeof(hdr), 1, fp) != 1)
472 return (-1);
473
474 return (0);
475 }
476
477 pcap_dumper_t *
priv_pcap_dump_open(pcap_t * p,char * fname)478 priv_pcap_dump_open(pcap_t *p, char *fname)
479 {
480 int fd, err;
481 FILE *f;
482
483 if (priv_fd < 0)
484 errx(1, "%s: called from privileged portion", __func__);
485
486 if (fname[0] == '-' && fname[1] == '\0')
487 f = stdout;
488 else {
489 write_command(priv_fd, PRIV_OPEN_OUTPUT);
490 fd = receive_fd(priv_fd);
491 must_read(priv_fd, &err, sizeof(err));
492 if (fd < 0) {
493 snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
494 "Failed to open output file %s: %s",
495 fname, strerror(err));
496 return (NULL);
497 }
498 f = fdopen(fd, "w");
499 if (f == NULL) {
500 snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "%s: %s",
501 fname, pcap_strerror(errno));
502 close(fd);
503 return (NULL);
504 }
505 }
506 priv_init_done();
507
508 (void)sf_write_header(f, p->linktype, p->tzoff, p->snapshot);
509 return ((pcap_dumper_t *)f);
510 }
511