summaryrefslogtreecommitdiff
path: root/usr.sbin
diff options
context:
space:
mode:
authorCan Erkin Acar <canacar@cvs.openbsd.org>2004-01-28 19:44:56 +0000
committerCan Erkin Acar <canacar@cvs.openbsd.org>2004-01-28 19:44:56 +0000
commit87d1aa5c72a876badad301facf67f7ee8ec5880b (patch)
treeca8152a20e4c4e01f6872b9741bc361313522cad /usr.sbin
parenta9765aa5e532b4e14ae9e6a3d66d5c3a514d6de2 (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/Makefile8
-rw-r--r--usr.sbin/tcpdump/addrtoname.c76
-rw-r--r--usr.sbin/tcpdump/addrtoname.h5
-rw-r--r--usr.sbin/tcpdump/gmt2local.c7
-rw-r--r--usr.sbin/tcpdump/interface.h6
-rw-r--r--usr.sbin/tcpdump/pf_print_state.c288
-rw-r--r--usr.sbin/tcpdump/pfctl_osfp.c1093
-rw-r--r--usr.sbin/tcpdump/print-atalk.c74
-rw-r--r--usr.sbin/tcpdump/print-cnfp.c15
-rw-r--r--usr.sbin/tcpdump/print-sunrpc.c12
-rw-r--r--usr.sbin/tcpdump/print-udp.c4
-rw-r--r--usr.sbin/tcpdump/privsep.c901
-rw-r--r--usr.sbin/tcpdump/privsep.h103
-rw-r--r--usr.sbin/tcpdump/privsep_fdpass.c116
-rw-r--r--usr.sbin/tcpdump/privsep_pcap.c496
-rw-r--r--usr.sbin/tcpdump/tcpdump.c86
-rw-r--r--usr.sbin/tcpdump/util.c75
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, &ether, sizeof(ether));
+ if (ether_ntohost(hostname, &ether) == -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, &gt0, sizeof(struct tm)) == 0) {
+ lt.tm_sec = gt0.tm_sec;
+ lt.tm_min = gt0.tm_min;
+ return &lt;
+ }
+ }
+
+ write_command(priv_fd, PRIV_LOCALTIME);
+ must_write(priv_fd, t, sizeof(time_t));
+ must_read(priv_fd, &lt, sizeof(lt));
+ must_read(priv_fd, &gt0, 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 = &gt0;
+
+ return &lt;
+}
+
+/* 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);
}