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