diff options
author | Can Erkin Acar <canacar@cvs.openbsd.org> | 2004-01-28 19:44:56 +0000 |
---|---|---|
committer | Can Erkin Acar <canacar@cvs.openbsd.org> | 2004-01-28 19:44:56 +0000 |
commit | 87d1aa5c72a876badad301facf67f7ee8ec5880b (patch) | |
tree | ca8152a20e4c4e01f6872b9741bc361313522cad /usr.sbin | |
parent | a9765aa5e532b4e14ae9e6a3d66d5c3a514d6de2 (diff) |
privilege separated tcpdump, joint work with otto@
tested by avsm@ vincent@ dhartmei@ markus@ hshoexer@ and others
go for it deraadt@
Diffstat (limited to 'usr.sbin')
-rw-r--r-- | usr.sbin/tcpdump/Makefile | 8 | ||||
-rw-r--r-- | usr.sbin/tcpdump/addrtoname.c | 76 | ||||
-rw-r--r-- | usr.sbin/tcpdump/addrtoname.h | 5 | ||||
-rw-r--r-- | usr.sbin/tcpdump/gmt2local.c | 7 | ||||
-rw-r--r-- | usr.sbin/tcpdump/interface.h | 6 | ||||
-rw-r--r-- | usr.sbin/tcpdump/pf_print_state.c | 288 | ||||
-rw-r--r-- | usr.sbin/tcpdump/pfctl_osfp.c | 1093 | ||||
-rw-r--r-- | usr.sbin/tcpdump/print-atalk.c | 74 | ||||
-rw-r--r-- | usr.sbin/tcpdump/print-cnfp.c | 15 | ||||
-rw-r--r-- | usr.sbin/tcpdump/print-sunrpc.c | 12 | ||||
-rw-r--r-- | usr.sbin/tcpdump/print-udp.c | 4 | ||||
-rw-r--r-- | usr.sbin/tcpdump/privsep.c | 901 | ||||
-rw-r--r-- | usr.sbin/tcpdump/privsep.h | 103 | ||||
-rw-r--r-- | usr.sbin/tcpdump/privsep_fdpass.c | 116 | ||||
-rw-r--r-- | usr.sbin/tcpdump/privsep_pcap.c | 496 | ||||
-rw-r--r-- | usr.sbin/tcpdump/tcpdump.c | 86 | ||||
-rw-r--r-- | usr.sbin/tcpdump/util.c | 75 |
17 files changed, 3207 insertions, 158 deletions
diff --git a/usr.sbin/tcpdump/Makefile b/usr.sbin/tcpdump/Makefile index defe27e72a4..bb92ffb8675 100644 --- a/usr.sbin/tcpdump/Makefile +++ b/usr.sbin/tcpdump/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.42 2003/08/21 19:14:23 frantzen Exp $ +# $OpenBSD: Makefile,v 1.43 2004/01/28 19:44:55 canacar Exp $ # # Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994 # The Regents of the University of California. All rights reserved. @@ -24,7 +24,6 @@ PROG= tcpdump MAN= tcpdump.8 CFLAGS+=-Wall -I${.CURDIR}/../../sbin/pfctl -.PATH: ${.CURDIR}/../../sbin/pfctl CFLAGS+=-DCSLIP -DPPP -DHAVE_FDDI -DETHER_SERVICE -DRETSIGTYPE=void -DHAVE_NET_SLIP_H -DHAVE_ETHER_NTOHOST -DINET6 .if (${MACHINE_ARCH} == "alpha") || (${MACHINE_ARCH} == "sparc") || \ @@ -35,7 +34,10 @@ CFLAGS+=-DLBL_ALIGN LDADD+= -lpcap -ll -lcrypto DPADD+= ${LIBL} ${LIBPCAP} ${LIBCRYPTO} -SRCS= tcpdump.c addrtoname.c \ +#BINMODE=4555 +#BINOWN= root + +SRCS= tcpdump.c addrtoname.c privsep.c privsep_fdpass.c privsep_pcap.c \ print-ether.c print-ip.c print-arp.c print-tcp.c print-udp.c \ print-atalk.c print-domain.c print-tftp.c print-bootp.c print-nfs.c \ print-icmp.c print-sl.c print-ppp.c print-rip.c print-timed.c \ diff --git a/usr.sbin/tcpdump/addrtoname.c b/usr.sbin/tcpdump/addrtoname.c index 227af7589cd..2c2d6d9faeb 100644 --- a/usr.sbin/tcpdump/addrtoname.c +++ b/usr.sbin/tcpdump/addrtoname.c @@ -1,4 +1,4 @@ -/* $OpenBSD: addrtoname.c,v 1.19 2002/02/19 19:39:40 millert Exp $ */ +/* $OpenBSD: addrtoname.c,v 1.20 2004/01/28 19:44:55 canacar Exp $ */ /* * Copyright (c) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997 @@ -25,7 +25,7 @@ */ #ifndef lint static const char rcsid[] = - "@(#) $Header: /cvs/OpenBSD/src/usr.sbin/tcpdump/addrtoname.c,v 1.19 2002/02/19 19:39:40 millert Exp $ (LBL)"; + "@(#) $Header: /cvs/OpenBSD/src/usr.sbin/tcpdump/addrtoname.c,v 1.20 2004/01/28 19:44:55 canacar Exp $ (LBL)"; #endif #include <sys/types.h> @@ -61,6 +61,7 @@ struct rtentry; #include "interface.h" #include "addrtoname.h" #include "llc.h" +#include "privsep.h" #include "savestr.h" #include "setsignal.h" @@ -106,6 +107,7 @@ struct enamemem { struct enamemem enametable[HASHNAMESIZE]; struct enamemem nsaptable[HASHNAMESIZE]; struct enamemem bytestringtable[HASHNAMESIZE]; +static char *ipprototable[256]; struct protoidmem { u_int32_t p_oui; @@ -160,7 +162,7 @@ static u_int32_t netmask; char * getname(const u_char *ap) { - register struct hostent *hp; + char host[MAXHOSTNAMELEN]; u_int32_t addr; struct hnamemem *p; @@ -222,11 +224,12 @@ getname(const u_char *ap) (addr & f_netmask) == f_localnet && (aflag || !((addr & ~netmask) == 0 || (addr | netmask) == 0xffffffff))) { - hp = gethostbyaddr((char *)&addr, 4, AF_INET); - if (hp) { + int n = priv_gethostbyaddr((char *)&addr, 4, AF_INET, + host, sizeof(host)); + if (n > 0) { char *dotp; - p->name = savestr(hp->h_name); + p->name = savestr(host); if (Nflag) { /* Remove domain qualifications */ dotp = strchr(p->name, '.'); @@ -248,7 +251,7 @@ getname(const u_char *ap) char * getname6(const u_char *ap) { - register struct hostent *hp; + char host[MAXHOSTNAMELEN]; struct in6_addr addr; struct h6namemem *p; register char *cp; @@ -280,11 +283,12 @@ getname6(const u_char *ap) !((addr & ~netmask) == 0 || (addr | netmask) == 0xffffffff)) #endif ) { - hp = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET6); - if (hp) { + int n = priv_gethostbyaddr((char *)&addr, sizeof(addr), + AF_INET6, host, sizeof(host)); + if (n > 0) { char *dotp; - p->name = savestr(hp->h_name); + p->name = savestr(host); if (Nflag) { /* Remove domain qualifications */ dotp = strchr(p->name, '.'); @@ -463,7 +467,8 @@ etheraddr_string(register const u_char *ep) #ifdef HAVE_ETHER_NTOHOST if (!nflag) { char buf[MAXHOSTNAMELEN + 1]; - if (ether_ntohost(buf, (struct ether_addr *)ep) == 0) { + if (priv_ether_ntohost(buf, sizeof(buf), + (struct ether_addr *)ep) > 0) { tp->e_name = savestr(buf); return (tp->e_name); } @@ -649,20 +654,29 @@ udpport_string(register u_short port) return (tp->name); } +char * +ipproto_string(u_int proto) +{ + return ipprototable[proto & 0xff]; +} + static void init_servarray(void) { - struct servent *sv; - register struct hnamemem *table; - register int i; + struct hnamemem *table; + int i, port; char buf[sizeof("0000000000")]; + char service[BUFSIZ]; + char protocol[sizeof("tcp")]; - while ((sv = getservent()) != NULL) { - int port = ntohs(sv->s_port); + priv_getserventries(); + while (priv_getserventry(service, sizeof(service), &port, protocol, + sizeof(protocol)) != 0) { + port = ntohs(port); i = port & (HASHNAMESIZE-1); - if (strcmp(sv->s_proto, "tcp") == 0) + if (strcmp(protocol, "tcp") == 0) table = &tporttable[i]; - else if (strcmp(sv->s_proto, "udp") == 0) + else if (strcmp(protocol, "udp") == 0) table = &uporttable[i]; else continue; @@ -673,11 +687,29 @@ init_servarray(void) (void)snprintf(buf, sizeof(buf), "%d", port); table->name = savestr(buf); } else - table->name = savestr(sv->s_name); + table->name = savestr(service); table->addr = port; table->nxt = newhnamemem(); } - endservent(); +} + +static void +init_ipprotoarray(void) +{ + int i; + char buf[sizeof("000")]; + char prot[BUFSIZ]; + + if (!nflag) { + priv_getprotoentries(); + while (priv_getprotoentry(prot, sizeof(prot), &i) != 0) + ipprototable[i & 0xff] = savestr(prot); + } + for (i = 0; i < 256; i++) + if (ipprototable[i] == NULL) { + (void)snprintf(buf, sizeof(buf), "%d", i); + ipprototable[i] = savestr(buf); + } } /*XXX from libbpfc.a */ @@ -779,7 +811,8 @@ init_etherarray(void) #ifdef HAVE_ETHER_NTOHOST /* Use yp/nis version of name if available */ - if (ether_ntohost(name, (struct ether_addr *)el->addr) == 0) { + if (priv_ether_ntohost(name, sizeof(name), + (struct ether_addr *)el->addr) > 0) { tp->e_name = savestr(name); continue; } @@ -844,6 +877,7 @@ init_addrtoname(u_int32_t localnet, u_int32_t mask) init_eprotoarray(); init_llcsaparray(); init_protoidarray(); + init_ipprotoarray(); } char * diff --git a/usr.sbin/tcpdump/addrtoname.h b/usr.sbin/tcpdump/addrtoname.h index 5bfa22b7e10..ef890cabf9b 100644 --- a/usr.sbin/tcpdump/addrtoname.h +++ b/usr.sbin/tcpdump/addrtoname.h @@ -1,4 +1,4 @@ -/* $OpenBSD: addrtoname.h,v 1.9 2000/10/03 14:31:54 ho Exp $ */ +/* $OpenBSD: addrtoname.h,v 1.10 2004/01/28 19:44:55 canacar Exp $ */ /* * Copyright (c) 1990, 1992, 1993, 1994, 1995, 1996, 1997 @@ -20,7 +20,7 @@ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * - * @(#) $Header: /cvs/OpenBSD/src/usr.sbin/tcpdump/addrtoname.h,v 1.9 2000/10/03 14:31:54 ho Exp $ (LBL) + * @(#) $Header: /cvs/OpenBSD/src/usr.sbin/tcpdump/addrtoname.h,v 1.10 2004/01/28 19:44:55 canacar Exp $ (LBL) */ #ifndef BYTE_ORDER @@ -37,6 +37,7 @@ extern char *etheraddr_string(const u_char *); extern char *etherproto_string(u_short); extern char *tcpport_string(u_short); extern char *udpport_string(u_short); +extern char *ipproto_string(u_int); extern char *getname(const u_char *); #ifdef INET6 extern char *getname6(const u_char *); diff --git a/usr.sbin/tcpdump/gmt2local.c b/usr.sbin/tcpdump/gmt2local.c index ea37c53b7ad..e436ed79095 100644 --- a/usr.sbin/tcpdump/gmt2local.c +++ b/usr.sbin/tcpdump/gmt2local.c @@ -1,4 +1,4 @@ -/* $OpenBSD: gmt2local.c,v 1.2 2000/10/03 14:31:55 ho Exp $ */ +/* $OpenBSD: gmt2local.c,v 1.3 2004/01/28 19:44:55 canacar Exp $ */ /* * Copyright (c) 1997 @@ -23,7 +23,7 @@ #ifndef lint static const char rcsid[] = - "@(#) $Header: /cvs/OpenBSD/src/usr.sbin/tcpdump/gmt2local.c,v 1.2 2000/10/03 14:31:55 ho Exp $ (LBL)"; + "@(#) $Header: /cvs/OpenBSD/src/usr.sbin/tcpdump/gmt2local.c,v 1.3 2004/01/28 19:44:55 canacar Exp $ (LBL)"; #endif #include <sys/types.h> @@ -40,6 +40,7 @@ static const char rcsid[] = #endif #include "gmt2local.h" +#include "privsep.h" /* * Returns the difference between gmt and local time in seconds. @@ -56,7 +57,7 @@ gmt2local(time_t t) t = time(NULL); gmt = &sgmt; *gmt = *gmtime(&t); - loc = localtime(&t); + loc = priv_localtime(&t); dt = (loc->tm_hour - gmt->tm_hour) * 60 * 60 + (loc->tm_min - gmt->tm_min) * 60; diff --git a/usr.sbin/tcpdump/interface.h b/usr.sbin/tcpdump/interface.h index c354907b43b..ec369060653 100644 --- a/usr.sbin/tcpdump/interface.h +++ b/usr.sbin/tcpdump/interface.h @@ -1,4 +1,4 @@ -/* $OpenBSD: interface.h,v 1.42 2004/01/18 15:33:30 otto Exp $ */ +/* $OpenBSD: interface.h,v 1.43 2004/01/28 19:44:55 canacar Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997 @@ -20,7 +20,7 @@ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * - * @(#) $Header: /cvs/OpenBSD/src/usr.sbin/tcpdump/interface.h,v 1.42 2004/01/18 15:33:30 otto Exp $ (LBL) + * @(#) $Header: /cvs/OpenBSD/src/usr.sbin/tcpdump/interface.h,v 1.43 2004/01/28 19:44:55 canacar Exp $ (LBL) */ #ifndef tcpdump_interface_h @@ -165,7 +165,7 @@ extern __dead void error(const char *, ...) extern void warning(const char *, ...) __attribute__ ((format (printf, 1, 2))); extern char *read_infile(char *); -extern char *copy_argv(char **); +extern char *copy_argv(char * const *); extern char *isonsap_string(const u_char *); extern char *llcsap_string(u_char); diff --git a/usr.sbin/tcpdump/pf_print_state.c b/usr.sbin/tcpdump/pf_print_state.c new file mode 100644 index 00000000000..ea0a02765b7 --- /dev/null +++ b/usr.sbin/tcpdump/pf_print_state.c @@ -0,0 +1,288 @@ +/* $OpenBSD: pf_print_state.c,v 1.1 2004/01/28 19:44:55 canacar Exp $ */ + +/* + * Copyright (c) 2001 Daniel Hartmeier + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <net/if.h> +#define TCPSTATES +#include <netinet/tcp_fsm.h> +#include <net/pfvar.h> +#include <arpa/inet.h> +#include <netdb.h> + +#include <stdio.h> +#include <string.h> + +#include "pfctl_parser.h" +#include "pfctl.h" +#include "addrtoname.h" + +void print_name(struct pf_addr *, sa_family_t); + +void +print_addr(struct pf_addr_wrap *addr, sa_family_t af, int verbose) +{ + switch(addr->type) { + case PF_ADDR_DYNIFTL: + printf("(%s", addr->v.ifname); + if (addr->iflags & PFI_AFLAG_NETWORK) + printf(":network"); + if (addr->iflags & PFI_AFLAG_BROADCAST) + printf(":broadcast"); + if (addr->iflags & PFI_AFLAG_PEER) + printf(":peer"); + if (addr->iflags & PFI_AFLAG_NOALIAS) + printf(":0"); + if (verbose) { + if (addr->p.dyncnt <= 0) + printf(":*"); + else + printf(":%d", addr->p.dyncnt); + } + printf(")"); + break; + case PF_ADDR_TABLE: + if (verbose) + if (addr->p.tblcnt == -1) + printf("<%s:*>", addr->v.tblname); + else + printf("<%s:%d>", addr->v.tblname, + addr->p.tblcnt); + else + printf("<%s>", addr->v.tblname); + return; + case PF_ADDR_ADDRMASK: + if (PF_AZERO(&addr->v.a.addr, AF_INET6) && + PF_AZERO(&addr->v.a.mask, AF_INET6)) + printf("any"); + else { + char buf[48]; + + if (inet_ntop(af, &addr->v.a.addr, buf, + sizeof(buf)) == NULL) + printf("?"); + else + printf("%s", buf); + } + break; + case PF_ADDR_NOROUTE: + printf("no-route"); + return; + default: + printf("?"); + return; + } + if (! PF_AZERO(&addr->v.a.mask, af)) { + int bits = unmask(&addr->v.a.mask, af); + + if (bits != (af == AF_INET ? 32 : 128)) + printf("/%d", bits); + } +} + +void +print_name(struct pf_addr *addr, sa_family_t af) +{ + char *host; + + switch (af) { + case AF_INET: + host = getname((char *)&addr->v4); + break; + case AF_INET6: + host = getname6((char *)&addr->v6); + break; + default: + host = "?"; + break; + } + printf("%s", host); +} + +void +print_host(struct pf_state_host *h, sa_family_t af, int opts) +{ + u_int16_t p = ntohs(h->port); + + if (opts & PF_OPT_USEDNS) + print_name(&h->addr, af); + else { + struct pf_addr_wrap aw; + + memset(&aw, 0, sizeof(aw)); + aw.v.a.addr = h->addr; + if (af == AF_INET) + aw.v.a.mask.addr32[0] = 0xffffffff; + else { + memset(&aw.v.a.mask, 0xff, sizeof(aw.v.a.mask)); + af = AF_INET6; + } + print_addr(&aw, af, opts & PF_OPT_VERBOSE2); + } + + if (p) { + if (af == AF_INET) + printf(":%u", p); + else + printf("[%u]", p); + } +} + +void +print_seq(struct pf_state_peer *p) +{ + if (p->seqdiff) + printf("[%u + %u](+%u)", p->seqlo, p->seqhi - p->seqlo, + p->seqdiff); + else + printf("[%u + %u]", p->seqlo, p->seqhi - p->seqlo); +} + +void +print_state(struct pf_state *s, int opts) +{ + struct pf_state_peer *src, *dst; + int min, sec; + + if (s->direction == PF_OUT) { + src = &s->src; + dst = &s->dst; + } else { + src = &s->dst; + dst = &s->src; + } + printf("%s ", s->u.ifname); + printf("%s ", ipproto_string(s->proto)); + if (PF_ANEQ(&s->lan.addr, &s->gwy.addr, s->af) || + (s->lan.port != s->gwy.port)) { + print_host(&s->lan, s->af, opts); + if (s->direction == PF_OUT) + printf(" -> "); + else + printf(" <- "); + } + print_host(&s->gwy, s->af, opts); + if (s->direction == PF_OUT) + printf(" -> "); + else + printf(" <- "); + print_host(&s->ext, s->af, opts); + + printf(" "); + if (s->proto == IPPROTO_TCP) { + if (src->state <= TCPS_TIME_WAIT && + dst->state <= TCPS_TIME_WAIT) + printf(" %s:%s\n", tcpstates[src->state], + tcpstates[dst->state]); + else if (src->state == PF_TCPS_PROXY_SRC || + dst->state == PF_TCPS_PROXY_SRC) + printf(" PROXY:SRC\n"); + else if (src->state == PF_TCPS_PROXY_DST || + dst->state == PF_TCPS_PROXY_DST) + printf(" PROXY:DST\n"); + else + printf(" <BAD STATE LEVELS %u:%u>\n", + src->state, dst->state); + if (opts & PF_OPT_VERBOSE) { + printf(" "); + print_seq(src); + if (src->wscale && dst->wscale) + printf(" wscale %u", + src->wscale & PF_WSCALE_MASK); + printf(" "); + print_seq(dst); + if (src->wscale && dst->wscale) + printf(" wscale %u", + dst->wscale & PF_WSCALE_MASK); + printf("\n"); + } + } else if (s->proto == IPPROTO_UDP && src->state < PFUDPS_NSTATES && + dst->state < PFUDPS_NSTATES) { + const char *states[] = PFUDPS_NAMES; + + printf(" %s:%s\n", states[src->state], states[dst->state]); + } else if (s->proto != IPPROTO_ICMP && src->state < PFOTHERS_NSTATES && + dst->state < PFOTHERS_NSTATES) { + /* XXX ICMP doesn't really have state levels */ + const char *states[] = PFOTHERS_NAMES; + + printf(" %s:%s\n", states[src->state], states[dst->state]); + } else { + printf(" %u:%u\n", src->state, dst->state); + } + + if (opts & PF_OPT_VERBOSE) { + sec = s->creation % 60; + s->creation /= 60; + min = s->creation % 60; + s->creation /= 60; + printf(" age %.2u:%.2u:%.2u", s->creation, min, sec); + sec = s->expire % 60; + s->expire /= 60; + min = s->expire % 60; + s->expire /= 60; + printf(", expires in %.2u:%.2u:%.2u", s->expire, min, sec); + printf(", %u:%u pkts, %u:%u bytes", + s->packets[0], s->packets[1], s->bytes[0], s->bytes[1]); + if (s->anchor.nr != -1) + printf(", anchor %u", s->anchor.nr); + if (s->rule.nr != -1) + printf(", rule %u", s->rule.nr); + if (s->src_node != NULL) + printf(", source-track"); + if (s->nat_src_node != NULL) + printf(", sticky-address"); + printf("\n"); + } + if (opts & PF_OPT_VERBOSE2) { + printf(" id: %016llx creatorid: %08x\n", + betoh64(s->id), ntohl(s->creatorid)); + } +} + +int +unmask(struct pf_addr *m, sa_family_t af) +{ + int i = 31, j = 0, b = 0; + u_int32_t tmp; + + while (j < 4 && m->addr32[j] == 0xffffffff) { + b += 32; + j++; + } + if (j < 4) { + tmp = ntohl(m->addr32[j]); + for (i = 31; tmp & (1 << i); --i) + b++; + } + return (b); +} diff --git a/usr.sbin/tcpdump/pfctl_osfp.c b/usr.sbin/tcpdump/pfctl_osfp.c new file mode 100644 index 00000000000..2433c7adc18 --- /dev/null +++ b/usr.sbin/tcpdump/pfctl_osfp.c @@ -0,0 +1,1093 @@ +/* $OpenBSD: pfctl_osfp.c,v 1.1 2004/01/28 19:44:55 canacar Exp $ */ + +/* + * Copyright (c) 2003 Mike Frantzen <frantzen@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/socket.h> + +#include <net/if.h> +#include <net/pfvar.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "privsep.h" +#include "pfctl_parser.h" + +#ifndef MIN +# define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#endif /* MIN */ +#ifndef MAX +# define MAX(a,b) (((a) > (b)) ? (a) : (b)) +#endif /* MAX */ + + +#if 0 +# define DEBUG(fp, str, v...) \ + fprintf(stderr, "%s:%s:%s " str "\n", (fp)->fp_os.fp_class_nm, \ + (fp)->fp_os.fp_version_nm, (fp)->fp_os.fp_subtype_nm , ## v); +#else +# define DEBUG(fp, str, v...) ((void)0) +#endif + + +struct name_entry; +LIST_HEAD(name_list, name_entry); +struct name_entry { + LIST_ENTRY(name_entry) nm_entry; + int nm_num; + char nm_name[PF_OSFP_LEN]; + + struct name_list nm_sublist; + int nm_sublist_num; +}; +struct name_list classes = LIST_HEAD_INITIALIZER(&classes); +int class_count; +int fingerprint_count; + +void add_fingerprint(int, int, struct pf_osfp_ioctl *); +struct name_entry *fingerprint_name_entry(struct name_list *, char *); +void pfctl_flush_my_fingerprints(struct name_list *); +char *get_field(char **, size_t *, int *); +int get_int(char **, size_t *, int *, int *, const char *, + int, int, const char *, int); +int get_str(char **, size_t *, char **, const char *, int, + const char *, int); +int get_tcpopts(const char *, int, const char *, + pf_tcpopts_t *, int *, int *, int *, int *, int *, + int *); +void import_fingerprint(struct pf_osfp_ioctl *); +const char *print_ioctl(struct pf_osfp_ioctl *); +void print_name_list(int, struct name_list *, const char *); +void sort_name_list(int, struct name_list *); +struct name_entry *lookup_name_list(struct name_list *, const char *); + +/* XXX arbitrary */ +#define MAX_FP_LINE 1024 + +/* Load fingerprints from a file */ +int +pfctl_file_fingerprints(int dev, int opts, const char *fp_filename) +{ + char buf[MAX_FP_LINE]; + char *line; + size_t len; + int i, lineno = 0; + int window, w_mod, ttl, df, psize, p_mod, mss, mss_mod, wscale, + wscale_mod, optcnt, ts0; + pf_tcpopts_t packed_tcpopts; + char *class, *version, *subtype, *desc, *tcpopts; + struct pf_osfp_ioctl fp; + + pfctl_flush_my_fingerprints(&classes); + class = version = subtype = desc = tcpopts = NULL; + + if ((opts & PF_OPT_NOACTION) == 0) + pfctl_clear_fingerprints(dev, opts); + + priv_getlines(FTAB_PFOSFP); + while ((len = priv_getline(buf, sizeof(buf))) > 0) { + buf[len -1] = '\n'; + line = buf; + lineno++; + if (class) + free(class); + if (version) + free(version); + if (subtype) + free(subtype); + if (desc) + free(desc); + if (tcpopts) + free(tcpopts); + class = version = subtype = desc = tcpopts = NULL; + memset(&fp, 0, sizeof(fp)); + + /* Chop off comment */ + for (i = 0; i < len; i++) + if (line[i] == '#') { + len = i; + break; + } + /* Chop off whitespace */ + while (len > 0 && isspace(line[len - 1])) + len--; + while (len > 0 && isspace(line[0])) { + len--; + line++; + } + if (len == 0) + continue; + +#define T_DC 0x01 /* Allow don't care */ +#define T_MSS 0x02 /* Allow MSS multiple */ +#define T_MTU 0x04 /* Allow MTU multiple */ +#define T_MOD 0x08 /* Allow modulus */ + +#define GET_INT(v, mod, n, ty, mx) \ + get_int(&line, &len, &v, mod, n, ty, mx, fp_filename, lineno) +#define GET_STR(v, n, mn) \ + get_str(&line, &len, &v, n, mn, fp_filename, lineno) + + if (GET_INT(window, &w_mod, "window size", T_DC|T_MSS|T_MTU| + T_MOD, 0xffff) || + GET_INT(ttl, NULL, "ttl", 0, 0xff) || + GET_INT(df, NULL, "don't fragment frag", 0, 1) || + GET_INT(psize, &p_mod, "overall packet size", T_MOD|T_DC, + 8192) || + GET_STR(tcpopts, "TCP Options", 1) || + GET_STR(class, "OS class", 1) || + GET_STR(version, "OS version", 0) || + GET_STR(subtype, "OS subtype", 0) || + GET_STR(desc, "OS description", 2)) + continue; + if (get_tcpopts(fp_filename, lineno, tcpopts, &packed_tcpopts, + &optcnt, &mss, &mss_mod, &wscale, &wscale_mod, &ts0)) + continue; + if (len != 0) { + fprintf(stderr, "%s:%d excess field\n", fp_filename, + lineno); + continue; + } + + fp.fp_ttl = ttl; + if (df) + fp.fp_flags |= PF_OSFP_DF; + switch (w_mod) { + case 0: + break; + case T_DC: + fp.fp_flags |= PF_OSFP_WSIZE_DC; + break; + case T_MSS: + fp.fp_flags |= PF_OSFP_WSIZE_MSS; + break; + case T_MTU: + fp.fp_flags |= PF_OSFP_WSIZE_MTU; + break; + case T_MOD: + fp.fp_flags |= PF_OSFP_WSIZE_MOD; + break; + } + fp.fp_wsize = window; + + switch (p_mod) { + case T_DC: + fp.fp_flags |= PF_OSFP_PSIZE_DC; + break; + case T_MOD: + fp.fp_flags |= PF_OSFP_PSIZE_MOD; + } + fp.fp_psize = psize; + + + switch (wscale_mod) { + case T_DC: + fp.fp_flags |= PF_OSFP_WSCALE_DC; + break; + case T_MOD: + fp.fp_flags |= PF_OSFP_WSCALE_MOD; + } + fp.fp_wscale = wscale; + + switch (mss_mod) { + case T_DC: + fp.fp_flags |= PF_OSFP_MSS_DC; + break; + case T_MOD: + fp.fp_flags |= PF_OSFP_MSS_MOD; + break; + } + fp.fp_mss = mss; + + fp.fp_tcpopts = packed_tcpopts; + fp.fp_optcnt = optcnt; + if (ts0) + fp.fp_flags |= PF_OSFP_TS0; + + if (class[0] == '@') + fp.fp_os.fp_enflags |= PF_OSFP_GENERIC; + if (class[0] == '*') + fp.fp_os.fp_enflags |= PF_OSFP_NODETAIL; + + if (class[0] == '@' || class[0] == '*') + strlcpy(fp.fp_os.fp_class_nm, class + 1, + sizeof(fp.fp_os.fp_class_nm)); + else + strlcpy(fp.fp_os.fp_class_nm, class, + sizeof(fp.fp_os.fp_class_nm)); + strlcpy(fp.fp_os.fp_version_nm, version, + sizeof(fp.fp_os.fp_version_nm)); + strlcpy(fp.fp_os.fp_subtype_nm, subtype, + sizeof(fp.fp_os.fp_subtype_nm)); + + add_fingerprint(dev, opts, &fp); + } + + if (class) + free(class); + if (version) + free(version); + if (subtype) + free(subtype); + if (desc) + free(desc); + + if (opts & PF_OPT_VERBOSE2) + printf("Loaded %d passive OS fingerprints\n", + fingerprint_count); + return (0); +} + +/* flush the kernel's fingerprints */ +void +pfctl_clear_fingerprints(int dev, int opts) +{ + if (ioctl(dev, DIOCOSFPFLUSH)) + err(1, "DIOCOSFPFLUSH"); +} + +/* flush pfctl's view of the fingerprints */ +void +pfctl_flush_my_fingerprints(struct name_list *list) +{ + struct name_entry *nm; + + while ((nm = LIST_FIRST(list)) != NULL) { + LIST_REMOVE(nm, nm_entry); + pfctl_flush_my_fingerprints(&nm->nm_sublist); + fingerprint_count--; + free(nm); + } + class_count = 0; +} + +/* Fetch the active fingerprints from the kernel */ +int +pfctl_load_fingerprints(int dev, int opts) +{ + struct pf_osfp_ioctl io; + int i; + + pfctl_flush_my_fingerprints(&classes); + + for (i = 0; i >= 0; i++) { + memset(&io, 0, sizeof(io)); + io.fp_getnum = i; + if (ioctl(dev, DIOCOSFPGET, &io)) { + if (errno == EBUSY) + break; + warn("DIOCOSFPGET"); + return (1); + } + import_fingerprint(&io); + } + return (0); +} + +/* List the fingerprints */ +void +pfctl_show_fingerprints(int opts) +{ + printf("Passive OS Fingerprints:\n"); + printf("\tClass\tVersion\tSubtype(subversion)\n"); + printf("\t-----\t-------\t-------------------\n"); + sort_name_list(opts, &classes); + print_name_list(opts, &classes, "\t"); +} + +/* Lookup a fingerprint */ +pf_osfp_t +pfctl_get_fingerprint(const char *name) +{ + struct name_entry *nm, *class_nm, *version_nm, *subtype_nm; + pf_osfp_t ret = PF_OSFP_NOMATCH; + int class, version, subtype; + int unp_class, unp_version, unp_subtype; + int wr_len, version_len, subtype_len; + char *ptr, *wr_name; + + if (strcasecmp(name, "unknown") == 0) + return (PF_OSFP_UNKNOWN); + + /* Try most likely no version and no subtype */ + if ((nm = lookup_name_list(&classes, name))) { + class = nm->nm_num; + version = PF_OSFP_ANY; + subtype = PF_OSFP_ANY; + goto found; + } else { + + /* Chop it up into class/version/subtype */ + + if ((wr_name = strdup(name)) == NULL) + err(1, "malloc"); + if ((ptr = index(wr_name, ' ')) == NULL) { + free(wr_name); + return (PF_OSFP_NOMATCH); + } + *ptr++ = '\0'; + + /* The class is easy to find since it is delimited by a space */ + if ((class_nm = lookup_name_list(&classes, wr_name)) == NULL) { + free(wr_name); + return (PF_OSFP_NOMATCH); + } + class = class_nm->nm_num; + + /* Try no subtype */ + if ((version_nm = lookup_name_list(&class_nm->nm_sublist, ptr))) + { + version = version_nm->nm_num; + subtype = PF_OSFP_ANY; + free(wr_name); + goto found; + } + + + /* + * There must be a version and a subtype. + * We'll do some fuzzy matching to pick up things like: + * Linux 2.2.14 (version=2.2 subtype=14) + * FreeBSD 4.0-STABLE (version=4.0 subtype=STABLE) + * Windows 2000 SP2 (version=2000 subtype=SP2) + */ +#define CONNECTOR(x) ((x) == '.' || (x) == ' ' || (x) == '\t' || (x) == '-') + wr_len = strlen(ptr); + LIST_FOREACH(version_nm, &class_nm->nm_sublist, nm_entry) { + version_len = strlen(version_nm->nm_name); + if (wr_len < version_len + 2 || + !CONNECTOR(ptr[version_len])) + continue; + /* first part of the string must be version */ + if (strncasecmp(ptr, version_nm->nm_name, + version_len)) + continue; + + LIST_FOREACH(subtype_nm, &version_nm->nm_sublist, + nm_entry) { + subtype_len = strlen(subtype_nm->nm_name); + if (wr_len != version_len + subtype_len + 1) + continue; + + /* last part of the string must be subtype */ + if (strcasecmp(&ptr[version_len+1], + subtype_nm->nm_name) != 0) + continue; + + /* Found it!! */ + version = version_nm->nm_num; + subtype = subtype_nm->nm_num; + free(wr_name); + goto found; + } + } + + free(wr_name); + return (PF_OSFP_NOMATCH); + } + +found: + PF_OSFP_PACK(ret, class, version, subtype); + if (ret != PF_OSFP_NOMATCH) { + PF_OSFP_UNPACK(ret, unp_class, unp_version, unp_subtype); + if (class != unp_class) { + fprintf(stderr, "warning: fingerprint table overflowed " + "classes\n"); + return (PF_OSFP_NOMATCH); + } + if (version != unp_version) { + fprintf(stderr, "warning: fingerprint table overflowed " + "versions\n"); + return (PF_OSFP_NOMATCH); + } + if (subtype != unp_subtype) { + fprintf(stderr, "warning: fingerprint table overflowed " + "subtypes\n"); + return (PF_OSFP_NOMATCH); + } + } + if (ret == PF_OSFP_ANY) { + /* should never happen */ + fprintf(stderr, "warning: fingerprint packed to 'any'\n"); + return (PF_OSFP_NOMATCH); + } + + return (ret); +} + +/* Lookup a fingerprint name by ID */ +char * +pfctl_lookup_fingerprint(pf_osfp_t fp, char *buf, size_t len) +{ + int class, version, subtype; + struct name_list *list; + struct name_entry *nm; + + char *class_name, *version_name, *subtype_name; + class_name = version_name = subtype_name = NULL; + + if (fp == PF_OSFP_UNKNOWN) { + strlcpy(buf, "unknown", len); + return (buf); + } + if (fp == PF_OSFP_ANY) { + strlcpy(buf, "any", len); + return (buf); + } + + PF_OSFP_UNPACK(fp, class, version, subtype); + if (class >= (1 << _FP_CLASS_BITS) || + version >= (1 << _FP_VERSION_BITS) || + subtype >= (1 << _FP_SUBTYPE_BITS)) { + warnx("PF_OSFP_UNPACK(0x%x) failed!!", fp); + strlcpy(buf, "nomatch", len); + return (buf); + } + + LIST_FOREACH(nm, &classes, nm_entry) { + if (nm->nm_num == class) { + class_name = nm->nm_name; + if (version == PF_OSFP_ANY) + goto found; + list = &nm->nm_sublist; + LIST_FOREACH(nm, list, nm_entry) { + if (nm->nm_num == version) { + version_name = nm->nm_name; + if (subtype == PF_OSFP_ANY) + goto found; + list = &nm->nm_sublist; + LIST_FOREACH(nm, list, nm_entry) { + if (nm->nm_num == subtype) { + subtype_name = + nm->nm_name; + goto found; + } + } /* foreach subtype */ + strlcpy(buf, "nomatch", len); + return (buf); + } + } /* foreach version */ + strlcpy(buf, "nomatch", len); + return (buf); + } + } /* foreach class */ + + strlcpy(buf, "nomatch", len); + return (buf); + +found: + snprintf(buf, len, "%s", class_name); + if (version_name) { + strlcat(buf, " ", len); + strlcat(buf, version_name, len); + if (subtype_name) { + if (index(version_name, ' ')) + strlcat(buf, " ", len); + else if (index(version_name, '.') && + isdigit(*subtype_name)) + strlcat(buf, ".", len); + else + strlcat(buf, " ", len); + strlcat(buf, subtype_name, len); + } + } + return (buf); +} + +/* lookup a name in a list */ +struct name_entry * +lookup_name_list(struct name_list *list, const char *name) +{ + struct name_entry *nm; + LIST_FOREACH(nm, list, nm_entry) + if (strcasecmp(name, nm->nm_name) == 0) + return (nm); + + return (NULL); +} + + +void +add_fingerprint(int dev, int opts, struct pf_osfp_ioctl *fp) +{ + struct pf_osfp_ioctl fptmp; + struct name_entry *nm_class, *nm_version, *nm_subtype; + int class, version, subtype; + +/* We expand #-# or #.#-#.# version/subtypes into multiple fingerprints */ +#define EXPAND(field) do { \ + int _dot = -1, _start = -1, _end = -1, _i = 0; \ + /* pick major version out of #.# */ \ + if (isdigit(fp->field[_i]) && fp->field[_i+1] == '.') { \ + _dot = fp->field[_i] - '0'; \ + _i += 2; \ + } \ + if (isdigit(fp->field[_i])) \ + _start = fp->field[_i++] - '0'; \ + else \ + break; \ + if (isdigit(fp->field[_i])) \ + _start = (_start * 10) + fp->field[_i++] - '0'; \ + if (fp->field[_i++] != '-') \ + break; \ + if (isdigit(fp->field[_i]) && fp->field[_i+1] == '.' && \ + fp->field[_i] - '0' == _dot) \ + _i += 2; \ + else if (_dot != -1) \ + break; \ + if (isdigit(fp->field[_i])) \ + _end = fp->field[_i++] - '0'; \ + else \ + break; \ + if (isdigit(fp->field[_i])) \ + _end = (_end * 10) + fp->field[_i++] - '0'; \ + if (isdigit(fp->field[_i])) \ + _end = (_end * 10) + fp->field[_i++] - '0'; \ + if (fp->field[_i] != '\0') \ + break; \ + memcpy(&fptmp, fp, sizeof(fptmp)); \ + for (;_start <= _end; _start++) { \ + memset(fptmp.field, 0, sizeof(fptmp.field)); \ + fptmp.fp_os.fp_enflags |= PF_OSFP_EXPANDED; \ + if (_dot == -1) \ + snprintf(fptmp.field, sizeof(fptmp.field), \ + "%d", _start); \ + else \ + snprintf(fptmp.field, sizeof(fptmp.field), \ + "%d.%d", _dot, _start); \ + add_fingerprint(dev, opts, &fptmp); \ + } \ +} while(0) + + /* We allow "#-#" as a version or subtype and we'll expand it */ + EXPAND(fp_os.fp_version_nm); + EXPAND(fp_os.fp_subtype_nm); + + if (strcasecmp(fp->fp_os.fp_class_nm, "nomatch") == 0) + errx(1, "fingerprint class \"nomatch\" is reserved"); + + version = PF_OSFP_ANY; + subtype = PF_OSFP_ANY; + + nm_class = fingerprint_name_entry(&classes, fp->fp_os.fp_class_nm); + if (nm_class->nm_num == 0) + nm_class->nm_num = ++class_count; + class = nm_class->nm_num; + + nm_version = fingerprint_name_entry(&nm_class->nm_sublist, + fp->fp_os.fp_version_nm); + if (nm_version) { + if (nm_version->nm_num == 0) + nm_version->nm_num = ++nm_class->nm_sublist_num; + version = nm_version->nm_num; + nm_subtype = fingerprint_name_entry(&nm_version->nm_sublist, + fp->fp_os.fp_subtype_nm); + if (nm_subtype) { + if (nm_subtype->nm_num == 0) + nm_subtype->nm_num = + ++nm_version->nm_sublist_num; + subtype = nm_subtype->nm_num; + } + } + + + DEBUG(fp, "\tsignature %d:%d:%d %s", class, version, subtype, + print_ioctl(fp)); + + PF_OSFP_PACK(fp->fp_os.fp_os, class, version, subtype); + fingerprint_count++; + +#ifdef FAKE_PF_KERNEL + /* Linked to the sys/net/pf_osfp.c. Call pf_osfp_add() */ + if ((errno = pf_osfp_add(fp))) +#else + if ((opts & PF_OPT_NOACTION) == 0 && ioctl(dev, DIOCOSFPADD, fp)) +#endif /* FAKE_PF_KERNEL */ + { + if (errno == EEXIST) { + warn("Duplicate signature for %s %s %s", + fp->fp_os.fp_class_nm, + fp->fp_os.fp_version_nm, + fp->fp_os.fp_subtype_nm); + + } else { + err(1, "DIOCOSFPADD"); + } + } +} + +/* import a fingerprint from the kernel */ +void +import_fingerprint(struct pf_osfp_ioctl *fp) +{ + struct name_entry *nm_class, *nm_version, *nm_subtype; + int class, version, subtype; + + PF_OSFP_UNPACK(fp->fp_os.fp_os, class, version, subtype); + + nm_class = fingerprint_name_entry(&classes, fp->fp_os.fp_class_nm); + if (nm_class->nm_num == 0) { + nm_class->nm_num = class; + class_count = MAX(class_count, class); + } + + nm_version = fingerprint_name_entry(&nm_class->nm_sublist, + fp->fp_os.fp_version_nm); + if (nm_version) { + if (nm_version->nm_num == 0) { + nm_version->nm_num = version; + nm_class->nm_sublist_num = MAX(nm_class->nm_sublist_num, + version); + } + nm_subtype = fingerprint_name_entry(&nm_version->nm_sublist, + fp->fp_os.fp_subtype_nm); + if (nm_subtype) { + if (nm_subtype->nm_num == 0) { + nm_subtype->nm_num = subtype; + nm_version->nm_sublist_num = + MAX(nm_version->nm_sublist_num, subtype); + } + } + } + + + fingerprint_count++; + DEBUG(fp, "import signature %d:%d:%d", class, version, subtype); +} + +/* Find an entry for a fingerprints class/version/subtype */ +struct name_entry * +fingerprint_name_entry(struct name_list *list, char *name) +{ + struct name_entry *nm_entry; + + if (name == NULL || strlen(name) == 0) + return (NULL); + + LIST_FOREACH(nm_entry, list, nm_entry) { + if (strcasecmp(nm_entry->nm_name, name) == 0) { + /* We'll move this to the front of the list later */ + LIST_REMOVE(nm_entry, nm_entry); + break; + } + } + if (nm_entry == NULL) { + nm_entry = calloc(1, sizeof(*nm_entry)); + if (nm_entry == NULL) + err(1, "calloc"); + LIST_INIT(&nm_entry->nm_sublist); + strlcpy(nm_entry->nm_name, name, + sizeof(nm_entry->nm_name)); + } + LIST_INSERT_HEAD(list, nm_entry, nm_entry); + return (nm_entry); +} + + +void +print_name_list(int opts, struct name_list *nml, const char *prefix) +{ + char newprefix[32]; + struct name_entry *nm; + + LIST_FOREACH(nm, nml, nm_entry) { + snprintf(newprefix, sizeof(newprefix), "%s%s\t", prefix, + nm->nm_name); + printf("%s\n", newprefix); + print_name_list(opts, &nm->nm_sublist, newprefix); + } +} + +void +sort_name_list(int opts, struct name_list *nml) +{ + struct name_list new; + struct name_entry *nm, *nmsearch, *nmlast; + + /* yes yes, it's a very slow sort. so sue me */ + + LIST_INIT(&new); + + while ((nm = LIST_FIRST(nml)) != NULL) { + LIST_REMOVE(nm, nm_entry); + nmlast = NULL; + LIST_FOREACH(nmsearch, &new, nm_entry) { + if (strcasecmp(nmsearch->nm_name, nm->nm_name) > 0) { + LIST_INSERT_BEFORE(nmsearch, nm, nm_entry); + break; + } + nmlast = nmsearch; + } + if (nmsearch == NULL) { + if (nmlast) + LIST_INSERT_AFTER(nmlast, nm, nm_entry); + else + LIST_INSERT_HEAD(&new, nm, nm_entry); + } + + sort_name_list(opts, &nm->nm_sublist); + } + nmlast = NULL; + while ((nm = LIST_FIRST(&new)) != NULL) { + LIST_REMOVE(nm, nm_entry); + if (nmlast == NULL) + LIST_INSERT_HEAD(nml, nm, nm_entry); + else + LIST_INSERT_AFTER(nmlast, nm, nm_entry); + nmlast = nm; + } + return; +} + +/* parse the next integer in a formatted config file line */ +int +get_int(char **line, size_t *len, int *var, int *mod, + const char *name, int flags, int max, const char *filename, int lineno) +{ + int fieldlen, i; + char *field; + long val = 0; + + if (mod) + *mod = 0; + *var = 0; + + field = get_field(line, len, &fieldlen); + if (field == NULL) + return (1); + if (fieldlen == 0) { + fprintf(stderr, "%s:%d empty %s\n", filename, lineno, name); + return (1); + } + + i = 0; + if ((*field == '%' || *field == 'S' || *field == 'T' || *field == '*') + && fieldlen >= 1) { + switch (*field) { + case 'S': + if (mod && (flags & T_MSS)) + *mod = T_MSS; + if (fieldlen == 1) + return (0); + break; + case 'T': + if (mod && (flags & T_MTU)) + *mod = T_MTU; + if (fieldlen == 1) + return (0); + break; + case '*': + if (fieldlen != 1) { + fprintf(stderr, "%s:%d long '%c' %s\n", + filename, lineno, *field, name); + return (1); + } + if (mod && (flags & T_DC)) { + *mod = T_DC; + return (0); + } + case '%': + if (mod && (flags & T_MOD)) + *mod = T_MOD; + if (fieldlen == 1) { + fprintf(stderr, "%s:%d modulus %s must have a " + "value\n", filename, lineno, name); + return (1); + } + break; + } + if (mod == NULL || *mod == 0) { + fprintf(stderr, "%s:%d does not allow %c' %s\n", + filename, lineno, *field, name); + return (1); + } + i++; + } + + for (; i < fieldlen; i++) { + if (field[i] < '0' || field[i] > '9') { + fprintf(stderr, "%s:%d non-digit character in %s\n", + filename, lineno, name); + return (1); + } + val = val * 10 + field[i] - '0'; + if (val < 0) { + fprintf(stderr, "%s:%d %s overflowed\n", filename, + lineno, name); + return (1); + } + } + + if (val > max) { + fprintf(stderr, "%s:%d %s value %ld > %d\n", filename, lineno, + name, val, max); + return (1); + } + *var = (int)val; + + return (0); +} + +/* parse the next string in a formatted config file line */ +int +get_str(char **line, size_t *len, char **v, const char *name, int minlen, + const char *filename, int lineno) +{ + int fieldlen; + char *ptr; + + ptr = get_field(line, len, &fieldlen); + if (ptr == NULL) + return (1); + if (fieldlen < minlen) { + fprintf(stderr, "%s:%d too short %s\n", filename, lineno, name); + return (1); + } + if ((*v = malloc(fieldlen + 1)) == NULL) { + perror("malloc()"); + return (1); + } + memcpy(*v, ptr, fieldlen); + (*v)[fieldlen] = '\0'; + + return (0); +} + +/* Parse out the TCP opts */ +int +get_tcpopts(const char *filename, int lineno, const char *tcpopts, + pf_tcpopts_t *packed, int *optcnt, int *mss, int *mss_mod, int *wscale, + int *wscale_mod, int *ts0) +{ + int i, opt; + + *packed = 0; + *optcnt = 0; + *wscale = 0; + *wscale_mod = T_DC; + *mss = 0; + *mss_mod = T_DC; + *ts0 = 0; + if (strcmp(tcpopts, ".") == 0) + return (0); + + for (i = 0; tcpopts[i] && *optcnt < PF_OSFP_MAX_OPTS;) { + switch ((opt = toupper(tcpopts[i++]))) { + case 'N': /* FALLTHROUGH */ + case 'S': + *packed = (*packed << PF_OSFP_TCPOPT_BITS) | + (opt == 'N' ? PF_OSFP_TCPOPT_NOP : + PF_OSFP_TCPOPT_SACK); + break; + case 'W': /* FALLTHROUGH */ + case 'M': { + int *this_mod, *this; + + if (opt == 'W') { + this = wscale; + this_mod = wscale_mod; + } else { + this = mss; + this_mod = mss_mod; + } + *this = 0; + *this_mod = 0; + + *packed = (*packed << PF_OSFP_TCPOPT_BITS) | + (opt == 'W' ? PF_OSFP_TCPOPT_WSCALE : + PF_OSFP_TCPOPT_MSS); + if (tcpopts[i] == '*' && (tcpopts[i + 1] == '\0' || + tcpopts[i + 1] == ',')) { + *this_mod = T_DC; + i++; + break; + } + + if (tcpopts[i] == '%') { + *this_mod = T_MOD; + i++; + } + do { + if (!isdigit(tcpopts[i])) { + fprintf(stderr, "%s:%d unknown " + "character '%c' in %c TCP opt\n", + filename, lineno, tcpopts[i], opt); + return (1); + } + *this = (*this * 10) + tcpopts[i++] - '0'; + } while(tcpopts[i] != ',' && tcpopts[i] != '\0'); + break; + } + case 'T': + if (tcpopts[i] == '0') { + *ts0 = 1; + i++; + } + *packed = (*packed << PF_OSFP_TCPOPT_BITS) | + PF_OSFP_TCPOPT_TS; + break; + } + (*optcnt) ++; + if (tcpopts[i] == '\0') + break; + if (tcpopts[i] != ',') { + fprintf(stderr, "%s:%d unknown option to %c TCP opt\n", + filename, lineno, opt); + return (1); + } + i++; + } + + return (0); +} + +/* rip the next field ouf of a formatted config file line */ +char * +get_field(char **line, size_t *len, int *fieldlen) +{ + char *ret, *ptr = *line; + size_t plen = *len; + + + while (plen && isspace(*ptr)) { + plen--; + ptr++; + } + ret = ptr; + *fieldlen = 0; + + for (; plen > 0 && *ptr != ':'; plen--, ptr++) + (*fieldlen)++; + if (plen) { + *line = ptr + 1; + *len = plen - 1; + } else { + *len = 0; + } + while (*fieldlen && isspace(ret[*fieldlen - 1])) + (*fieldlen)--; + return (ret); +} + + +const char * +print_ioctl(struct pf_osfp_ioctl *fp) +{ + static char buf[1024]; + char tmp[32]; + int i, opt; + + *buf = '\0'; + if (fp->fp_flags & PF_OSFP_WSIZE_DC) + strlcat(buf, "*", sizeof(buf)); + else if (fp->fp_flags & PF_OSFP_WSIZE_MSS) + strlcat(buf, "S", sizeof(buf)); + else if (fp->fp_flags & PF_OSFP_WSIZE_MTU) + strlcat(buf, "T", sizeof(buf)); + else { + if (fp->fp_flags & PF_OSFP_WSIZE_MOD) + strlcat(buf, "%", sizeof(buf)); + snprintf(tmp, sizeof(tmp), "%d", fp->fp_wsize); + strlcat(buf, tmp, sizeof(buf)); + } + strlcat(buf, ":", sizeof(buf)); + + snprintf(tmp, sizeof(tmp), "%d", fp->fp_ttl); + strlcat(buf, tmp, sizeof(buf)); + strlcat(buf, ":", sizeof(buf)); + + if (fp->fp_flags & PF_OSFP_DF) + strlcat(buf, "1", sizeof(buf)); + else + strlcat(buf, "0", sizeof(buf)); + strlcat(buf, ":", sizeof(buf)); + + if (fp->fp_flags & PF_OSFP_PSIZE_DC) + strlcat(buf, "*", sizeof(buf)); + else { + if (fp->fp_flags & PF_OSFP_PSIZE_MOD) + strlcat(buf, "%", sizeof(buf)); + snprintf(tmp, sizeof(tmp), "%d", fp->fp_psize); + strlcat(buf, tmp, sizeof(buf)); + } + strlcat(buf, ":", sizeof(buf)); + + if (fp->fp_optcnt == 0) + strlcat(buf, ".", sizeof(buf)); + for (i = fp->fp_optcnt - 1; i >= 0; i--) { + opt = fp->fp_tcpopts >> (i * PF_OSFP_TCPOPT_BITS); + opt &= (1 << PF_OSFP_TCPOPT_BITS) - 1; + switch (opt) { + case PF_OSFP_TCPOPT_NOP: + strlcat(buf, "N", sizeof(buf)); + break; + case PF_OSFP_TCPOPT_SACK: + strlcat(buf, "S", sizeof(buf)); + break; + case PF_OSFP_TCPOPT_TS: + strlcat(buf, "T", sizeof(buf)); + if (fp->fp_flags & PF_OSFP_TS0) + strlcat(buf, "0", sizeof(buf)); + break; + case PF_OSFP_TCPOPT_MSS: + strlcat(buf, "M", sizeof(buf)); + if (fp->fp_flags & PF_OSFP_MSS_DC) + strlcat(buf, "*", sizeof(buf)); + else { + if (fp->fp_flags & PF_OSFP_MSS_MOD) + strlcat(buf, "%", sizeof(buf)); + snprintf(tmp, sizeof(tmp), "%d", fp->fp_mss); + strlcat(buf, tmp, sizeof(buf)); + } + break; + case PF_OSFP_TCPOPT_WSCALE: + strlcat(buf, "W", sizeof(buf)); + if (fp->fp_flags & PF_OSFP_WSCALE_DC) + strlcat(buf, "*", sizeof(buf)); + else { + if (fp->fp_flags & PF_OSFP_WSCALE_MOD) + strlcat(buf, "%", sizeof(buf)); + snprintf(tmp, sizeof(tmp), "%d", fp->fp_wscale); + strlcat(buf, tmp, sizeof(buf)); + } + break; + } + + if (i != 0) + strlcat(buf, ",", sizeof(buf)); + } + strlcat(buf, ":", sizeof(buf)); + + strlcat(buf, fp->fp_os.fp_class_nm, sizeof(buf)); + strlcat(buf, ":", sizeof(buf)); + strlcat(buf, fp->fp_os.fp_version_nm, sizeof(buf)); + strlcat(buf, ":", sizeof(buf)); + strlcat(buf, fp->fp_os.fp_subtype_nm, sizeof(buf)); + strlcat(buf, ":", sizeof(buf)); + + snprintf(tmp, sizeof(tmp), "TcpOpts %d 0x%llx", fp->fp_optcnt, + (long long int)fp->fp_tcpopts); + strlcat(buf, tmp, sizeof(buf)); + + return (buf); +} diff --git a/usr.sbin/tcpdump/print-atalk.c b/usr.sbin/tcpdump/print-atalk.c index 2d99971ee71..4ee1e80f8e4 100644 --- a/usr.sbin/tcpdump/print-atalk.c +++ b/usr.sbin/tcpdump/print-atalk.c @@ -1,4 +1,4 @@ -/* $OpenBSD: print-atalk.c,v 1.20 2003/04/14 21:28:10 pvalchev Exp $ */ +/* $OpenBSD: print-atalk.c,v 1.21 2004/01/28 19:44:55 canacar Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997 @@ -25,7 +25,7 @@ #ifndef lint static const char rcsid[] = - "@(#) $Header: /cvs/OpenBSD/src/usr.sbin/tcpdump/print-atalk.c,v 1.20 2003/04/14 21:28:10 pvalchev Exp $ (LBL)"; + "@(#) $Header: /cvs/OpenBSD/src/usr.sbin/tcpdump/print-atalk.c,v 1.21 2004/01/28 19:44:55 canacar Exp $ (LBL)"; #endif #include <sys/param.h> @@ -56,6 +56,7 @@ struct rtentry; #include "extract.h" /* must come after interface.h */ #include "appletalk.h" #include "savestr.h" +#include "privsep.h" static struct tok type2str[] = { { ddpRTMP, "rtmp" }, @@ -553,6 +554,39 @@ struct hnamemem { static struct hnamemem hnametable[HASHNAMESIZE]; +/* + * see if there's an AppleTalk number to name map file. + */ +static void +init_atalk(void) +{ + struct hnamemem *tp; + char nambuf[MAXHOSTNAMELEN + 20]; + char line[BUFSIZ]; + int i1, i2, i3; + + priv_getlines(FTAB_APPLETALK); + while (priv_getline(line, sizeof(line)) > 0) { + if (line[0] == '\n' || line[0] == 0 || line[0] == '#') + continue; + if (sscanf(line, "%d.%d.%d %255s", &i1, &i2, &i3, nambuf) == 4) + /* got a hostname. */ + i3 |= ((i1 << 8) | i2) << 8; + else if (sscanf(line, "%d.%d %255s", &i1, &i2, nambuf) == 3) + /* got a net name */ + i3 = (((i1 << 8) | i2) << 8) | 255; + else + continue; + + for (tp = &hnametable[i3 & (HASHNAMESIZE-1)]; + tp->nxt; tp = tp->nxt) + ; + tp->addr = i3; + tp->nxt = newhnamemem(); + tp->name = savestr(nambuf); + } +} + static const char * ataddr_string(u_short atnet, u_char athost) { @@ -560,41 +594,11 @@ ataddr_string(u_short atnet, u_char athost) register int i = (atnet << 8) | athost; char nambuf[MAXHOSTNAMELEN + 20]; static int first = 1; - FILE *fp; - /* - * if this is the first call, see if there's an AppleTalk - * number to name map file. - */ - if (first && (first = 0, !nflag) - && (fp = fopen("/etc/atalk.names", "r"))) { - char line[256]; - int i1, i2, i3; - - while (fgets(line, sizeof(line), fp)) { - if (line[0] == '\n' || line[0] == 0 || line[0] == '#') - continue; - if (sscanf(line, "%d.%d.%d %255s", &i1, &i2, &i3, - nambuf) == 4) - /* got a hostname. */ - i3 |= ((i1 << 8) | i2) << 8; - else if (sscanf(line, "%d.%d %255s", &i1, &i2, - nambuf) == 3) - /* got a net name */ - i3 = (((i1 << 8) | i2) << 8) | 255; - else - continue; - - for (tp = &hnametable[i3 & (HASHNAMESIZE-1)]; - tp->nxt; tp = tp->nxt) - ; - tp->addr = i3; - tp->nxt = newhnamemem(); - tp->name = savestr(nambuf); - } - fclose(fp); + if (first) { + first = 0; + init_atalk(); } - for (tp = &hnametable[i & (HASHNAMESIZE-1)]; tp->nxt; tp = tp->nxt) if (tp->addr == i) return (tp->name); diff --git a/usr.sbin/tcpdump/print-cnfp.c b/usr.sbin/tcpdump/print-cnfp.c index 80c2ec97155..a981b93751f 100644 --- a/usr.sbin/tcpdump/print-cnfp.c +++ b/usr.sbin/tcpdump/print-cnfp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: print-cnfp.c,v 1.5 2003/06/02 20:37:14 mickey Exp $ */ +/* $OpenBSD: print-cnfp.c,v 1.6 2004/01/28 19:44:55 canacar Exp $ */ /* * Copyright (c) 1998 Michael Shalayeff @@ -40,6 +40,7 @@ #include <string.h> #include "interface.h" +#include "addrtoname.h" struct nfhdr { u_int32_t ver_cnt; /* version [15], and # of records */ @@ -72,8 +73,7 @@ cnfp_print(register const u_char *cp, u_int len, register const u_char *bp) register const struct nfhdr *nh; register const struct nfrec *nr; register const struct ip *ip; - struct protoent *pent; - int nrecs, ver; + int nrecs, ver, proto; time_t t; ip = (struct ip *)bp; @@ -131,14 +131,11 @@ cnfp_print(register const u_char *cp, u_int len, register const u_char *bp) printf(">> %s\n ", inet_ntoa(nr->nhop_ina)); - pent = getprotobynumber((ntohl(nr->proto_tos) >> 8) & 0xff); - if (!pent || nflag) - printf("%u ", (ntohl(nr->proto_tos) >> 8) & 0xff); - else - printf("%s ", pent->p_name); + proto = (ntohl(nr->proto_tos) >> 8) & 0xff; + printf("%s ", ipproto_string(proto)); /* tcp flags for tcp only */ - if (pent && pent->p_proto == IPPROTO_TCP) { + if (proto == IPPROTO_TCP) { int flags; if (ver == 1) flags = (ntohl(nr->asses) >> 24) & 0xff; diff --git a/usr.sbin/tcpdump/print-sunrpc.c b/usr.sbin/tcpdump/print-sunrpc.c index 451fba80d08..59c22aa986e 100644 --- a/usr.sbin/tcpdump/print-sunrpc.c +++ b/usr.sbin/tcpdump/print-sunrpc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: print-sunrpc.c,v 1.14 2004/01/20 09:00:41 otto Exp $ */ +/* $OpenBSD: print-sunrpc.c,v 1.15 2004/01/28 19:44:55 canacar Exp $ */ /* * Copyright (c) 1992, 1993, 1994, 1995, 1996 @@ -23,7 +23,7 @@ #ifndef lint static const char rcsid[] = - "@(#) $Header: /cvs/OpenBSD/src/usr.sbin/tcpdump/print-sunrpc.c,v 1.14 2004/01/20 09:00:41 otto Exp $ (LBL)"; + "@(#) $Header: /cvs/OpenBSD/src/usr.sbin/tcpdump/print-sunrpc.c,v 1.15 2004/01/28 19:44:55 canacar Exp $ (LBL)"; #endif #include <sys/param.h> @@ -53,6 +53,7 @@ struct rtentry; #include "interface.h" #include "addrtoname.h" +#include "privsep.h" static struct tok proc2str[] = { { PMAPPROC_NULL, "null" }, @@ -105,17 +106,16 @@ static char * progstr(prog) u_int32_t prog; { - register struct rpcent *rp; + char progname[32]; static char buf[32]; static int lastprog = 0; if (lastprog != 0 && prog == lastprog) return (buf); lastprog = prog; - rp = getrpcbynumber(prog); - if (rp == NULL) + if (priv_getrpcbynumber(prog, progname, sizeof(progname)) == 0) snprintf(buf, sizeof(buf), "#%u", prog); else - strlcpy(buf, rp->r_name, sizeof(buf)); + strlcpy(buf, progname, sizeof(buf)); return (buf); } diff --git a/usr.sbin/tcpdump/print-udp.c b/usr.sbin/tcpdump/print-udp.c index 91b184a5681..123df225556 100644 --- a/usr.sbin/tcpdump/print-udp.c +++ b/usr.sbin/tcpdump/print-udp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: print-udp.c,v 1.23 2004/01/18 15:33:30 otto Exp $ */ +/* $OpenBSD: print-udp.c,v 1.24 2004/01/28 19:44:55 canacar Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996 @@ -23,7 +23,7 @@ #ifndef lint static const char rcsid[] = - "@(#) $Header: /cvs/OpenBSD/src/usr.sbin/tcpdump/print-udp.c,v 1.23 2004/01/18 15:33:30 otto Exp $ (LBL)"; + "@(#) $Header: /cvs/OpenBSD/src/usr.sbin/tcpdump/print-udp.c,v 1.24 2004/01/28 19:44:55 canacar Exp $ (LBL)"; #endif #include <sys/param.h> diff --git a/usr.sbin/tcpdump/privsep.c b/usr.sbin/tcpdump/privsep.c new file mode 100644 index 00000000000..91d07f7cd14 --- /dev/null +++ b/usr.sbin/tcpdump/privsep.c @@ -0,0 +1,901 @@ +/* $OpenBSD: privsep.c,v 1.1 2004/01/28 19:44:55 canacar Exp $ */ + +/* + * Copyright (c) 2003 Can Erkin Acar + * Copyright (c) 2003 Anil Madhavapeddy <anil@recoil.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/wait.h> + +#include <net/bpf.h> +#include <net/if.h> +#include <net/pfvar.h> +#include <netinet/in.h> +#include <netinet/if_ether.h> + +#include <rpc/rpc.h> + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <netdb.h> +#include <paths.h> +#include <pwd.h> +#include <signal.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <tzfile.h> +#include <unistd.h> + +#include "interface.h" +#include "privsep.h" +#include "pfctl_parser.h" + +/* + * tcpdump goes through three states: STATE_INIT is where the + * descriptors and output files are opened. STATE_RUNNING is packet + * processing part. It is not allowed to go back in states. + */ +enum priv_state { + STATE_INIT, /* initial state */ + STATE_BPF, /* input file/device opened */ + STATE_FILTER, /* filter applied */ + STATE_RUN, /* running and accepting network traffic */ + STATE_QUIT /* shutting down */ +}; + +struct ftab { + char *name; + int max; + int count; +}; + +static struct ftab file_table[] = {{"/etc/appletalk.names", 1, 0}, + {PF_OSFP_FILE, 1, 0}}; + +#define NUM_FILETAB (sizeof(file_table) / sizeof(struct ftab)) + +int debug_level = LOG_INFO; +int priv_fd = -1; +static volatile pid_t child_pid = -1; +static volatile sig_atomic_t cur_state = STATE_INIT; + +static void parent_open_bpf(int, int *); +static void parent_open_dump(int, const char *); +static void parent_open_output(int, const char *); +static void parent_setfilter(int, char *, int *); +static void parent_done_init(int, int *); +static void parent_gethostbyaddr(int); +static void parent_ether_ntohost(int); +static void parent_getrpcbynumber(int); +static void parent_getserventries(int); +static void parent_getprotoentries(int); +static void parent_localtime(int fd); +static void parent_getlines(int); + +static void sig_pass_to_chld(int); +static void sig_got_chld(int); +static void test_state(int, int); +static void logmsg(int, const char *, ...); + +int +priv_init(int argc, char **argv) +{ + int bpfd = -1; + int i, socks[2], cmd; + struct passwd *pw; + uid_t uid; + gid_t gid; + char *cmdbuf, *infile = NULL; + char *RFileName = NULL; + char *WFileName = NULL; + + closefrom(STDERR_FILENO + 1); + + /* Create sockets */ + if (socketpair(AF_LOCAL, SOCK_STREAM, PF_UNSPEC, socks) == -1) + err(1, "socketpair() failed"); + + child_pid = fork(); + if (child_pid < 0) + err(1, "fork() failed"); + + if (!child_pid) { + if (getuid() == 0) { + pw = getpwnam("_tcpdump"); + if (pw == NULL) + errx(1, "unknown user _tcpdump"); + + /* chroot, drop privs and return */ + if (chroot(pw->pw_dir) != 0) + err(1, "unable to chroot"); + chdir("/"); + + /* drop to _tcpdump */ + if (setgroups(1, &pw->pw_gid) == -1) + err(1, "setgroups() failed"); + if (setegid(pw->pw_gid) == -1) + err(1, "setegid() failed"); + if (setgid(pw->pw_gid) == -1) + err(1, "setgid() failed"); + if (seteuid(pw->pw_uid) == -1) + err(1, "seteuid() failed"); + if (setuid(pw->pw_uid) == -1) + err(1, "setuid() failed"); + + } else { + /* Child - drop suid privileges */ + gid = getgid(); + uid = getuid(); + if (setegid(gid) == -1) + err(1, "setegid() failed"); + if (setgid(gid) == -1) + err(1, "setgid() failed"); + if (seteuid(uid) == -1) + err(1, "seteuid() failed"); + if (setuid(uid) == -1) + err(1, "setuid() failed"); + } + close(socks[0]); + priv_fd = socks[1]; + return (0); + } + + /* Father - drop suid privileges */ + gid = getgid(); + uid = getuid(); + + if (setegid(gid) == -1) + err(1, "setegid() failed"); + if (setgid(gid) == -1) + err(1, "setgid() failed"); + if (seteuid(uid) == -1) + err(1, "seteuid() failed"); + if (setuid(uid) == -1) + err(1, "setuid() failed"); + + /* parse the arguments for required options so that the child + * need not send them back */ + while ((i = getopt(argc, argv, + "ac:deE:fF:i:lnNOopqr:s:StT:vw:xXY")) != -1) { + switch (i) { + case 'r': + RFileName = optarg; + break; + + case 'w': + WFileName = optarg; + break; + + case 'F': + infile = optarg; + break; + + default: + /* nothing */ + break; + } + } + + if (infile) + cmdbuf = read_infile(infile); + else + cmdbuf = copy_argv(&argv[optind]); + + for (i = 1; i < _NSIG; i++) + signal(i, SIG_DFL); + + /* Pass ALRM/TERM/HUP through to child, and accept CHLD */ + signal(SIGALRM, sig_pass_to_chld); + signal(SIGTERM, sig_pass_to_chld); + signal(SIGHUP, sig_pass_to_chld); + signal(SIGCHLD, sig_got_chld); + + setproctitle("[priv]"); + close(socks[1]); + + while (cur_state < STATE_QUIT) { + if (may_read(socks[0], &cmd, sizeof(int))) + break; + switch (cmd) { + case PRIV_OPEN_BPF: + test_state(STATE_INIT, STATE_BPF); + parent_open_bpf(socks[0], &bpfd); + break; + case PRIV_OPEN_DUMP: + test_state(STATE_INIT, STATE_BPF); + parent_open_dump(socks[0], RFileName); + break; + case PRIV_OPEN_OUTPUT: + test_state(STATE_FILTER, STATE_RUN); + parent_open_output(socks[0], WFileName); + break; + case PRIV_SETFILTER: + test_state(STATE_INIT, STATE_FILTER); + parent_setfilter(socks[0], cmdbuf, &bpfd); + break; + case PRIV_DONE_INIT: + test_state(STATE_FILTER, STATE_RUN); + parent_done_init(socks[0], &bpfd); + break; + case PRIV_GETHOSTBYADDR: + test_state(STATE_RUN, STATE_RUN); + parent_gethostbyaddr(socks[0]); + break; + case PRIV_ETHER_NTOHOST: + test_state(STATE_BPF, cur_state); + parent_ether_ntohost(socks[0]); + break; + case PRIV_GETRPCBYNUMBER: + test_state(STATE_RUN, STATE_RUN); + parent_getrpcbynumber(socks[0]); + break; + case PRIV_GETSERVENTRIES: + test_state(STATE_FILTER, STATE_FILTER); + parent_getserventries(socks[0]); + break; + case PRIV_GETPROTOENTRIES: + test_state(STATE_FILTER, STATE_FILTER); + parent_getprotoentries(socks[0]); + break; + case PRIV_LOCALTIME: + test_state(STATE_RUN, STATE_RUN); + parent_localtime(socks[0]); + break; + case PRIV_GETLINES: + test_state(STATE_RUN, STATE_RUN); + parent_getlines(socks[0]); + break; + default: + logmsg(LOG_ERR, "[priv]: unknown command %d", cmd); + _exit(1); + /* NOTREACHED */ + } + } + + _exit(0); +} + +static void +parent_open_bpf(int fd, int *bpfd) +{ + int snaplen, promisc; + char device[IFNAMSIZ]; + size_t iflen; + + logmsg(LOG_DEBUG, "[priv]: msg PRIV_OPEN_BPF received"); + + must_read(fd, &snaplen, sizeof(int)); + must_read(fd, &promisc, sizeof(int)); + iflen = read_string(fd, device, sizeof(device), __func__); + if (iflen == 0) + errx(1, "Invalid interface size specified"); + *bpfd = pcap_live(device, snaplen, promisc); + if (*bpfd < 0) + logmsg(LOG_NOTICE, "[priv]: failed to open bpf"); + send_fd(fd, *bpfd); + /* do not close bpfd until filter is set */ +} + +static void +parent_open_dump(int fd, const char *RFileName) +{ + int file; + + logmsg(LOG_DEBUG, "[priv]: msg PRIV_OPEN_DUMP received"); + + if (RFileName == NULL) { + file = -1; + logmsg(LOG_ERR, "[priv]: No offline file specified"); + } else { + file = open(RFileName, O_RDONLY, 0); + if (file < 0) + logmsg(LOG_NOTICE, "[priv]: failed to open %s: %s", + RFileName, strerror(errno)); + } + send_fd(fd, file); + close(file); +} + +static void +parent_open_output(int fd, const char *WFileName) +{ + int file; + + logmsg(LOG_DEBUG, "[priv]: msg PRIV_OPEN_OUTPUT received"); + + file = open(WFileName, O_WRONLY|O_CREAT|O_TRUNC, 0666); + if (file < 0) + logmsg(LOG_NOTICE, "[priv]: failed to open %s: %s", + WFileName, strerror(errno)); + + send_fd(fd, file); + close(file); +} + +static void +parent_setfilter(int fd, char *cmdbuf, int *bpfd) +{ + logmsg(LOG_DEBUG, "[priv]: msg PRIV_SETFILTER received"); + + if (setfilter(*bpfd, fd, cmdbuf)) + logmsg(LOG_DEBUG, "[priv]: setfilter() failed"); + close(*bpfd); /* done with bpf descriptor */ + *bpfd = -1; +} + +static void +parent_done_init(int fd, int *bpfd) +{ + int ret; + + logmsg(LOG_DEBUG, "[priv]: msg PRIV_DONE_INIT received"); + + close(*bpfd); /* done with bpf descriptor */ + *bpfd = -1; + ret = 0; + must_write(fd, &ret, sizeof(ret)); +} + +static void +parent_gethostbyaddr(int fd) +{ + char hostname[MAXHOSTNAMELEN]; + size_t hostname_len; + int addr_af; + struct hostent *hp; + + logmsg(LOG_DEBUG, "[priv]: msg PRIV_GETHOSTBYADDR received"); + + /* Expecting: address block, address family */ + hostname_len = read_block(fd, hostname, sizeof(hostname), __func__); + if (hostname_len == 0) + _exit(1); + must_read(fd, &addr_af, sizeof(int)); + hp = gethostbyaddr(hostname, hostname_len, addr_af); + if (hp == NULL) + write_zero(fd); + else + write_string(fd, hp->h_name); +} + +static void +parent_ether_ntohost(int fd) +{ + struct ether_addr ether; + char hostname[MAXHOSTNAMELEN]; + + logmsg(LOG_DEBUG, "[priv]: msg PRIV_ETHER_NTOHOST received"); + + /* Expecting: ethernet address */ + must_read(fd, ðer, sizeof(ether)); + if (ether_ntohost(hostname, ðer) == -1) + write_zero(fd); + else + write_string(fd, hostname); +} + +static void +parent_getrpcbynumber(int fd) +{ + int rpc; + struct rpcent *rpce; + + logmsg(LOG_DEBUG, "[priv]: msg PRIV_GETRPCBYNUMBER received"); + + must_read(fd, &rpc, sizeof(int)); + rpce = getrpcbynumber(rpc); + if (rpce == NULL) + write_zero(fd); + else + write_string(fd, rpce->r_name); +} + +static void +parent_getserventries(int fd) +{ + struct servent *sp; + + logmsg(LOG_DEBUG, "[priv]: msg PRIV_GETSERVENTRIES received"); + + for (;;) { + sp = getservent(); + if (sp == NULL) { + write_zero(fd); + break; + } else { + write_string(fd, sp->s_name); + must_write(fd, &sp->s_port, sizeof(int)); + write_string(fd, sp->s_proto); + } + } + endservent(); +} + +static void +parent_getprotoentries(int fd) +{ + struct protoent *pe; + + logmsg(LOG_DEBUG, "[priv]: msg PRIV_GETPROTOENTRIES received"); + + for (;;) { + pe = getprotoent(); + if (pe == NULL) { + write_zero(fd); + break; + } else { + write_string(fd, pe->p_name); + must_write(fd, &pe->p_proto, sizeof(int)); + } + } + endprotoent(); +} + +/* read the time and send the corresponding localtime and gmtime + * results back to the child */ +static void +parent_localtime(int fd) +{ + struct tm *lt, *gt; + time_t t; + + logmsg(LOG_DEBUG, "[priv]: msg PRIV_LOCALTIME received"); + + must_read(fd, &t, sizeof(time_t)); + + /* this must be done seperately, since they apparently use the + * same local buffer */ + if ((lt = localtime(&t)) == NULL) + errx(1, "localtime()"); + must_write(fd, lt, sizeof(*lt)); + + if ((gt = gmtime(&t)) == NULL) + errx(1, "gmtime()"); + must_write(fd, gt, sizeof(*gt)); + + if (lt->tm_zone == NULL) + write_zero(fd); + else + write_string(fd, lt->tm_zone); +} + +static void +parent_getlines(int fd) +{ + FILE *fp; + char *buf, *lbuf, *file; + size_t len, fid; + + logmsg(LOG_DEBUG, "[priv]: msg PRIV_GETLINES received"); + + must_read(fd, &fid, sizeof(size_t)); + if (fid >= NUM_FILETAB) + errx(1, "invalid file id"); + + file = file_table[fid].name; + + if (file == NULL) + errx(1, "invalid file referenced"); + + if (file_table[fid].count >= file_table[fid].max) + errx(1, "maximum open count exceeded for %s", file); + + file_table[fid].count++; + + if ((fp = fopen(file, "r")) == NULL) { + write_zero(fd); + return; + } + + lbuf = NULL; + while ((buf = fgetln(fp, &len))) { + if (buf[len - 1] == '\n') + buf[len - 1] = '\0'; + else { + if ((lbuf = (char *)malloc(len + 1)) == NULL) + err(1, NULL); + memcpy(lbuf, buf, len); + lbuf[len] = '\0'; + buf = lbuf; + } + + write_string(fd, buf); + + if (lbuf != NULL) { + free(lbuf); + lbuf = NULL; + } + } + write_zero(fd); + fclose(fp); +} + +void +priv_init_done(void) +{ + int ret; + + if (priv_fd < 0) + errx(1, "%s: called from privileged portion\n", __func__); + + write_command(priv_fd, PRIV_DONE_INIT); + must_read(priv_fd, &ret, sizeof(int)); +} + +/* Reverse address resolution; response is placed into res, and length of + * response is returned (zero on error) */ +size_t +priv_gethostbyaddr(char *addr, size_t addr_len, int af, char *res, size_t res_len) +{ + if (priv_fd < 0) + errx(1, "%s called from privileged portion", __func__); + + write_command(priv_fd, PRIV_GETHOSTBYADDR); + write_block(priv_fd, addr_len, addr); + must_write(priv_fd, &af, sizeof(int)); + + return (read_string(priv_fd, res, res_len, __func__)); +} + +size_t +priv_ether_ntohost(char *name, size_t name_len, struct ether_addr *e) +{ + if (priv_fd < 0) + errx(1, "%s called from privileged portion", __func__); + + write_command(priv_fd, PRIV_ETHER_NTOHOST); + must_write(priv_fd, e, sizeof(*e)); + + /* Read the host name */ + return (read_string(priv_fd, name, name_len, __func__)); +} + +size_t +priv_getrpcbynumber(int rpc, char *progname, size_t progname_len) +{ + if (priv_fd < 0) + errx(1, "%s called from privileged portion", __func__); + + write_command(priv_fd, PRIV_GETRPCBYNUMBER); + must_write(priv_fd, &rpc, sizeof(int)); + + return read_string(priv_fd, progname, progname_len, __func__); +} + +/* start getting service entries */ +void +priv_getserventries(void) +{ + if (priv_fd < 0) + errx(1, "%s called from privileged portion", __func__); + + write_command(priv_fd, PRIV_GETSERVENTRIES); +} + +/* retrieve a service entry, should be called repeatedly after calling + priv_getserventries(), until it returns zero. */ +size_t +priv_getserventry(char *name, size_t name_len, int *port, char *prot, + size_t prot_len) +{ + if (priv_fd < 0) + errx(1, "%s called from privileged portion", __func__); + + /* read the service name */ + if (read_string(priv_fd, name, name_len, __func__) == 0) + return 0; + + /* read the port */ + must_read(priv_fd, port, sizeof(int)); + + /* read the protocol */ + return (read_string(priv_fd, prot, prot_len, __func__)); +} + +/* start getting ip protocol entries */ +void +priv_getprotoentries(void) +{ + if (priv_fd < 0) + errx(1, "%s called from privileged portion", __func__); + + write_command(priv_fd, PRIV_GETPROTOENTRIES); +} + +/* retrieve a ip protocol entry, should be called repeatedly after calling + priv_getprotoentries(), until it returns zero. */ +size_t +priv_getprotoentry(char *name, size_t name_len, int *num) +{ + if (priv_fd < 0) + errx(1, "%s called from privileged portion", __func__); + + /* read the proto name */ + if (read_string(priv_fd, name, name_len, __func__) == 0) + return 0; + + /* read the num */ + must_read(priv_fd, num, sizeof(int)); + + return (1); +} + +/* localtime() replacement: ask parent for localtime and gmtime, cache + * the localtime for about one hour i.e. until one of the fields other + * than seconds and minutes change. The check is done using gmtime + * values since they are the same in parent and child. + * XXX assumes timezone granularity is 1 hour. */ +struct tm * +priv_localtime(const time_t *t) +{ + static struct tm lt, gt0; + static struct tm *gt = NULL; + static char zone[TZ_MAX_CHARS]; + + if (gt != NULL) { + gt = gmtime(t); + gt0.tm_sec = gt->tm_sec; + gt0.tm_min = gt->tm_min; + gt0.tm_zone = gt->tm_zone; + + if (memcmp(gt, >0, sizeof(struct tm)) == 0) { + lt.tm_sec = gt0.tm_sec; + lt.tm_min = gt0.tm_min; + return < + } + } + + write_command(priv_fd, PRIV_LOCALTIME); + must_write(priv_fd, t, sizeof(time_t)); + must_read(priv_fd, <, sizeof(lt)); + must_read(priv_fd, >0, sizeof(gt0)); + + if (read_string(priv_fd, zone, sizeof(zone), __func__)) + lt.tm_zone = zone; + else + lt.tm_zone = NULL; + + gt0.tm_zone = NULL; + gt = >0; + + return < +} + +/* start getting lines from a file */ +void +priv_getlines(size_t sz) +{ + if (priv_fd < 0) + errx(1, "%s called from privileged portion", __func__); + + write_command(priv_fd, PRIV_GETLINES); + must_write(priv_fd, &sz, sizeof(size_t)); +} + +/* retrieve a line from a file, should be called repeatedly after calling + priv_getlines(), until it returns zero. */ +size_t +priv_getline(char *line, size_t line_len) +{ + if (priv_fd < 0) + errx(1, "%s called from privileged portion", __func__); + + /* read the line */ + return (read_string(priv_fd, line, line_len, __func__)); +} + +/* If priv parent gets a TERM or HUP, pass it through to child instead */ +static void +sig_pass_to_chld(int sig) +{ + int save_err; + + save_err = errno; + if (child_pid != -1) + kill(child_pid, sig); + errno = save_err; +} + +/* When child dies, move into the shutdown state */ +static void +sig_got_chld(int sig) +{ + int save_err; + pid_t pid; + int status; + + save_err = errno; + pid = waitpid(child_pid, &status, WCONTINUED); + if ((pid == -1 || !WIFCONTINUED(status)) && cur_state < STATE_QUIT) + cur_state = STATE_QUIT; + errno = save_err; +} + +/* Read all data or return 1 for error. */ +int +may_read(int fd, void *buf, size_t n) +{ + char *s = buf; + ssize_t res, pos = 0; + + while (n > pos) { + res = read(fd, s + pos, n - pos); + switch (res) { + case -1: + if (errno == EINTR || errno == EAGAIN) + continue; + case 0: + return (1); + default: + pos += res; + } + } + return (0); +} + +/* Read data with the assertion that it all must come through, or + * else abort the process. Based on atomicio() from openssh. */ +void +must_read(int fd, void *buf, size_t n) +{ + char *s = buf; + ssize_t res, pos = 0; + + while (n > pos) { + res = read(fd, s + pos, n - pos); + switch (res) { + case -1: + if (errno == EINTR || errno == EAGAIN) + continue; + case 0: + _exit(0); + default: + pos += res; + } + } +} + +/* Write data with the assertion that it all has to be written, or + * else abort the process. Based on atomicio() from openssh. */ +void +must_write(int fd, const void *buf, size_t n) +{ + const char *s = buf; + ssize_t res, pos = 0; + + while (n > pos) { + res = write(fd, s + pos, n - pos); + switch (res) { + case -1: + if (errno == EINTR || errno == EAGAIN) + continue; + case 0: + _exit(0); + default: + pos += res; + } + } +} + +/* test for a given state, and possibly increase state */ +static void +test_state(int expect, int next) +{ + if (cur_state < expect) { + logmsg(LOG_ERR, "[priv] Invalid state: %d < %d", + cur_state, expect); + _exit(1); + } + + if (next < cur_state) { + logmsg(LOG_ERR, "[priv] Invalid next state: %d < %d", + next, cur_state); + _exit(1); + } + + cur_state = next; +} + +static void +logmsg(int pri, const char *message, ...) +{ + va_list ap; + if (pri > debug_level) + return; + va_start(ap, message); + + vfprintf(stderr, message, ap); + fprintf(stderr, "\n"); + va_end(ap); +} + +/* write a command to the peer */ +void +write_command(int fd, int cmd) +{ + must_write(fd, &cmd, sizeof(cmd)); +} + +/* write a zero 'length' to signal an error to read_{string|block} */ +void +write_zero(int fd) +{ + size_t len = 0; + must_write(fd, &len, sizeof(size_t)); +} + +/* send a string */ +void +write_string(int fd, const char *str) +{ + size_t len; + + len = strlen(str) + 1; + must_write(fd, &len, sizeof(size_t)); + must_write(fd, str, len); +} + +/* send a block of data of given size */ +void +write_block(int fd, size_t size, const char *str) +{ + must_write(fd, &size, sizeof(size_t)); + must_write(fd, str, size); +} + +/* read a string from the channel, return 0 if error, or total size of + * the buffer, including the terminating '\0' */ +size_t +read_string(int fd, char *buf, size_t size, const char *func) +{ + size_t len; + + len = read_block(fd, buf, size, func); + if (len == 0) + return (0); + + if (buf[len - 1] != '\0') + errx(1, "%s: received invalid string", func); + + return (len); +} + +/* read a block of data from the channel, return length of data, or 0 + * if error */ +size_t +read_block(int fd, char *buf, size_t size, const char *func) +{ + size_t len; + /* Expect back an integer size, and then a string of that length */ + must_read(fd, &len, sizeof(size_t)); + + /* Check there was no error (indicated by a return of 0) */ + if (len == 0) + return (0); + + /* Make sure we aren't overflowing the passed in buffer */ + if (size < len) + errx(1, "%s: overflow attempt in return", func); + + /* Read the string and make sure we got all of it */ + must_read(fd, buf, len); + return (len); +} diff --git a/usr.sbin/tcpdump/privsep.h b/usr.sbin/tcpdump/privsep.h new file mode 100644 index 00000000000..07b4fb5b1f5 --- /dev/null +++ b/usr.sbin/tcpdump/privsep.h @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2003 Can Erkin Acar + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _PRIVSEP_H_ +#define _PRIVSEP_H_ + +#include <pcap-int.h> + +#define TCPDUMP_MAGIC 0xa1b2c3d4 + +/* file ids used by priv_getlines */ +#define FTAB_APPLETALK 0 +#define FTAB_PFOSFP 1 + +enum cmd_types { + PRIV_OPEN_BPF, /* open a bpf descriptor */ + PRIV_OPEN_DUMP, /* open dump file for reading */ + PRIV_OPEN_OUTPUT, /* open output file */ + PRIV_SETFILTER, /* set a bpf read filter */ + PRIV_GETHOSTBYADDR, /* resolve numeric address into hostname */ + PRIV_ETHER_NTOHOST, /* translate ethernet address into host name */ + PRIV_GETRPCBYNUMBER, /* translate rpc number into name */ + PRIV_GETSERVENTRIES, /* get the service entries table */ + PRIV_GETPROTOENTRIES, /* get the ip protocol entries table */ + PRIV_LOCALTIME, /* return localtime */ + PRIV_GETLINES, /* get lines from a file */ + PRIV_DONE_INIT /* signal that the initialization is done */ +}; + +struct ether_addr; + +/* Privilege separation */ +int priv_init(int, char **); +void priv_init_done(void); + +int setfilter(int, int, char *); +int pcap_live(const char *, int, int); + +struct bpf_program *priv_pcap_setfilter(pcap_t *, int, u_int32_t); +pcap_t *priv_pcap_live(const char *, int, int, int, char *); +pcap_t *priv_pcap_offline(const char *, char *); + +size_t priv_gethostbyaddr(char *, size_t, int, char *, size_t); +size_t priv_ether_ntohost(char *, size_t, struct ether_addr *); +size_t priv_getrpcbynumber(int, char *, size_t); + +struct tm *priv_localtime(const time_t *); + +/* Start getting service entries */ +void priv_getserventries(void); + +/* Retrieve a single service entry, should be called repeatedly after + calling priv_getserventries() until it returns zero */ +size_t priv_getserventry(char *name, size_t name_len, int *port, char *prot, + size_t prot_len); + +/* Start getting ip protocol entries */ +void priv_getprotoentries(void); + +/* Retrieve a single protocol entry, should be called repeatedly after + calling priv_getprotoentries() until it returns zero */ +size_t priv_getprotoentry(char *name, size_t name_len, int *num); + +/* Start getting lines from a file */ +void priv_getlines(size_t); + +/* Retrieve a single line from a file, should be called repeatedly after + calling priv_getlines() until it returns zero */ +size_t priv_getline(char *, size_t); + +pcap_dumper_t *priv_pcap_dump_open(pcap_t *, char *); + +/* File descriptor send/recv */ +void send_fd(int, int); +int receive_fd(int); + +/* communications over the channel */ +int may_read(int, void *, size_t); +void must_read(int, void *, size_t); +void must_write(int, const void *, size_t); +size_t read_block(int, char *, size_t, const char *); +size_t read_string(int, char *, size_t, const char *); +void write_block(int, size_t, const char *); +void write_command(int fd, int cmd); +void write_string(int, const char *); +void write_zero(int); + +extern int priv_fd; + +#endif diff --git a/usr.sbin/tcpdump/privsep_fdpass.c b/usr.sbin/tcpdump/privsep_fdpass.c new file mode 100644 index 00000000000..5396b70e8da --- /dev/null +++ b/usr.sbin/tcpdump/privsep_fdpass.c @@ -0,0 +1,116 @@ +/* $OpenBSD: privsep_fdpass.c,v 1.1 2004/01/28 19:44:55 canacar Exp $ */ + +/* + * Copyright 2001 Niels Provos <provos@citi.umich.edu> + * All rights reserved. + * + * Copyright (c) 2002 Matthieu Herrb + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/uio.h> + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> + +#include "privsep.h" + +void +send_fd(int sock, int fd) +{ + struct msghdr msg; + char tmp[CMSG_SPACE(sizeof(int))]; + struct cmsghdr *cmsg; + struct iovec vec; + int result = 0; + ssize_t n; + + memset(&msg, 0, sizeof(msg)); + + if (fd >= 0) { + msg.msg_control = (caddr_t)tmp; + msg.msg_controllen = CMSG_LEN(sizeof(int)); + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len = CMSG_LEN(sizeof(int)); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + *(int *)CMSG_DATA(cmsg) = fd; + } else + result = errno; + + vec.iov_base = &result; + vec.iov_len = sizeof(int); + msg.msg_iov = &vec; + msg.msg_iovlen = 1; + + if ((n = sendmsg(sock, &msg, 0)) == -1) + warn("%s: sendmsg(%d)", __func__, sock); + if (n != sizeof(int)) + warnx("%s: sendmsg: expected sent 1 got %ld", + __func__, (long)n); +} + +int +receive_fd(int sock) +{ + struct msghdr msg; + char tmp[CMSG_SPACE(sizeof(int))]; + struct cmsghdr *cmsg; + struct iovec vec; + ssize_t n; + int result; + int fd; + + memset(&msg, 0, sizeof(msg)); + vec.iov_base = &result; + vec.iov_len = sizeof(int); + msg.msg_iov = &vec; + msg.msg_iovlen = 1; + msg.msg_control = tmp; + msg.msg_controllen = sizeof(tmp); + + if ((n = recvmsg(sock, &msg, 0)) == -1) + warn("%s: recvmsg", __func__); + if (n != sizeof(int)) + warnx("%s: recvmsg: expected received 1 got %ld", + __func__, (long)n); + if (result == 0) { + cmsg = CMSG_FIRSTHDR(&msg); + if (cmsg->cmsg_type != SCM_RIGHTS) + warnx("%s: expected type %d got %d", __func__, + SCM_RIGHTS, cmsg->cmsg_type); + fd = (*(int *)CMSG_DATA(cmsg)); + return (fd); + } else { + errno = result; + return (-1); + } +} diff --git a/usr.sbin/tcpdump/privsep_pcap.c b/usr.sbin/tcpdump/privsep_pcap.c new file mode 100644 index 00000000000..b051e781f7f --- /dev/null +++ b/usr.sbin/tcpdump/privsep_pcap.c @@ -0,0 +1,496 @@ +/* $OpenBSD: privsep_pcap.c,v 1.1 2004/01/28 19:44:55 canacar Exp $ */ + +/* + * Copyright (c) 2004 Can Erkin Acar + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <net/if.h> + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <pcap-int.h> +#include <pcap.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "privsep.h" + +/* + * privileged part of priv_pcap_setfilter, compile the filter + * expression, and return it to the child. Note that we fake an hpcap + * and use it to capture the error messages, and pass the error back + * to client. + */ +int +setfilter(int bpfd, int sock, char *filter) +{ + struct bpf_program fcode; + int oflag, snap, link; + u_int32_t netmask; + pcap_t hpcap; + + must_read(sock, &oflag, sizeof(oflag)); + must_read(sock, &netmask, sizeof(netmask)); + must_read(sock, &snap, sizeof(snap)); + must_read(sock, &link, sizeof(link)); + + if (snap < 0) { + snprintf(hpcap.errbuf, PCAP_ERRBUF_SIZE, "invalid snaplen"); + goto err; + } + + /* fake hpcap, it only needs errbuf, snaplen, and linktype to + * compile a filter expression */ + /* XXX messing with pcap internals */ + hpcap.snapshot = snap; + hpcap.linktype = link; + if (pcap_compile(&hpcap, &fcode, filter, oflag, netmask)) + goto err; + + /* write the filter */ + must_write(sock, &fcode.bf_len, sizeof(fcode.bf_len)); + if (fcode.bf_len > 0) + must_write(sock, fcode.bf_insns, + fcode.bf_len * sizeof(struct bpf_insn)); + else { + write_string(sock, "Invalid filter size"); + return (1); + } + + /* if bpf descriptor is open, set the filter XXX check oflag? */ + if (bpfd >= 0 && ioctl(bpfd, BIOCSETF, &fcode)) + return 1; + + pcap_freecode(&fcode); + + return (0); + + err: + fcode.bf_len = 0; + must_write(sock, &fcode.bf_len, sizeof(fcode.bf_len)); + + /* write back the error string */ + write_string(sock, hpcap.errbuf); + + return (1); +} + +/* + * filter is compiled and set in the parent, get the compiled output, + * and set it locally, for filtering dumps etc. + */ +struct bpf_program * +priv_pcap_setfilter(pcap_t *hpcap, int oflag, u_int32_t netmask) +{ + int snap, link; + struct bpf_program *fcode = NULL; + char *ebuf = pcap_geterr(hpcap); + + if (priv_fd < 0) + errx(1, "%s: called from privileged portion", __func__); + + snap = pcap_snapshot(hpcap); + link = pcap_datalink(hpcap); + + fcode = calloc(1, sizeof(*fcode)); + if (fcode == NULL) { + snprintf(ebuf, PCAP_ERRBUF_SIZE, "out of memory"); + return (NULL); + } + + write_command(priv_fd, PRIV_SETFILTER); + + /* send oflag, netmask, snaplen and linktype */ + must_write(priv_fd, &oflag, sizeof(oflag)); + must_write(priv_fd, &netmask, sizeof(netmask)); + must_write(priv_fd, &snap, sizeof(snap)); + must_write(priv_fd, &link, sizeof(link)); + + /* receive compiled filter */ + must_read(priv_fd, &fcode->bf_len, sizeof(fcode->bf_len)); + if (fcode->bf_len <= 0) { + int len; + len = read_string(priv_fd, ebuf, PCAP_ERRBUF_SIZE, __func__); + if (len == 0) + snprintf(ebuf, PCAP_ERRBUF_SIZE, "pcap compile error"); + goto err; + } + + fcode->bf_insns = calloc(fcode->bf_len, sizeof(struct bpf_insn)); + if (fcode->bf_insns == NULL) { + snprintf(ebuf, PCAP_ERRBUF_SIZE, "out of memory"); + goto err; + } + + must_read(priv_fd, fcode->bf_insns, + fcode->bf_len * sizeof(struct bpf_insn)); + + pcap_setfilter(hpcap, fcode); + + return (fcode); + + err: + if (fcode) { + if (fcode->bf_insns) + free(fcode->bf_insns); + free(fcode); + } + return (NULL); +} + + +/* privileged part of priv_pcap_live */ +int +pcap_live(const char *device, int snaplen, int promisc) +{ + char bpf[sizeof "/dev/bpf0000000000"]; + struct ifreq ifr; + unsigned v; + int fd = -1; + int n = 0; + + if (device == NULL || snaplen <= 0) + goto error; + + do { + snprintf(bpf, sizeof(bpf), "/dev/bpf%d", n++); + fd = open(bpf, O_RDONLY); + } while (fd < 0 && errno == EBUSY); + + if (fd < 0) + goto error; + + v = 32768; /* XXX this should be a user-accessible hook */ + ioctl(fd, BIOCSBLEN, &v); + + strlcpy(ifr.ifr_name, device, sizeof(ifr.ifr_name)); + if (ioctl(fd, BIOCSETIF, &ifr) < 0) + goto error; + + if (promisc) + /* this is allowed to fail */ + ioctl(fd, BIOCPROMISC, NULL); + + /* lock the descriptor */ + if (ioctl(fd, BIOCLOCK, NULL) < 0) + goto error; + + return (fd); + + error: + if (fd >= 0) + close(fd); + + return (-1); +} + + +/* + * XXX reimplement pcap_open_live with privsep, this is the + * unprivileged part. + */ +pcap_t * +priv_pcap_live(const char *dev, int slen, int prom, int to_ms, char *ebuf) +{ + int fd; + struct bpf_version bv; + u_int v; + pcap_t *p; + + if (priv_fd < 0) + errx(1, "%s: called from privileged portion", __func__); + + if (dev == NULL) + snprintf(ebuf, PCAP_ERRBUF_SIZE, "No interface specified"); + + p = (pcap_t *)malloc(sizeof(*p)); + if (p == NULL) { + snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s", + pcap_strerror(errno)); + return (NULL); + } + + bzero(p, sizeof(*p)); + + write_command(priv_fd, PRIV_OPEN_BPF); + must_write(priv_fd, &slen, sizeof(int)); + must_write(priv_fd, &prom, sizeof(int)); + write_string(priv_fd, dev); + + fd = receive_fd(priv_fd); + if (fd < 0) { + snprintf(ebuf, PCAP_ERRBUF_SIZE, + "Failed open bpf descriptor in privileged process."); + goto bad; + } + + /* fd is locked, can only use 'safe' ioctls */ + + if (ioctl(fd, BIOCVERSION, &bv) < 0) { + snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCVERSION: %s", + pcap_strerror(errno)); + goto bad; + } + + if (bv.bv_major != BPF_MAJOR_VERSION || + bv.bv_minor < BPF_MINOR_VERSION) { + snprintf(ebuf, PCAP_ERRBUF_SIZE, + "kernel bpf filter out of date"); + goto bad; + } + + p->fd = fd; + p->snapshot = slen; + + /* Get the data link layer type. */ + if (ioctl(fd, BIOCGDLT, &v) < 0) { + snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCGDLT: %s", + pcap_strerror(errno)); + goto bad; + } +#if _BSDI_VERSION - 0 >= 199510 + /* The SLIP and PPP link layer header changed in BSD/OS 2.1 */ + switch (v) { + + case DLT_SLIP: + v = DLT_SLIP_BSDOS; + break; + + case DLT_PPP: + v = DLT_PPP_BSDOS; + break; + } +#endif + p->linktype = v; + + /* set timeout */ + if (to_ms != 0) { + struct timeval to; + to.tv_sec = to_ms / 1000; + to.tv_usec = (to_ms * 1000) % 1000000; + if (ioctl(p->fd, BIOCSRTIMEOUT, &to) < 0) { + snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCSRTIMEOUT: %s", + pcap_strerror(errno)); + goto bad; + } + } + + if (ioctl(fd, BIOCGBLEN, &v) < 0) { + snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCGBLEN: %s", + pcap_strerror(errno)); + goto bad; + } + p->bufsize = v; + p->buffer = (u_char *)malloc(p->bufsize); + if (p->buffer == NULL) { + snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s", + pcap_strerror(errno)); + goto bad; + } + + return (p); + bad: + close(fd); + free(p); + return (NULL); +} + + + +/* + * reimplement pcap_open_offline with privsep, this is the + * unprivileged part. + * XXX merge with above? + */ +static void +swap_hdr(struct pcap_file_header *hp) +{ + hp->version_major = swap16(hp->version_major); + hp->version_minor = swap16(hp->version_minor); + hp->thiszone = swap32(hp->thiszone); + hp->sigfigs = swap32(hp->sigfigs); + hp->snaplen = swap32(hp->snaplen); + hp->linktype = swap32(hp->linktype); +} + +pcap_t * +priv_pcap_offline(const char *fname, char *errbuf) +{ + register pcap_t *p; + register FILE *fp; + struct pcap_file_header hdr; + int linklen; + + if (priv_fd < 0) + errx(1, "%s: called from privileged portion", __func__); + + p = (pcap_t *)malloc(sizeof(*p)); + if (p == NULL) { + strlcpy(errbuf, "out of swap", PCAP_ERRBUF_SIZE); + return (NULL); + } + + memset((char *)p, 0, sizeof(*p)); + + if (fname[0] == '-' && fname[1] == '\0') { + p->fd = -1; + fp = stdin; + } else { + write_command(priv_fd, PRIV_OPEN_DUMP); + p->fd = receive_fd(priv_fd); + if (p->fd < 0) { + snprintf(errbuf, PCAP_ERRBUF_SIZE, + "Failed open input file in privileged process."); + goto bad; + } + + fp = fdopen(p->fd, "r"); + if (fp == NULL) { + snprintf(errbuf, PCAP_ERRBUF_SIZE, "%s: %s", fname, + pcap_strerror(errno)); + goto bad; + } + } + if (fread((char *)&hdr, sizeof(hdr), 1, fp) != 1) { + snprintf(errbuf, PCAP_ERRBUF_SIZE, "fread: %s", + pcap_strerror(errno)); + goto bad; + } + + if (hdr.magic != TCPDUMP_MAGIC) { + if (swap32(hdr.magic) != TCPDUMP_MAGIC) { + snprintf(errbuf, PCAP_ERRBUF_SIZE, + "bad dump file format"); + goto bad; + } + p->sf.swapped = 1; + swap_hdr(&hdr); + } + if (hdr.version_major < PCAP_VERSION_MAJOR) { + snprintf(errbuf, PCAP_ERRBUF_SIZE, "archaic file format"); + goto bad; + } + + p->tzoff = hdr.thiszone; + p->snapshot = hdr.snaplen; + p->linktype = hdr.linktype; + p->sf.rfile = fp; + p->bufsize = hdr.snaplen; + + /* Align link header as required for proper data alignment */ + /* XXX should handle all types */ + switch (p->linktype) { + + case DLT_EN10MB: + linklen = 14; + break; + + case DLT_FDDI: + linklen = 13 + 8; /* fddi_header + llc */ + break; + + case DLT_NULL: + default: + linklen = 0; + break; + } + + if (p->bufsize < 0) + p->bufsize = BPF_MAXBUFSIZE; + p->sf.base = (u_char *)malloc(p->bufsize + BPF_ALIGNMENT); + if (p->sf.base == NULL) { + strlcpy(errbuf, "out of swap", PCAP_ERRBUF_SIZE); + goto bad; + } + p->buffer = p->sf.base + BPF_ALIGNMENT - (linklen % BPF_ALIGNMENT); + p->sf.version_major = hdr.version_major; + p->sf.version_minor = hdr.version_minor; +#ifdef PCAP_FDDIPAD + /* XXX what to do with this? */ + /* XXX padding only needed for kernel fcode */ + pcap_fddipad = 0; +#endif + + return (p); + bad: + free(p); + return (NULL); +} + + +static int +sf_write_header(FILE *fp, int linktype, int thiszone, int snaplen) +{ + struct pcap_file_header hdr; + + hdr.magic = TCPDUMP_MAGIC; + hdr.version_major = PCAP_VERSION_MAJOR; + hdr.version_minor = PCAP_VERSION_MINOR; + + hdr.thiszone = thiszone; + hdr.snaplen = snaplen; + hdr.sigfigs = 0; + hdr.linktype = linktype; + + if (fwrite((char *)&hdr, sizeof(hdr), 1, fp) != 1) + return (-1); + + return (0); +} + +pcap_dumper_t * +priv_pcap_dump_open(pcap_t *p, char *fname) +{ + int fd; + FILE *f; + + if (priv_fd < 0) + errx(1, "%s: called from privileged portion\n", __func__); + + if (fname[0] == '-' && fname[1] == '\0') + f = stdout; + else { + write_command(priv_fd, PRIV_OPEN_OUTPUT); + fd = receive_fd(priv_fd); + f = fdopen(fd, "w"); + if (f == NULL) { + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "%s: %s", + fname, pcap_strerror(errno)); + return (NULL); + } + } + + (void)sf_write_header(f, p->linktype, p->tzoff, p->snapshot); + return ((pcap_dumper_t *)f); +} diff --git a/usr.sbin/tcpdump/tcpdump.c b/usr.sbin/tcpdump/tcpdump.c index 16df5b6c8d6..ac3fb064849 100644 --- a/usr.sbin/tcpdump/tcpdump.c +++ b/usr.sbin/tcpdump/tcpdump.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tcpdump.c,v 1.34 2003/09/25 13:32:58 jmc Exp $ */ +/* $OpenBSD: tcpdump.c,v 1.35 2004/01/28 19:44:55 canacar Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997 @@ -26,7 +26,7 @@ static const char copyright[] = "@(#) Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997\n\ The Regents of the University of California. All rights reserved.\n"; static const char rcsid[] = - "@(#) $Header: /cvs/OpenBSD/src/usr.sbin/tcpdump/tcpdump.c,v 1.34 2003/09/25 13:32:58 jmc Exp $ (LBL)"; + "@(#) $Header: /cvs/OpenBSD/src/usr.sbin/tcpdump/tcpdump.c,v 1.35 2004/01/28 19:44:55 canacar Exp $ (LBL)"; #endif /* @@ -62,6 +62,7 @@ static const char rcsid[] = #include <net/pfvar.h> #include "pfctl.h" #include "pfctl_parser.h" +#include "privsep.h" int aflag; /* translate network and broadcast addresses */ int dflag; /* print filter code */ @@ -81,7 +82,6 @@ int Xflag; /* print packet in emacs-hexl style */ int packettype; - char *program_name; int32_t thiszone; /* seconds offset from gmt to local time */ @@ -138,6 +138,16 @@ lookup_printer(int type) /* NOTREACHED */ } +static int +init_pfosfp(void) +{ + pf_osfp_initialize(); + if (pfctl_file_fingerprints(-1, + PF_OPT_QUIET|PF_OPT_NOACTION, PF_OSFP_FILE) == 0) + return 1; + return 0; +} + static pcap_t *pd; extern int optind; @@ -149,9 +159,9 @@ main(int argc, char **argv) { register int cnt, op, i; bpf_u_int32 localnet, netmask; - register char *cp, *infile, *cmdbuf, *device, *RFileName, *WFileName; + register char *cp, *infile, *device, *RFileName, *WFileName; pcap_handler printer; - struct bpf_program fcode; + struct bpf_program *fcode; RETSIGTYPE (*oldhandler)(int); u_char *pcap_userdata; char ebuf[PCAP_ERRBUF_SIZE]; @@ -161,6 +171,11 @@ main(int argc, char **argv) infile = NULL; RFileName = NULL; WFileName = NULL; + + if (priv_init(argc, argv)) + error("Failed to setup privsep"); + + /* state: STATE_INIT */ if ((cp = strrchr(argv[0], '/')) != NULL) program_name = cp + 1; else @@ -224,9 +239,6 @@ main(int argc, char **argv) break; case 'o': - pf_osfp_initialize(); - if (pfctl_file_fingerprints(-1, - PF_OPT_QUIET|PF_OPT_NOACTION, PF_OSFP_FILE) == 0) oflag = 1; break; @@ -313,21 +325,12 @@ main(int argc, char **argv) if (aflag && nflag) error("-a and -n options are incompatible"); - if (tflag > 0) - thiszone = gmt2local(0); - if (RFileName != NULL) { - /* - * We don't need network access, so set it back to the user id. - * Also, this prevents the user from reading anyone's - * trace file. - */ - seteuid(getuid()); - setuid(getuid()); - - pd = pcap_open_offline(RFileName, ebuf); + pd = priv_pcap_offline(RFileName, ebuf); if (pd == NULL) error("%s", ebuf); + + /* state: STATE_BPF */ localnet = 0; netmask = 0; if (fflag != 0) @@ -338,51 +341,45 @@ main(int argc, char **argv) if (device == NULL) error("%s", ebuf); } - pd = pcap_open_live(device, snaplen, !pflag, 1000, ebuf); + pd = priv_pcap_live(device, snaplen, !pflag, 1000, ebuf); if (pd == NULL) error("%s", ebuf); + + /* state: STATE_BPF */ i = pcap_snapshot(pd); if (snaplen < i) { warning("snaplen raised from %d to %d", snaplen, i); snaplen = i; } - if (pcap_lookupnet(device, &localnet, &netmask, ebuf) < 0) { + + if (pcap_lookupnet(device, &localnet, &netmask, ebuf)) { warning("%s", ebuf); localnet = 0; netmask = 0; } - - /* - * Let user own process after socket has been opened. - */ - seteuid(getuid()); - setuid(getuid()); } - if (infile) - cmdbuf = read_infile(infile); - else - cmdbuf = copy_argv(&argv[optind]); - if (pcap_compile(pd, &fcode, cmdbuf, Oflag, netmask) < 0) + fcode = priv_pcap_setfilter(pd, Oflag, netmask); + /* state: STATE_FILTER */ + if (fcode == NULL) error("%s", pcap_geterr(pd)); if (dflag) { - bpf_dump(&fcode, dflag); + bpf_dump(fcode, dflag); exit(0); } init_addrtoname(localnet, netmask); - (void)setsignal(SIGTERM, cleanup); - (void)setsignal(SIGINT, cleanup); - /* Cooperate with nohup(1) */ + setsignal(SIGTERM, cleanup); + setsignal(SIGINT, cleanup); + /* Cooperate with nohup(1) XXX is this still necessary/working? */ if ((oldhandler = setsignal(SIGHUP, cleanup)) != SIG_DFL) (void)setsignal(SIGHUP, oldhandler); - if (pcap_setfilter(pd, &fcode) < 0) - error("%s", pcap_geterr(pd)); if (WFileName) { pcap_dumper_t *p; - p = pcap_dump_open(pd, WFileName); + p = priv_pcap_dump_open(pd, WFileName); + /* state: STATE_RUN */ if (p == NULL) error("%s", pcap_geterr(pd)); { @@ -395,12 +392,21 @@ main(int argc, char **argv) } else { printer = lookup_printer(pcap_datalink(pd)); pcap_userdata = 0; + priv_init_done(); + /* state: STATE_RUN */ } if (RFileName == NULL) { (void)fprintf(stderr, "%s: listening on %s\n", program_name, device); (void)fflush(stderr); } + + if (oflag) + oflag = init_pfosfp(); + if (tflag > 0) + thiszone = gmt2local(0); + + if (pcap_loop(pd, cnt, printer, pcap_userdata) < 0) { (void)fprintf(stderr, "%s: pcap_loop: %s\n", program_name, pcap_geterr(pd)); diff --git a/usr.sbin/tcpdump/util.c b/usr.sbin/tcpdump/util.c index 98c6bc48492..6f4c6985ac6 100644 --- a/usr.sbin/tcpdump/util.c +++ b/usr.sbin/tcpdump/util.c @@ -1,4 +1,4 @@ -/* $OpenBSD: util.c,v 1.15 2002/02/19 19:39:40 millert Exp $ */ +/* $OpenBSD: util.c,v 1.16 2004/01/28 19:44:55 canacar Exp $ */ /* * Copyright (c) 1990, 1991, 1993, 1994, 1995, 1996, 1997 @@ -23,15 +23,17 @@ #ifndef lint static const char rcsid[] = - "@(#) $Header: /cvs/OpenBSD/src/usr.sbin/tcpdump/util.c,v 1.15 2002/02/19 19:39:40 millert Exp $ (LBL)"; + "@(#) $Header: /cvs/OpenBSD/src/usr.sbin/tcpdump/util.c,v 1.16 2004/01/28 19:44:55 canacar Exp $ (LBL)"; #endif #include <sys/types.h> #include <sys/time.h> #include <sys/file.h> +#include <sys/limits.h> #include <sys/stat.h> #include <ctype.h> +#include <err.h> #include <errno.h> #ifdef HAVE_FCNTL_H #include <fcntl.h> @@ -47,7 +49,7 @@ static const char rcsid[] = #include <unistd.h> #include "interface.h" - +#include "privsep.h" /* * Print out a filename (or other ascii string). * If ep is NULL, assume no truncation check is needed. @@ -134,7 +136,7 @@ ts_print(register const struct bpf_timeval *tvp) break; case -2: t=tvp->tv_sec; - strftime(buf, TSBUFLEN, "%b %d %T", localtime(&t)); + strftime(buf, TSBUFLEN, "%b %d %T", priv_localtime(&t)); printf("%s.%06u ", buf, (u_int32_t)tvp->tv_usec); break; default: @@ -230,46 +232,44 @@ warning(const char *fmt, ...) } } + /* * Copy arg vector into a new buffer, concatenating arguments with spaces. */ char * -copy_argv(register char **argv) +copy_argv(char * const *argv) { - register char **p; - register u_int len = 0; + size_t len = 0, n; char *buf; - char *src, *dst; - p = argv; - if (*p == 0) - return 0; + if (argv == NULL) + return (NULL); - while (*p) - len += strlen(*p++) + 1; + for (n = 0; argv[n]; n++) + len += strlen(argv[n])+1; + if (len == 0) + return (NULL); - buf = (char *)malloc(len); + buf = malloc(len); if (buf == NULL) - error("copy_argv: malloc"); - - p = argv; - dst = buf; - while ((src = *p++) != NULL) { - while ((*dst++ = *src++) != '\0') - ; - dst[-1] = ' '; - } - dst[-1] = '\0'; + return (NULL); - return buf; + strlcpy(buf, argv[0], len); + for (n = 1; argv[n]; n++) { + strlcat(buf, " ", len); + strlcat(buf, argv[n], len); + } + return (buf); } char * read_infile(char *fname) { - register int fd, cc; - register char *cp; - struct stat buf; + struct stat buf; + int fd; + ssize_t cc; + size_t bs; + char *cp; fd = open(fname, O_RDONLY); if (fd < 0) @@ -278,13 +278,20 @@ read_infile(char *fname) if (fstat(fd, &buf) < 0) error("can't stat %s: %s", fname, pcap_strerror(errno)); - cp = malloc((u_int)buf.st_size + 1); - cc = read(fd, cp, (int)buf.st_size); - if (cc < 0) + if (buf.st_size >= SSIZE_MAX) + error("file too long"); + + bs = buf.st_size; + cp = malloc(bs + 1); + if (cp == NULL) + err(1, NULL); + cc = read(fd, cp, bs); + if (cc == -1) error("read %s: %s", fname, pcap_strerror(errno)); - if (cc != buf.st_size) - error("short read %s (%d != %d)", fname, cc, (int)buf.st_size); - cp[(int)buf.st_size] = '\0'; + if (cc != bs) + error("short read %s (%ld != %lu)", fname, (long)cc, + (unsigned long)bs); + cp[bs] = '\0'; return (cp); } |