#include "config.h"
#endif
+/*
+ * Mac OS X may ship pcap.h from libpcap 0.6 with a libpcap based on
+ * 0.8. That means it has pcap_findalldevs() but the header doesn't
+ * define pcap_if_t, meaning that we can't actually *use* pcap_findalldevs().
+ */
+#ifdef HAVE_PCAP_FINDALLDEVS
+#ifndef HAVE_PCAP_IF_T
+#undef HAVE_PCAP_FINDALLDEVS
+#endif
+#endif
+
#include <tcpdump-stdinc.h>
-#ifdef WIN32
+#ifdef _WIN32
#include "w32_fzs.h"
extern int strcasecmp (const char *__s1, const char *__s2);
extern int SIZE_BUF;
#define off_t long
#define uint UINT
-#endif /* WIN32 */
+#endif /* _WIN32 */
-#ifdef HAVE_SMI_H
+#ifdef USE_LIBSMI
#include <smi.h>
#endif
+#ifdef HAVE_LIBCRYPTO
+#include <openssl/crypto.h>
+#endif
+
#ifdef HAVE_GETOPT_LONG
#include <getopt.h>
#else
#include "getopt_long.h"
#endif
+/* Capsicum-specific code requires macros from <net/bpf.h>, which will fail
+ * to compile if <pcap.h> has already been included; including the headers
+ * in the opposite order works fine.
+ */
+#ifdef HAVE_CAPSICUM
+#include <sys/capability.h>
+#include <sys/ioccom.h>
+#include <net/bpf.h>
+#include <fcntl.h>
+#include <libgen.h>
+#endif /* HAVE_CAPSICUM */
#include <pcap.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
-#ifndef WIN32
+#ifndef _WIN32
#include <sys/wait.h>
#include <sys/resource.h>
#include <pwd.h>
#include <grp.h>
-#endif /* WIN32 */
+#endif /* _WIN32 */
-/* capabilities convinience library */
+/* capabilities convenience library */
+/* If a code depends on HAVE_LIBCAP_NG, it depends also on HAVE_CAP_NG_H.
+ * If HAVE_CAP_NG_H is not defined, undefine HAVE_LIBCAP_NG.
+ * Thus, the later tests are done only on HAVE_LIBCAP_NG.
+ */
+#ifdef HAVE_LIBCAP_NG
#ifdef HAVE_CAP_NG_H
#include <cap-ng.h>
+#else
+#undef HAVE_LIBCAP_NG
#endif /* HAVE_CAP_NG_H */
+#endif /* HAVE_LIBCAP_NG */
#include "netdissect.h"
#include "interface.h"
/* Forwards */
static RETSIGTYPE cleanup(int);
static RETSIGTYPE child_cleanup(int);
-static void usage(void) __attribute__((noreturn));
+static void print_version(void);
+static void print_usage(void);
static void show_dlts_and_exit(const char *device, pcap_t *pd) __attribute__((noreturn));
static void print_packet(u_char *, const struct pcap_pkthdr *, const u_char *);
char *CurrentFileName;
pcap_t *pd;
pcap_dumper_t *p;
+#ifdef HAVE_CAPSICUM
+ int dirfd;
+#endif
};
#ifdef HAVE_PCAP_SET_TSTAMP_TYPE
static void
show_dlts_and_exit(const char *device, pcap_t *pd)
{
- int n_dlts;
+ int n_dlts, i;
int *dlts = 0;
const char *dlt_name;
(void) fprintf(stderr, "Data link types for %s (use option -y to set):\n",
device);
- while (--n_dlts >= 0) {
- dlt_name = pcap_datalink_val_to_name(dlts[n_dlts]);
+ for (i = 0; i < n_dlts; i++) {
+ dlt_name = pcap_datalink_val_to_name(dlts[i]);
if (dlt_name != NULL) {
(void) fprintf(stderr, " %s (%s)", dlt_name,
- pcap_datalink_val_to_description(dlts[n_dlts]));
+ pcap_datalink_val_to_description(dlts[i]));
/*
* OK, does tcpdump handle that type?
*/
- if (lookup_printer(dlts[n_dlts]) == NULL
- && lookup_ndo_printer(dlts[n_dlts]) == NULL)
+ if (lookup_printer(dlts[i]) == NULL
+ && lookup_ndo_printer(dlts[i]) == NULL)
(void) fprintf(stderr, " (printing not supported)");
fprintf(stderr, "\n");
} else {
(void) fprintf(stderr, " DLT %d (printing not supported)\n",
- dlts[n_dlts]);
+ dlts[i]);
}
}
#ifdef HAVE_PCAP_FREE_DATALINKS
*
* OS X tcpdump uses -P to indicate that -w should write pcap-ng rather
* than pcap files.
+ *
+ * OS X tcpdump also uses -Q to specify expressions that match packet
+ * metadata, including but not limited to the packet direction.
+ * The expression syntax is different from a simple "in|out|inout",
+ * and those expressions aren't accepted by OS X tcpdump, but the
+ * equivalents would be "in" = "dir=in", "out" = "dir=out", and
+ * "inout" = "dir=in or dir=out", and the parser could conceivably
+ * special-case "in", "out", and "inout" as expressions for backwards
+ * compatibility, so all is not (yet) lost.
*/
/*
* Set up flags that might or might not be supported depending on the
* version of libpcap we're using.
*/
-#if defined(HAVE_PCAP_CREATE) || defined(WIN32)
+#if defined(HAVE_PCAP_CREATE) || defined(_WIN32)
#define B_FLAG "B:"
#define B_FLAG_USAGE " [ -B size ]"
-#else /* defined(HAVE_PCAP_CREATE) || defined(WIN32) */
+#else /* defined(HAVE_PCAP_CREATE) || defined(_WIN32) */
#define B_FLAG
#define B_FLAG_USAGE
-#endif /* defined(HAVE_PCAP_CREATE) || defined(WIN32) */
+#endif /* defined(HAVE_PCAP_CREATE) || defined(_WIN32) */
#ifdef HAVE_PCAP_CREATE
#define I_FLAG "I"
#define J_FLAG
#endif /* PCAP_ERROR_TSTAMP_TYPE_NOTSUP */
-#ifdef HAVE_PCAP_FINDALLDEVS
-#ifndef HAVE_PCAP_IF_T
-#undef HAVE_PCAP_FINDALLDEVS
-#endif
-#endif
-
#ifdef HAVE_PCAP_FINDALLDEVS
#define D_FLAG "D"
#else
#define Q_FLAG
#endif
+#define SHORTOPTS "aAb" B_FLAG "c:C:d" D_FLAG "eE:fF:G:hHi:" I_FLAG j_FLAG J_FLAG "KlLm:M:nNOpq" Q_FLAG "r:Rs:StT:u" U_FLAG "vV:w:W:xXy:Yz:Z:#"
+
/*
* Long options.
*
* doesn't make sense; it should be --verbosity={N} or something such
* as that.
*
- * We do not currently have long options with no corresponding short
- * options; for those, we should define values outside the range of
- * ASCII graphic characters, make that the last component of the
- * entry for the long option, and have a case for that option in the
- * switch statement.
+ * For long options with no corresponding short options, we define values
+ * outside the range of ASCII graphic characters, make that the last
+ * component of the entry for the long option, and have a case for that
+ * option in the switch statement.
*/
-static struct option longopts[] = {
-#if defined(HAVE_PCAP_CREATE) || defined(WIN32)
+#define OPTION_VERSION 128
+#define OPTION_TSTAMP_PRECISION 129
+#define OPTION_IMMEDIATE_MODE 130
+
+static const struct option longopts[] = {
+#if defined(HAVE_PCAP_CREATE) || defined(_WIN32)
{ "buffer-size", required_argument, NULL, 'B' },
#endif
{ "list-interfaces", no_argument, NULL, 'D' },
#ifdef HAVE_PCAP_SET_TSTAMP_TYPE
{ "time-stamp-type", required_argument, NULL, 'j' },
{ "list-time-stamp-types", no_argument, NULL, 'J' },
+#endif
+#ifdef HAVE_PCAP_SET_TSTAMP_PRECISION
+ { "time-stamp-precision", required_argument, NULL, OPTION_TSTAMP_PRECISION},
#endif
{ "dont-verify-checksums", no_argument, NULL, 'K' },
{ "list-data-link-types", no_argument, NULL, 'L' },
{ "packet-buffered", no_argument, NULL, 'U' },
#endif
{ "linktype", required_argument, NULL, 'y' },
+#ifdef HAVE_PCAP_SET_IMMEDIATE_MODE
+ { "immediate-mode", no_argument, NULL, OPTION_IMMEDIATE_MODE },
+#endif
#if defined(HAVE_PCAP_DEBUG) || defined(HAVE_YYDEBUG)
{ "debug-filter-parser", no_argument, NULL, 'Y' },
#endif
{ "relinquish-privileges", required_argument, NULL, 'Z' },
- { "number", no_argument, NULL, 'z' + 1},
+ { "number", no_argument, NULL, '#' },
+ { "version", no_argument, NULL, OPTION_VERSION },
{ NULL, 0, NULL, 0 }
};
-#ifndef WIN32
+#ifndef _WIN32
/* Drop root privileges and chroot if necessary */
static void
droproot(const char *username, const char *chroot_dir)
exit(1);
}
}
-#ifdef HAVE_CAP_NG_H
+#ifdef HAVE_LIBCAP_NG
int ret = capng_change_id(pw->pw_uid, pw->pw_gid, CAPNG_NO_FLAG);
if (ret < 0) {
- printf("error : ret %d\n", ret);
+ fprintf(stderr, "error : ret %d\n", ret);
+ }
+ else {
+ fprintf(stderr, "dropped privs to %s\n", username);
}
- /* We don't need CAP_SETUID and CAP_SETGID */
- capng_update(CAPNG_DROP, CAPNG_EFFECTIVE, CAP_SETUID);
- capng_update(CAPNG_DROP, CAPNG_EFFECTIVE, CAP_SETUID);
- capng_update(CAPNG_DROP, CAPNG_PERMITTED, CAP_SETUID);
- capng_update(CAPNG_DROP, CAPNG_PERMITTED, CAP_SETUID);
- capng_apply(CAPNG_SELECT_BOTH);
-
#else
if (initgroups(pw->pw_name, pw->pw_gid) != 0 ||
setgid(pw->pw_gid) != 0 || setuid(pw->pw_uid) != 0) {
pcap_strerror(errno));
exit(1);
}
-#endif /* HAVE_CAP_NG_H */
+ else {
+ fprintf(stderr, "dropped privs to %s\n", username);
+ }
+#endif /* HAVE_LIBCAP_NG */
}
else {
fprintf(stderr, "tcpdump: Couldn't find user '%.32s'\n",
username);
exit(1);
}
+#ifdef HAVE_LIBCAP_NG
+ /* We don't need CAP_SETUID and CAP_SETGID any more. */
+ capng_updatev(
+ CAPNG_DROP,
+ CAPNG_EFFECTIVE | CAPNG_PERMITTED,
+ CAP_SETUID,
+ CAP_SETGID,
+ -1);
+ capng_apply(CAPNG_SELECT_BOTH);
+#endif /* HAVE_LIBCAP_NG */
+
}
-#endif /* WIN32 */
+#endif /* _WIN32 */
static int
getWflagChars(int x)
return ret;
}
+#ifdef HAVE_PCAP_SET_TSTAMP_PRECISION
+static int
+tstamp_precision_from_string(const char *precision)
+{
+ if (strncmp(precision, "nano", strlen("nano")) == 0)
+ return PCAP_TSTAMP_PRECISION_NANO;
+
+ if (strncmp(precision, "micro", strlen("micro")) == 0)
+ return PCAP_TSTAMP_PRECISION_MICRO;
+
+ return -EINVAL;
+}
+
+static const char *
+tstamp_precision_to_string(int precision)
+{
+ switch (precision) {
+
+ case PCAP_TSTAMP_PRECISION_MICRO:
+ return "micro";
+
+ case PCAP_TSTAMP_PRECISION_NANO:
+ return "nano";
+
+ default:
+ return "unknown";
+ }
+}
+#endif
+
+#ifdef HAVE_CAPSICUM
+/*
+ * Ensure that, on a dump file's descriptor, we have all the rights
+ * necessary to make the standard I/O library work with an fdopen()ed
+ * FILE * from that descriptor.
+ *
+ * A long time ago, in a galaxy far far away, AT&T decided that, instead
+ * of providing separate APIs for getting and setting the FD_ flags on a
+ * descriptor, getting and setting the O_ flags on a descriptor, and
+ * locking files, they'd throw them all into a kitchen-sink fcntl() call
+ * along the lines of ioctl(), the fact that ioctl() operations are
+ * largely specific to particular character devices but fcntl() operations
+ * are either generic to all descriptors or generic to all descriptors for
+ * regular files nonwithstanding.
+ *
+ * The Capsicum people decided that fine-grained control of descriptor
+ * operations was required, so that you need to grant permission for
+ * reading, writing, seeking, and fcntl-ing. The latter, courtesy of
+ * AT&T's decision, means that "fcntl-ing" isn't a thing, but a motley
+ * collection of things, so there are *individual* fcntls for which
+ * permission needs to be granted.
+ *
+ * The FreeBSD standard I/O people implemented some optimizations that
+ * requires that the standard I/O routines be able to determine whether
+ * the descriptor for the FILE * is open append-only or not; as that
+ * descriptor could have come from an open() rather than an fopen(),
+ * that requires that it be able to do an F_GETFL fcntl() to read
+ * the O_ flags.
+ *
+ * Tcpdump uses ftell() to determine how much data has been written
+ * to a file in order to, when used with -C, determine when it's time
+ * to rotate capture files. ftell() therefore needs to do an lseek()
+ * to find out the file offset and must, thanks to the aforementioned
+ * optimization, also know whether the descriptor is open append-only
+ * or not.
+ *
+ * The net result of all the above is that we need to grant CAP_SEEK,
+ * CAP_WRITE, and CAP_FCNTL with the CAP_FCNTL_GETFL subcapability.
+ *
+ * Perhaps this is the universe's way of saying that either
+ *
+ * 1) there needs to be an fopenat() call and a pcap_dump_openat() call
+ * using it, so that Capsicum-capable tcpdump wouldn't need to do
+ * an fdopen()
+ *
+ * or
+ *
+ * 2) there needs to be a cap_fdopen() call in the FreeBSD standard
+ * I/O library that knows what rights are needed by the standard
+ * I/O library, based on the open mode, and assigns them, perhaps
+ * with an additional argument indicating, for example, whether
+ * seeking should be allowed, so that tcpdump doesn't need to know
+ * what the standard I/O library happens to require this week.
+ */
+static void
+set_dumper_capsicum_rights(pcap_dumper_t *p)
+{
+ int fd = fileno(pcap_dump_file(p));
+ cap_rights_t rights;
+
+ cap_rights_init(&rights, CAP_SEEK, CAP_WRITE, CAP_FCNTL);
+ if (cap_rights_limit(fd, &rights) < 0 && errno != ENOSYS) {
+ error("unable to limit dump descriptor");
+ }
+ if (cap_fcntls_limit(fd, CAP_FCNTL_GETFL) < 0 && errno != ENOSYS) {
+ error("unable to limit dump descriptor fcntls");
+ }
+}
+#endif
+
int
main(int argc, char **argv)
{
int new_dlt;
const char *dlt_name;
struct bpf_program fcode;
-#ifndef WIN32
+#ifndef _WIN32
RETSIGTYPE (*oldhandler)(int);
#endif
struct print_info printinfo;
#endif
int status;
FILE *VFile;
-#ifdef WIN32
+#ifdef HAVE_CAPSICUM
+ cap_rights_t rights;
+ int cansandbox;
+#endif /* HAVE_CAPSICUM */
+
+#ifdef _WIN32
if(wsockinit() != 0) return 1;
-#endif /* WIN32 */
+#endif /* _WIN32 */
jflag=-1; /* not set */
gndo->ndo_Oflag=1;
gndo->ndo_error=ndo_error;
gndo->ndo_warning=ndo_warning;
gndo->ndo_snaplen = DEFAULT_SNAPLEN;
+ gndo->ndo_immediate = 0;
cnt = -1;
device = NULL;
else
program_name = argv[0];
+ /*
+ * On platforms where the CPU doesn't support unaligned loads,
+ * force unaligned accesses to abort with SIGBUS, rather than
+ * being fixed up (slowly) by the OS kernel; on those platforms,
+ * misaligned accesses are bugs, and we want tcpdump to crash so
+ * that the bugs are reported.
+ */
if (abort_on_misalignment(ebuf, sizeof(ebuf)) < 0)
error("%s", ebuf);
-#ifdef LIBSMI
+#ifdef USE_LIBSMI
smiInit("tcpdump");
#endif
while (
- (op = getopt_long(argc, argv, "aAb" B_FLAG "c:C:d" D_FLAG "eE:fF:G:hHi:" I_FLAG j_FLAG J_FLAG "KlLm:M:nNOpq" Q_FLAG "r:Rs:StT:u" U_FLAG "vV:w:W:xXy:Yz:Z:", longopts, NULL)) != -1)
+ (op = getopt_long(argc, argv, SHORTOPTS, longopts, NULL)) != -1)
switch (op) {
case 'a':
++bflag;
break;
-#if defined(HAVE_PCAP_CREATE) || defined(WIN32)
+#if defined(HAVE_PCAP_CREATE) || defined(_WIN32)
case 'B':
Bflag = atoi(optarg)*1024;
if (Bflag <= 0)
error("invalid packet buffer size %s", optarg);
break;
-#endif /* defined(HAVE_PCAP_CREATE) || defined(WIN32) */
+#endif /* defined(HAVE_PCAP_CREATE) || defined(_WIN32) */
case 'c':
cnt = atoi(optarg);
break;
case 'h':
- usage();
+ print_usage();
+ exit(0);
break;
case 'H':
#endif
case 'l':
-#ifdef WIN32
+#ifdef _WIN32
/*
* _IOLBF is the same as _IOFBF in Microsoft's C
* libraries; the only alternative they offer
* is _IONBF.
*
* XXX - this should really be checking for MSVC++,
- * not WIN32, if, for example, MinGW has its own
+ * not _WIN32, if, for example, MinGW has its own
* C library that is more UNIX-compatible.
*/
setvbuf(stdout, NULL, _IONBF, 0);
-#else /* WIN32 */
+#else /* _WIN32 */
#ifdef HAVE_SETLINEBUF
setlinebuf(stdout);
#else
setvbuf(stdout, NULL, _IOLBF, 0);
#endif
-#endif /* WIN32 */
+#endif /* _WIN32 */
break;
case 'K':
break;
case 'm':
-#ifdef LIBSMI
+#ifdef USE_LIBSMI
if (smiLoadModule(optarg) == 0) {
error("could not load MIB module %s", optarg);
}
username = strdup(optarg);
break;
- case 'z' + 1:
+ case '#':
gndo->ndo_packet_number = 1;
break;
+ case OPTION_VERSION:
+ print_version();
+ exit(0);
+ break;
+
+#ifdef HAVE_PCAP_SET_TSTAMP_PRECISION
+ case OPTION_TSTAMP_PRECISION:
+ gndo->ndo_tstamp_precision = tstamp_precision_from_string(optarg);
+ if (gndo->ndo_tstamp_precision < 0)
+ error("unsupported time stamp precision");
+ break;
+#endif
+
+#ifdef HAVE_PCAP_SET_IMMEDIATE_MODE
+ case OPTION_IMMEDIATE_MODE:
+ gndo->ndo_immediate = 1;
+ break;
+#endif
+
default:
- usage();
+ print_usage();
+ exit(1);
/* NOTREACHED */
}
if (VFileName != NULL && RFileName != NULL)
error("-V and -r are mutually exclusive.");
+#ifdef HAVE_PCAP_SET_IMMEDIATE_MODE
+ /*
+ * If we're printing dissected packets to the standard output
+ * rather than saving raw packets to a file, and the standard
+ * output is a terminal, use immediate mode, as the user's
+ * probably expecting to see packets pop up immediately.
+ */
+ if (WFileName == NULL && isatty(1))
+ gndo->ndo_immediate = 1;
+#endif
+
#ifdef WITH_CHROOT
/* if run as root, prepare for chrooting */
if (getuid() == 0 || geteuid() == 0) {
* In either case, we're reading a savefile, not doing
* a live capture.
*/
-#ifndef WIN32
+#ifndef _WIN32
/*
* We don't need network access, so relinquish any set-UID
* or set-GID privileges we have (if any).
*/
if (setgid(getgid()) != 0 || setuid(getuid()) != 0 )
fprintf(stderr, "Warning: setgid/setuid failed !\n");
-#endif /* WIN32 */
+#endif /* _WIN32 */
if (VFileName != NULL) {
if (VFileName[0] == '-' && VFileName[1] == '\0')
VFile = stdin;
RFileName = VFileLine;
}
+#ifdef HAVE_PCAP_SET_TSTAMP_PRECISION
+ pd = pcap_open_offline_with_tstamp_precision(RFileName,
+ gndo->ndo_tstamp_precision, ebuf);
+#else
pd = pcap_open_offline(RFileName, ebuf);
+#endif
+
if (pd == NULL)
error("%s", ebuf);
+#ifdef HAVE_CAPSICUM
+ cap_rights_init(&rights, CAP_READ);
+ if (cap_rights_limit(fileno(pcap_file(pd)), &rights) < 0 &&
+ errno != ENOSYS) {
+ error("unable to limit pcap descriptor");
+ }
+#endif
dlt = pcap_datalink(pd);
dlt_name = pcap_datalink_val_to_name(dlt);
if (dlt_name == NULL) {
if (device == NULL)
error("%s", ebuf);
}
-#ifdef WIN32
+#ifdef _WIN32
/*
* Print a message to the standard error on Windows.
* XXX - why do it here, with a different message?
}
fflush(stderr);
-#endif /* WIN32 */
+#endif /* _WIN32 */
#ifdef HAVE_PCAP_CREATE
pd = pcap_create(device, ebuf);
if (pd == NULL)
#ifdef HAVE_PCAP_SET_TSTAMP_TYPE
if (Jflag)
show_tstamp_types_and_exit(device, pd);
+#endif
+#ifdef HAVE_PCAP_SET_TSTAMP_PRECISION
+ status = pcap_set_tstamp_precision(pd, gndo->ndo_tstamp_precision);
+ if (status != 0)
+ error("%s: Can't set %ssecond time stamp precision: %s",
+ device,
+ tstamp_precision_to_string(gndo->ndo_tstamp_precision),
+ pcap_statustostr(status));
+#endif
+
+#ifdef HAVE_PCAP_SET_IMMEDIATE_MODE
+ if (gndo->ndo_immediate) {
+ status = pcap_set_immediate_mode(pd, 1);
+ if (status != 0)
+ error("%s: Can't set immediate mode: %s",
+ device,
+ pcap_statustostr(status));
+ }
#endif
/*
* Is this an interface that supports monitor mode?
status = pcap_set_tstamp_type(pd, jflag);
if (status < 0)
error("%s: Can't set time stamp type: %s",
- device, pcap_statustostr(status));
+ device, pcap_statustostr(status));
}
#endif
status = pcap_activate(pd);
/*
* Let user own process after socket has been opened.
*/
-#ifndef WIN32
+#ifndef _WIN32
if (setgid(getgid()) != 0 || setuid(getuid()) != 0)
fprintf(stderr, "Warning: setgid/setuid failed !\n");
-#endif /* WIN32 */
-#if !defined(HAVE_PCAP_CREATE) && defined(WIN32)
+#endif /* _WIN32 */
+#if !defined(HAVE_PCAP_CREATE) && defined(_WIN32)
if(Bflag != 0)
if(pcap_setbuff(pd, Bflag)==-1){
error("%s", pcap_geterr(pd));
}
-#endif /* !defined(HAVE_PCAP_CREATE) && defined(WIN32) */
+#endif /* !defined(HAVE_PCAP_CREATE) && defined(_WIN32) */
if (Lflag)
show_dlts_and_exit(device, pd);
if (gndo->ndo_dlt >= 0) {
init_addrtoname(gndo, localnet, netmask);
init_checksum();
-#ifndef WIN32
+#ifndef _WIN32
(void)setsignal(SIGPIPE, cleanup);
(void)setsignal(SIGTERM, cleanup);
(void)setsignal(SIGINT, cleanup);
-#endif /* WIN32 */
+#endif /* _WIN32 */
#if defined(HAVE_FORK) || defined(HAVE_VFORK)
(void)setsignal(SIGCHLD, child_cleanup);
#endif
/* Cooperate with nohup(1) */
-#ifndef WIN32
+#ifndef _WIN32
if ((oldhandler = setsignal(SIGHUP, cleanup)) != SIG_DFL)
(void)setsignal(SIGHUP, oldhandler);
-#endif /* WIN32 */
+#endif /* _WIN32 */
-#ifndef WIN32
+#ifndef _WIN32
/*
* If a user name was specified with "-Z", attempt to switch to
* that user's UID. This would probably be used with sudo,
* savefile doesn't handle the general case.
*/
-#ifdef HAVE_CAP_NG_H
- /* We are running as root and we will be writing to savefile */
- if ((getuid() == 0 || geteuid() == 0) && WFileName) {
+ if (getuid() == 0 || geteuid() == 0) {
+#ifdef HAVE_LIBCAP_NG
+ /* Initialize capng */
+ capng_clear(CAPNG_SELECT_BOTH);
if (username) {
- /* Drop all capabilities from effective set */
- capng_clear(CAPNG_EFFECTIVE);
- /* Add capabilities we will need*/
- capng_update(CAPNG_ADD, CAPNG_PERMITTED, CAP_SETUID);
- capng_update(CAPNG_ADD, CAPNG_PERMITTED, CAP_SETGID);
- capng_update(CAPNG_ADD, CAPNG_PERMITTED, CAP_DAC_OVERRIDE);
-
- capng_update(CAPNG_ADD, CAPNG_EFFECTIVE, CAP_SETUID);
- capng_update(CAPNG_ADD, CAPNG_EFFECTIVE, CAP_SETGID);
- capng_update(CAPNG_ADD, CAPNG_EFFECTIVE, CAP_DAC_OVERRIDE);
-
- capng_apply(CAPNG_SELECT_BOTH);
+ capng_updatev(
+ CAPNG_ADD,
+ CAPNG_PERMITTED | CAPNG_EFFECTIVE,
+ CAP_SETUID,
+ CAP_SETGID,
+ -1);
}
- }
-#endif /* HAVE_CAP_NG_H */
- if (getuid() == 0 || geteuid() == 0) {
+ if (WFileName) {
+ capng_update(
+ CAPNG_ADD,
+ CAPNG_PERMITTED | CAPNG_EFFECTIVE,
+ CAP_DAC_OVERRIDE
+ );
+ }
+ capng_apply(CAPNG_SELECT_BOTH);
+#endif /* HAVE_LIBCAP_NG */
if (username || chroot_dir)
droproot(username, chroot_dir);
}
-#endif /* WIN32 */
+#endif /* _WIN32 */
if (pcap_setfilter(pd, &fcode) < 0)
error("%s", pcap_geterr(pd));
+#ifdef HAVE_CAPSICUM
+ if (RFileName == NULL && VFileName == NULL) {
+ static const unsigned long cmds[] = { BIOCGSTATS };
+
+ cap_rights_init(&rights, CAP_IOCTL, CAP_READ);
+ if (cap_rights_limit(pcap_fileno(pd), &rights) < 0 &&
+ errno != ENOSYS) {
+ error("unable to limit pcap descriptor");
+ }
+ if (cap_ioctls_limit(pcap_fileno(pd), cmds,
+ sizeof(cmds) / sizeof(cmds[0])) < 0 && errno != ENOSYS) {
+ error("unable to limit ioctls on pcap descriptor");
+ }
+ }
+#endif
if (WFileName) {
pcap_dumper_t *p;
/* Do not exceed the default PATH_MAX for files. */
MakeFilename(dumpinfo.CurrentFileName, WFileName, 0, 0);
p = pcap_dump_open(pd, dumpinfo.CurrentFileName);
-#ifdef HAVE_CAP_NG_H
- /* Give up capabilities, clear Effective set */
- capng_clear(CAPNG_EFFECTIVE);
-#endif
+#ifdef HAVE_LIBCAP_NG
+ /* Give up CAP_DAC_OVERRIDE capability.
+ * Only allow it to be restored if the -C or -G flag have been
+ * set since we may need to create more files later on.
+ */
+ capng_update(
+ CAPNG_DROP,
+ (Cflag || Gflag ? 0 : CAPNG_PERMITTED)
+ | CAPNG_EFFECTIVE,
+ CAP_DAC_OVERRIDE
+ );
+ capng_apply(CAPNG_SELECT_BOTH);
+#endif /* HAVE_LIBCAP_NG */
if (p == NULL)
error("%s", pcap_geterr(pd));
+#ifdef HAVE_CAPSICUM
+ set_dumper_capsicum_rights(p);
+#endif
if (Cflag != 0 || Gflag != 0) {
- callback = dump_packet_and_trunc;
+#ifdef HAVE_CAPSICUM
+ dumpinfo.WFileName = strdup(basename(WFileName));
+ dumpinfo.dirfd = open(dirname(WFileName),
+ O_DIRECTORY | O_RDONLY);
+ if (dumpinfo.dirfd < 0) {
+ error("unable to open directory %s",
+ dirname(WFileName));
+ }
+ cap_rights_init(&rights, CAP_CREATE, CAP_FCNTL,
+ CAP_FTRUNCATE, CAP_LOOKUP, CAP_SEEK, CAP_WRITE);
+ if (cap_rights_limit(dumpinfo.dirfd, &rights) < 0 &&
+ errno != ENOSYS) {
+ error("unable to limit directory rights");
+ }
+ if (cap_fcntls_limit(dumpinfo.dirfd, CAP_FCNTL_GETFL) < 0 &&
+ errno != ENOSYS) {
+ error("unable to limit dump descriptor fcntls");
+ }
+#else /* !HAVE_CAPSICUM */
dumpinfo.WFileName = WFileName;
+#endif
+ callback = dump_packet_and_trunc;
dumpinfo.pd = pd;
dumpinfo.p = p;
pcap_userdata = (u_char *)&dumpinfo;
#endif
}
-#ifndef WIN32
+#ifndef _WIN32
if (RFileName == NULL) {
/*
* Live capture (if -V was specified, we set RFileName
}
(void)fflush(stderr);
}
-#endif /* WIN32 */
+#endif /* _WIN32 */
+
+#ifdef HAVE_CAPSICUM
+ cansandbox = (nflag && VFileName == NULL && zflag == NULL);
+ if (cansandbox && cap_enter() < 0 && errno != ENOSYS)
+ error("unable to enter the capability mode");
+ if (cap_sandboxed())
+ fprintf(stderr, "capability mode sandbox enabled\n");
+#endif /* HAVE_CAPSICUM */
+
do {
status = pcap_loop(pd, cnt, callback, pcap_userdata);
if (WFileName == NULL) {
pd = pcap_open_offline(RFileName, ebuf);
if (pd == NULL)
error("%s", ebuf);
+#ifdef HAVE_CAPSICUM
+ cap_rights_init(&rights, CAP_READ);
+ if (cap_rights_limit(fileno(pcap_file(pd)),
+ &rights) < 0 && errno != ENOSYS) {
+ error("unable to limit pcap descriptor");
+ }
+#endif
new_dlt = pcap_datalink(pd);
if (WFileName && new_dlt != dlt)
error("%s: new dlt does not match original", RFileName);
/* If the time is greater than the specified window, rotate */
if (t - Gflag_time >= Gflag) {
+#ifdef HAVE_CAPSICUM
+ FILE *fp;
+ int fd;
+#endif
+
/* Update the Gflag_time */
Gflag_time = t;
/* Update Gflag_count */
else
MakeFilename(dump_info->CurrentFileName, dump_info->WFileName, 0, 0);
-#ifdef HAVE_CAP_NG_H
+#ifdef HAVE_LIBCAP_NG
capng_update(CAPNG_ADD, CAPNG_EFFECTIVE, CAP_DAC_OVERRIDE);
- capng_apply(CAPNG_EFFECTIVE);
-#endif /* HAVE_CAP_NG_H */
+ capng_apply(CAPNG_SELECT_BOTH);
+#endif /* HAVE_LIBCAP_NG */
+#ifdef HAVE_CAPSICUM
+ fd = openat(dump_info->dirfd,
+ dump_info->CurrentFileName,
+ O_CREAT | O_WRONLY | O_TRUNC, 0644);
+ if (fd < 0) {
+ error("unable to open file %s",
+ dump_info->CurrentFileName);
+ }
+ fp = fdopen(fd, "w");
+ if (fp == NULL) {
+ error("unable to fdopen file %s",
+ dump_info->CurrentFileName);
+ }
+ dump_info->p = pcap_dump_fopen(dump_info->pd, fp);
+#else /* !HAVE_CAPSICUM */
dump_info->p = pcap_dump_open(dump_info->pd, dump_info->CurrentFileName);
-#ifdef HAVE_CAP_NG_H
+#endif
+#ifdef HAVE_LIBCAP_NG
capng_update(CAPNG_DROP, CAPNG_EFFECTIVE, CAP_DAC_OVERRIDE);
- capng_apply(CAPNG_EFFECTIVE);
-#endif /* HAVE_CAP_NG_H */
+ capng_apply(CAPNG_SELECT_BOTH);
+#endif /* HAVE_LIBCAP_NG */
if (dump_info->p == NULL)
error("%s", pcap_geterr(pd));
+#ifdef HAVE_CAPSICUM
+ set_dumper_capsicum_rights(dump_info->p);
+#endif
}
}
* larger than Cflag - the last packet written to the
* file could put it over Cflag.
*/
- if (Cflag != 0 && pcap_dump_ftell(dump_info->p) > Cflag) {
- /*
- * Close the current file and open a new one.
- */
- pcap_dump_close(dump_info->p);
+ if (Cflag != 0) {
+ long size = pcap_dump_ftell(dump_info->p);
- /*
- * Compress the file we just closed, if the user asked for it
- */
- if (zflag != NULL)
- compress_savefile(dump_info->CurrentFileName);
+ if (size == -1)
+ error("ftell fails on output file");
+ if (size > Cflag) {
+#ifdef HAVE_CAPSICUM
+ FILE *fp;
+ int fd;
+#endif
+
+ /*
+ * Close the current file and open a new one.
+ */
+ pcap_dump_close(dump_info->p);
+
+ /*
+ * Compress the file we just closed, if the user
+ * asked for it.
+ */
+ if (zflag != NULL)
+ compress_savefile(dump_info->CurrentFileName);
- Cflag_count++;
- if (Wflag > 0) {
- if (Cflag_count >= Wflag)
- Cflag_count = 0;
+ Cflag_count++;
+ if (Wflag > 0) {
+ if (Cflag_count >= Wflag)
+ Cflag_count = 0;
+ }
+ if (dump_info->CurrentFileName != NULL)
+ free(dump_info->CurrentFileName);
+ dump_info->CurrentFileName = (char *)malloc(PATH_MAX + 1);
+ if (dump_info->CurrentFileName == NULL)
+ error("dump_packet_and_trunc: malloc");
+ MakeFilename(dump_info->CurrentFileName, dump_info->WFileName, Cflag_count, WflagChars);
+#ifdef HAVE_LIBCAP_NG
+ capng_update(CAPNG_ADD, CAPNG_EFFECTIVE, CAP_DAC_OVERRIDE);
+ capng_apply(CAPNG_SELECT_BOTH);
+#endif /* HAVE_LIBCAP_NG */
+#ifdef HAVE_CAPSICUM
+ fd = openat(dump_info->dirfd, dump_info->CurrentFileName,
+ O_CREAT | O_WRONLY | O_TRUNC, 0644);
+ if (fd < 0) {
+ error("unable to open file %s",
+ dump_info->CurrentFileName);
+ }
+ fp = fdopen(fd, "w");
+ if (fp == NULL) {
+ error("unable to fdopen file %s",
+ dump_info->CurrentFileName);
+ }
+ dump_info->p = pcap_dump_fopen(dump_info->pd, fp);
+#else /* !HAVE_CAPSICUM */
+ dump_info->p = pcap_dump_open(dump_info->pd, dump_info->CurrentFileName);
+#endif
+#ifdef HAVE_LIBCAP_NG
+ capng_update(CAPNG_DROP, CAPNG_EFFECTIVE, CAP_DAC_OVERRIDE);
+ capng_apply(CAPNG_SELECT_BOTH);
+#endif /* HAVE_LIBCAP_NG */
+ if (dump_info->p == NULL)
+ error("%s", pcap_geterr(pd));
+#ifdef HAVE_CAPSICUM
+ set_dumper_capsicum_rights(dump_info->p);
+#endif
}
- if (dump_info->CurrentFileName != NULL)
- free(dump_info->CurrentFileName);
- dump_info->CurrentFileName = (char *)malloc(PATH_MAX + 1);
- if (dump_info->CurrentFileName == NULL)
- error("dump_packet_and_trunc: malloc");
- MakeFilename(dump_info->CurrentFileName, dump_info->WFileName, Cflag_count, WflagChars);
- dump_info->p = pcap_dump_open(dump_info->pd, dump_info->CurrentFileName);
- if (dump_info->p == NULL)
- error("%s", pcap_geterr(pd));
}
pcap_dump((u_char *)dump_info->p, h, sp);
/*
* Some printers want to check that they're not walking off the
* end of the packet.
- * Rather than pass it all the way down, we set this global.
+ * Rather than pass it all the way down, we set this member
+ * of the netdissect_options structure.
*/
ndo->ndo_snapend = sp + h->caplen;
hdrlen = (*print_info->p.printer)(h, sp);
}
+ /*
+ * Restore the original snapend, as a printer might have
+ * changed it.
+ */
+ ndo->ndo_snapend = sp + h->caplen;
if (ndo->ndo_Xflag) {
/*
* Print the raw packet data in hex and ASCII.
info(0);
}
-#ifdef WIN32
+#ifdef _WIN32
/*
* XXX - there should really be libpcap calls to get the version
* number as a string (the string would be generated from #defines
static void
ndo_default_print(netdissect_options *ndo, const u_char *bp, u_int length)
{
- hex_and_ascii_print(ndo, "\n\t", bp, length); /* pass on lf and identation string */
+ hex_and_ascii_print(ndo, "\n\t", bp, length); /* pass on lf and indentation string */
}
void
}
#endif
+USES_APPLE_DEPRECATED_API
static void
-usage(void)
+print_version(void)
{
extern char version[];
#ifndef HAVE_PCAP_LIB_VERSION
-#if defined(WIN32) || defined(HAVE_PCAP_VERSION)
+#if defined(_WIN32) || defined(HAVE_PCAP_VERSION)
extern char pcap_version[];
-#else /* defined(WIN32) || defined(HAVE_PCAP_VERSION) */
+#else /* defined(_WIN32) || defined(HAVE_PCAP_VERSION) */
static char pcap_version[] = "unknown";
-#endif /* defined(WIN32) || defined(HAVE_PCAP_VERSION) */
+#endif /* defined(_WIN32) || defined(HAVE_PCAP_VERSION) */
#endif /* HAVE_PCAP_LIB_VERSION */
#ifdef HAVE_PCAP_LIB_VERSION
-#ifdef WIN32
+#ifdef _WIN32
(void)fprintf(stderr, "%s version %s, based on tcpdump version %s\n", program_name, WDversion, version);
-#else /* WIN32 */
+#else /* _WIN32 */
(void)fprintf(stderr, "%s version %s\n", program_name, version);
-#endif /* WIN32 */
+#endif /* _WIN32 */
(void)fprintf(stderr, "%s\n",pcap_lib_version());
#else /* HAVE_PCAP_LIB_VERSION */
-#ifdef WIN32
+#ifdef _WIN32
(void)fprintf(stderr, "%s version %s, based on tcpdump version %s\n", program_name, WDversion, version);
(void)fprintf(stderr, "WinPcap version %s, based on libpcap version %s\n",Wpcap_version, pcap_version);
-#else /* WIN32 */
+#else /* _WIN32 */
(void)fprintf(stderr, "%s version %s\n", program_name, version);
(void)fprintf(stderr, "libpcap version %s\n", pcap_version);
-#endif /* WIN32 */
+#endif /* _WIN32 */
#endif /* HAVE_PCAP_LIB_VERSION */
+
+#if defined(HAVE_LIBCRYPTO) && defined(SSLEAY_VERSION)
+ (void)fprintf (stderr, "%s\n", SSLeay_version(SSLEAY_VERSION));
+#endif
+
+#ifdef USE_LIBSMI
+ (void)fprintf (stderr, "SMI-library: %s\n", smi_version_string);
+#endif
+}
+USES_APPLE_RST
+
+static void
+print_usage(void)
+{
+ print_version();
(void)fprintf(stderr,
-"Usage: %s [-aAbd" D_FLAG "efhH" I_FLAG J_FLAG "KlLnNOpqRStu" U_FLAG "vxX]" B_FLAG_USAGE " [ -c count ]\n", program_name);
+"Usage: %s [-aAbd" D_FLAG "efhH" I_FLAG J_FLAG "KlLnNOpqRStu" U_FLAG "vxX#]" B_FLAG_USAGE " [ -c count ]\n", program_name);
(void)fprintf(stderr,
"\t\t[ -C file_size ] [ -E algo:secret ] [ -F file ] [ -G seconds ]\n");
(void)fprintf(stderr,
"\t\t[ -Q in|out|inout ]\n");
#endif
(void)fprintf(stderr,
-"\t\t[ -r file ] [ -s snaplen ] [ -T type ] [ -V file ] [ -w file ]\n");
+"\t\t[ -r file ] [ -s snaplen ] ");
+#ifdef HAVE_PCAP_SET_TSTAMP_PRECISION
+ (void)fprintf(stderr, "[ --time-stamp-precision precision ]\n");
(void)fprintf(stderr,
-"\t\t[ -W filecount ] [ -y datalinktype ] [ -z command ]\n");
+"\t\t");
+#endif
+#ifdef HAVE_PCAP_SET_IMMEDIATE_MODE
+ (void)fprintf(stderr, "[ --immediate-mode ] ");
+#endif
+ (void)fprintf(stderr, "[ -T type ] [ --version ] [ -V file ]\n");
+ (void)fprintf(stderr,
+"\t\t[ -w file ] [ -W filecount ] [ -y datalinktype ] [ -z command ]\n");
(void)fprintf(stderr,
"\t\t[ -Z user ] [ expression ]\n");
- exit(1);
}