X-Git-Url: https://git.tcpdump.org/tcpdump/blobdiff_plain/25f672ea6f5f4706dd38cb26466cf74e2b7d9e8f..2fd7b16acb63b86d7291abb4c68c27e24bc50a85:/tcpdump.c diff --git a/tcpdump.c b/tcpdump.c index 4c56faf4..5e232197 100644 --- a/tcpdump.c +++ b/tcpdump.c @@ -38,7 +38,7 @@ #endif /* - * Some older versions of Mac OS X may ship pcap.h from libpcap 0.6 with a + * Some older versions of Mac OS X 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(). @@ -78,7 +78,9 @@ The Regents of the University of California. All rights reserved.\n"; #endif /* Capsicum-specific code requires macros from , which will fail * to compile if has already been included; including the headers - * in the opposite order works fine. + * in the opposite order works fine. For the most part anyway, because in + * FreeBSD declares bpf_dump() instead of . Thus + * interface.h takes care of it later to avoid a compiler warning. */ #ifdef HAVE_CAPSICUM #include @@ -155,12 +157,13 @@ The Regents of the University of California. All rights reserved.\n"; #include "netdissect.h" #include "interface.h" #include "addrtoname.h" -#include "machdep.h" #include "pcap-missing.h" #include "ascii_strcasecmp.h" #include "print.h" +#include "diag-control.h" + #include "fptype.h" #ifndef PATH_MAX @@ -238,10 +241,6 @@ static int infoprint; char *program_name; -#ifdef HAVE_CASPER -cap_channel_t *capdns; -#endif - /* Forwards */ static void (*setsignal (int sig, void (*func)(int)))(int); static void cleanup(int); @@ -563,8 +562,21 @@ show_remote_devices_and_exit(void) int i; if (pcap_findalldevs_ex(remote_interfaces_source, NULL, &devlist, - ebuf) < 0) + ebuf) < 0) { + if (strcmp(ebuf, "not supported") == 0) { + /* + * macOS 14's pcap_findalldevs_ex(), which is a + * stub that always returns -1 with an error + * message of "not supported". + * + * In this case, as we passed it an rpcap:// + * URL, treat that as meaning "remote capture + * not supported". + */ + error("Remote capture not supported"); + } error("%s", ebuf); + } for (i = 0, dev = devlist; dev != NULL; i++, dev = dev->next) { printf("%d.%s", i+1, dev->name); if (dev->description != NULL) @@ -691,6 +703,8 @@ show_remote_devices_and_exit(void) #define OPTION_TSTAMP_NANO 134 #define OPTION_FP_TYPE 135 #define OPTION_COUNT 136 +#define OPTION_PRINT_SAMPLING 137 +#define OPTION_LENGTHS 138 static const struct option longopts[] = { #if defined(HAVE_PCAP_CREATE) || defined(_WIN32) @@ -738,12 +752,14 @@ static const struct option longopts[] = { { "fp-type", no_argument, NULL, OPTION_FP_TYPE }, { "number", no_argument, NULL, '#' }, { "print", no_argument, NULL, OPTION_PRINT }, + { "print-sampling", required_argument, NULL, OPTION_PRINT_SAMPLING }, + { "lengths", no_argument, NULL, OPTION_LENGTHS }, { "version", no_argument, NULL, OPTION_VERSION }, { NULL, 0, NULL, 0 } }; #ifdef HAVE_PCAP_FINDALLDEVS_EX -#define LIST_REMOTE_INTERFACES_USAGE "[ --list-remote-interfaces remote-source ]" +#define LIST_REMOTE_INTERFACES_USAGE " [ --list-remote-interfaces remote-source ]" #else #define LIST_REMOTE_INTERFACES_USAGE #endif @@ -794,8 +810,8 @@ droproot(const char *username, const char *chroot_dir) } else error("Couldn't find user '%.32s'", username); #ifdef HAVE_LIBCAP_NG - /* We don't need CAP_SETUID, CAP_SETGID and CAP_SYS_CHROOT any more. */ -DIAG_OFF_CLANG(assign-enum) + /* We don't need CAP_SETUID, CAP_SETGID and CAP_SYS_CHROOT anymore. */ +DIAG_OFF_ASSIGN_ENUM capng_updatev( CAPNG_DROP, CAPNG_EFFECTIVE | CAPNG_PERMITTED, @@ -803,7 +819,7 @@ DIAG_OFF_CLANG(assign-enum) CAP_SETGID, CAP_SYS_CHROOT, -1); -DIAG_ON_CLANG(assign-enum) +DIAG_ON_ASSIGN_ENUM capng_apply(CAPNG_SELECT_BOTH); #endif /* HAVE_LIBCAP_NG */ @@ -831,6 +847,8 @@ MakeFilename(char *buffer, char *orig_name, int cnt, int max_chars) char *filename = malloc(PATH_MAX + 1); if (filename == NULL) error("%s: malloc", __func__); + if (strlen(orig_name) == 0) + error("an empty string is not a valid file name"); /* Process with strftime if Gflag is set. */ if (Gflag != 0) { @@ -842,9 +860,25 @@ MakeFilename(char *buffer, char *orig_name, int cnt, int max_chars) } /* There's no good way to detect an error in strftime since a return - * value of 0 isn't necessarily failure. + * value of 0 isn't necessarily failure; if orig_name is an empty + * string, the formatted string will be empty. + * + * However, the C90 standard says that, if there *is* a + * buffer overflow, the content of the buffer is undefined, + * so we must check for a buffer overflow. + * + * So we check above for an empty orig_name, and only call + * strftime() if it's non-empty, in which case the return + * value will only be 0 if the formatted date doesn't fit + * in the buffer. + * + * (We check above because, even if we don't use -G, we + * want a better error message than "tcpdump: : No such + * file or directory" for this case.) */ - strftime(filename, PATH_MAX, orig_name, local_tm); + if (strftime(filename, PATH_MAX, orig_name, local_tm) == 0) { + error("%s: strftime", __func__); + } } else { strncpy(filename, orig_name, PATH_MAX); } @@ -896,6 +930,7 @@ capdns_setup(void) if (cap_dns_type_limit(capdnsloc, types, 1) < 0) error("unable to limit access to system.dns service"); families[0] = AF_INET; + /* Casper is a feature of FreeBSD, which defines AF_INET6. */ families[1] = AF_INET6; if (cap_dns_family_limit(capdnsloc, families, 2) < 0) error("unable to limit access to system.dns service"); @@ -947,7 +982,7 @@ tstamp_precision_to_string(int precision) * 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. + * regular files notwithstanding. * * The Capsicum people decided that fine-grained control of descriptor * operations was required, so that you need to grant permission for @@ -963,7 +998,7 @@ tstamp_precision_to_string(int precision) * 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 + * 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 @@ -1248,6 +1283,18 @@ open_interface(const char *device, netdissect_options *ndo, char *ebuf) pflag ? 0 : PCAP_OPENFLAG_PROMISCUOUS, timeout, NULL, ebuf); if (pc == NULL) { + /* + * macOS 14's pcap_pcap_open(), which is a + * stub that always returns NULL with an error + * message of "not supported". + * + * In this case, as we passed it an rpcap:// + * URL, treat that as meaning "remote capture + * not supported". + */ + if (strcmp(ebuf, "not supported") == 0) + error("Remote capture not supported"); + /* * If this failed with "No such device" or "The system * cannot find the device specified", that means @@ -1417,7 +1464,7 @@ open_interface(const char *device, netdissect_options *ndo, char *ebuf) if (status != 0) error("%s: pcap_setdirection() failed: %s", device, pcap_geterr(pc)); - } + } #endif /* HAVE_PCAP_SETDIRECTION */ #else /* HAVE_PCAP_CREATE */ *ebuf = '\0'; @@ -1484,6 +1531,7 @@ main(int argc, char **argv) int yflag_dlt = -1; const char *yflag_dlt_name = NULL; int print = 0; + long Cflagmult; netdissect_options Ndo; netdissect_options *ndo = &Ndo; @@ -1519,14 +1567,11 @@ main(int argc, char **argv) #endif /* - * 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. + * An explicit tzset() call is usually not needed as it happens + * implicitly the first time we call localtime() or mktime(), + * but in some cases (sandboxing, chroot) this may be too late. */ - if (abort_on_misalignment(ebuf, sizeof(ebuf)) < 0) - error("%s", ebuf); + tzset(); while ( (op = getopt_long(argc, argv, SHORTOPTS, longopts, NULL)) != -1) @@ -1565,19 +1610,77 @@ main(int argc, char **argv) #else Cflag = strtol(optarg, &endp, 10); #endif - if (endp == optarg || *endp != '\0' || errno != 0 - || Cflag <= 0) + if (endp == optarg || errno != 0 || Cflag <= 0) error("invalid file size %s", optarg); + + if (*endp == '\0') { + /* + * There's nothing after the file size, + * so the size is in units of 1 MB + * (1,000,000 bytes). + */ + Cflagmult = 1000000; + } else { + /* + * There's something after the file + * size. + * + * If it's a single letter, then: + * + * if the letter is k or K, the size + * is in units of 1 KiB (1024 bytes); + * + * if the letter is m or M, the size + * is in units of 1 MiB (1,048,576 bytes); + * + * if the letter is g or G, the size + * is in units of 1 GiB (1,073,741,824 bytes). + * + * Otherwise, it's an error. + */ + switch (*endp) { + + case 'k': + case 'K': + Cflagmult = 1024; + break; + + case 'm': + case 'M': + Cflagmult = 1024*1024; + break; + + case 'g': + case 'G': + Cflagmult = 1024*1024*1024; + break; + + default: + error("invalid file size %s", optarg); + } + + /* + * OK, there was a letter that we treat + * as a units indication; was there + * anything after it? + */ + endp++; + if (*endp != '\0') { + /* Yes - error */ + error("invalid file size %s", optarg); + } + } + /* - * Will multiplying it by 1000000 overflow? + * Will multiplying it by multiplier overflow? */ #ifdef HAVE_PCAP_DUMP_FTELL64 - if (Cflag > INT64_T_CONSTANT(0x7fffffffffffffff) / 1000000) + if (Cflag > INT64_T_CONSTANT(0x7fffffffffffffff) / Cflagmult) #else - if (Cflag > LONG_MAX / 1000000) + if (Cflag > LONG_MAX / Cflagmult) #endif error("file size %s is too large", optarg); - Cflag *= 1000000; + Cflag *= Cflagmult; break; case 'd': @@ -1696,7 +1799,7 @@ main(int argc, char **argv) if (nd_load_smi_module(optarg, ebuf, sizeof(ebuf)) == -1) error("%s", ebuf); } else { - (void)fprintf(stderr, "%s: ignoring option `-m %s' ", + (void)fprintf(stderr, "%s: ignoring option '-m %s' ", program_name, optarg); (void)fprintf(stderr, "(no libsmi support)\n"); } @@ -1740,7 +1843,7 @@ main(int argc, char **argv) else if (ascii_strcasecmp(optarg, "inout") == 0) Qflag = PCAP_D_INOUT; else - error("unknown capture direction `%s'", optarg); + error("unknown capture direction '%s'", optarg); break; #endif /* HAVE_PCAP_SETDIRECTION */ @@ -1805,8 +1908,10 @@ main(int argc, char **argv) ndo->ndo_packettype = PT_SOMEIP; else if (ascii_strcasecmp(optarg, "domain") == 0) ndo->ndo_packettype = PT_DOMAIN; + else if (ascii_strcasecmp(optarg, "quic") == 0) + ndo->ndo_packettype = PT_QUIC; else - error("unknown packet type `%s'", optarg); + error("unknown packet type '%s'", optarg); break; case 'u': @@ -1876,6 +1981,10 @@ main(int argc, char **argv) ndo->ndo_packet_number = 1; break; + case OPTION_LENGTHS: + ndo->ndo_lengths = 1; + break; + case OPTION_VERSION: print_version(stdout); exit_tcpdump(S_SUCCESS); @@ -1899,6 +2008,14 @@ main(int argc, char **argv) print = 1; break; + case OPTION_PRINT_SAMPLING: + print = 1; + ++ndo->ndo_Sflag; + ndo->ndo_print_sampling = atoi(optarg); + if (ndo->ndo_print_sampling <= 0) + error("invalid print sampling %s", optarg); + break; + #ifdef HAVE_PCAP_SET_TSTAMP_PRECISION case OPTION_TSTAMP_MICRO: ndo->ndo_tstamp_precision = PCAP_TSTAMP_PRECISION_MICRO; @@ -1939,14 +2056,6 @@ main(int argc, char **argv) show_remote_devices_and_exit(); #endif -#if defined(DLT_LINUX_SLL2) && defined(HAVE_PCAP_SET_DATALINK) -/* Set default linktype DLT_LINUX_SLL2 when capturing on the "any" device */ - if (device != NULL && - strncmp (device, "any", strlen("any")) == 0 - && yflag_dlt == -1) - yflag_dlt = DLT_LINUX_SLL2; -#endif - switch (ndo->ndo_tflag) { case 0: /* Default */ @@ -1996,6 +2105,8 @@ main(int argc, char **argv) /* Run with '-Z root' to restore old behaviour */ if (!username) username = WITH_USER; + else if (strcmp(username, "root") == 0) + username = NULL; } #endif @@ -2199,6 +2310,24 @@ main(int argc, char **argv) pcap_datalink_val_to_name(yflag_dlt)); (void)fflush(stderr); } +#if defined(DLT_LINUX_SLL2) && defined(HAVE_PCAP_SET_DATALINK) + else { + /* + * Attempt to set default linktype to + * DLT_LINUX_SLL2 when capturing on the + * "any" device. + * + * If the attempt fails, just quietly drive + * on; this may be a non-Linux "any" device + * that doesn't support DLT_LINUX_SLL2. + */ + if (strcmp(device, "any") == 0) { +DIAG_OFF_WARN_UNUSED_RESULT + (void) pcap_set_datalink(pd, DLT_LINUX_SLL2); +DIAG_ON_WARN_UNUSED_RESULT + } + } +#endif i = pcap_snapshot(pd); if (ndo->ndo_snaplen < i) { if (ndo->ndo_snaplen != 0) @@ -2263,7 +2392,7 @@ main(int argc, char **argv) * devices, and can't just give users that permission, * you'd make tcpdump set-UID or set-GID). * - * Tcpdump doesn't necessarily write only to one savefile; + * tcpdump doesn't necessarily write only to one savefile; * the general only way to allow a -Z instance to write to * savefiles as the user under whose UID it's run, rather * than as the user specified with -Z, would thus be to switch @@ -2278,33 +2407,33 @@ main(int argc, char **argv) /* Initialize capng */ capng_clear(CAPNG_SELECT_BOTH); if (username) { -DIAG_OFF_CLANG(assign-enum) +DIAG_OFF_ASSIGN_ENUM capng_updatev( CAPNG_ADD, CAPNG_PERMITTED | CAPNG_EFFECTIVE, CAP_SETUID, CAP_SETGID, -1); -DIAG_ON_CLANG(assign-enum) +DIAG_ON_ASSIGN_ENUM } if (chroot_dir) { -DIAG_OFF_CLANG(assign-enum) +DIAG_OFF_ASSIGN_ENUM capng_update( CAPNG_ADD, CAPNG_PERMITTED | CAPNG_EFFECTIVE, CAP_SYS_CHROOT ); -DIAG_ON_CLANG(assign-enum) +DIAG_ON_ASSIGN_ENUM } if (WFileName) { -DIAG_OFF_CLANG(assign-enum) +DIAG_OFF_ASSIGN_ENUM capng_update( CAPNG_ADD, CAPNG_PERMITTED | CAPNG_EFFECTIVE, CAP_DAC_OVERRIDE ); -DIAG_ON_CLANG(assign-enum) +DIAG_ON_ASSIGN_ENUM } capng_apply(CAPNG_SELECT_BOTH); #endif /* HAVE_LIBCAP_NG */ @@ -2370,17 +2499,44 @@ DIAG_ON_CLANG(assign-enum) #endif if (Cflag != 0 || Gflag != 0) { #ifdef HAVE_CAPSICUM - dumpinfo.WFileName = strdup(basename(WFileName)); + /* + * basename() and dirname() may modify their input buffer + * and they do since FreeBSD 12.0, but they didn't before. + * Hence use the return value only, but always assume the + * input buffer has been modified and would need to be + * reset before the next use. + */ + char *WFileName_copy; + + if ((WFileName_copy = strdup(WFileName)) == NULL) { + error("Unable to allocate memory for file %s", + WFileName); + } + DIAG_OFF_C11_EXTENSIONS + dumpinfo.WFileName = strdup(basename(WFileName_copy)); + DIAG_ON_C11_EXTENSIONS if (dumpinfo.WFileName == NULL) { error("Unable to allocate memory for file %s", WFileName); } - dumpinfo.dirfd = open(dirname(WFileName), + free(WFileName_copy); + + if ((WFileName_copy = strdup(WFileName)) == NULL) { + error("Unable to allocate memory for file %s", + WFileName); + } + DIAG_OFF_C11_EXTENSIONS + char *WFileName_dirname = dirname(WFileName_copy); + DIAG_ON_C11_EXTENSIONS + dumpinfo.dirfd = open(WFileName_dirname, O_DIRECTORY | O_RDONLY); if (dumpinfo.dirfd < 0) { error("unable to open directory %s", - dirname(WFileName)); + WFileName_dirname); } + free(WFileName_dirname); + free(WFileName_copy); + cap_rights_init(&rights, CAP_CREATE, CAP_FCNTL, CAP_FTRUNCATE, CAP_LOOKUP, CAP_SEEK, CAP_WRITE); if (cap_rights_limit(dumpinfo.dirfd, &rights) < 0 && @@ -2504,6 +2660,9 @@ DIAG_ON_CLANG(assign-enum) #else cansandbox = (cansandbox && ndo->ndo_nflag); #endif /* HAVE_CASPER */ + cansandbox = (cansandbox && (pcap_fileno(pd) != -1 || + RFileName != NULL)); + if (cansandbox && cap_enter() < 0 && errno != ENOSYS) error("unable to enter the capability mode"); #endif /* HAVE_CAPSICUM */ @@ -2595,6 +2754,8 @@ DIAG_ON_CLANG(assign-enum) */ dlt = new_dlt; ndo->ndo_if_printer = get_if_printer(dlt); + /* Free the old filter */ + pcap_freecode(&fcode); if (pcap_compile(pd, &fcode, cmdbuf, Oflag, netmask) < 0) error("%s", pcap_geterr(pd)); } @@ -2629,7 +2790,7 @@ DIAG_ON_CLANG(assign-enum) free(cmdbuf); pcap_freecode(&fcode); - exit_tcpdump(status == -1 ? 1 : 0); + exit_tcpdump(status == -1 ? S_ERR_HOST_PROGRAM : S_SUCCESS); } /* @@ -2645,7 +2806,14 @@ static void memset(&new, 0, sizeof(new)); new.sa_handler = func; - if (sig == SIGCHLD) + if ((sig == SIGCHLD) +# ifdef SIGNAL_REQ_INFO + || (sig == SIGNAL_REQ_INFO) +# endif +# ifdef SIGNAL_FLUSH_PCAP + || (sig == SIGNAL_FLUSH_PCAP) +# endif + ) new.sa_flags = SA_RESTART; if (sigaction(sig, &new, &old) < 0) return (SIG_ERR); @@ -2710,7 +2878,7 @@ cleanup(int signo _U_) static void child_cleanup(int signo _U_) { - wait(NULL); + while (waitpid(-1, NULL, WNOHANG) >= 0); } #endif /* HAVE_FORK && HAVE_VFORK */ @@ -3115,7 +3283,7 @@ static void verbose_stats_dump(int sig _U_) } #endif /* _WIN32 */ -USES_APPLE_DEPRECATED_API +DIAG_OFF_DEPRECATION static void print_version(FILE *f) { @@ -3153,7 +3321,7 @@ print_version(FILE *f) # endif #endif /* __SANITIZE_ADDRESS__ or __has_feature */ } -USES_APPLE_RST +DIAG_ON_DEPRECATION static void print_usage(FILE *f) @@ -3167,16 +3335,18 @@ print_usage(FILE *f) "\t\t[ -i interface ]" IMMEDIATE_MODE_USAGE j_FLAG_USAGE "\n"); #ifdef HAVE_PCAP_FINDALLDEVS_EX (void)fprintf(f, -"\t\t" LIST_REMOTE_INTERFACES_USAGE "\n"); +"\t\t[ --lengths ]" LIST_REMOTE_INTERFACES_USAGE "\n"); #endif #ifdef USE_LIBSMI (void)fprintf(f, "\t\t" m_FLAG_USAGE "\n"); #endif (void)fprintf(f, -"\t\t[ -M secret ] [ --number ] [ --print ]" Q_FLAG_USAGE "\n"); +"\t\t[ -M secret ] [ --number ] [ --print ]\n"); + (void)fprintf(f, +"\t\t[ --print-sampling nth ]" Q_FLAG_USAGE " [ -r file ]\n"); (void)fprintf(f, -"\t\t[ -r file ] [ -s snaplen ] [ -T type ] [ --version ]\n"); +"\t\t[ -s snaplen ] [ -T type ] [ --version ]\n"); (void)fprintf(f, "\t\t[ -V file ] [ -w file ] [ -W filecount ] [ -y datalinktype ]\n"); #ifdef HAVE_PCAP_SET_TSTAMP_PRECISION