X-Git-Url: https://git.tcpdump.org/tcpdump/blobdiff_plain/768c379e643e28184fe267700859a698cc90faf8..7cd7ddbc9ab8b3796ec6f2622c263fcf319b7fc5:/tcpdump.c diff --git a/tcpdump.c b/tcpdump.c index 2f120396..98ec2d5d 100644 --- a/tcpdump.c +++ b/tcpdump.c @@ -74,6 +74,10 @@ extern int SIZE_BUF; #include #endif /* WIN32 */ +/* capabilities convinience library */ +#ifdef HAVE_CAP_NG_H +#include +#endif /* HAVE_CAP_NG_H */ #include "netdissect.h" #include "interface.h" @@ -87,11 +91,20 @@ extern int SIZE_BUF; #define NAME_MAX 255 #endif +#ifdef SIGINFO +#define SIGNAL_REQ_INFO SIGINFO +#elif SIGUSR1 +#define SIGNAL_REQ_INFO SIGUSR1 +#endif + netdissect_options Gndo; netdissect_options *gndo = &Gndo; static int dflag; /* print filter code */ static int Lflag; /* list available data link types and exit */ +#ifdef HAVE_PCAP_SET_TSTAMP_TYPE +static int Jflag; /* list available time stamp types */ +#endif static char *zflag = NULL; /* compress each savefile using a specified command (like gzip or bzip2) */ static int infodelay; @@ -112,10 +125,11 @@ static void ndo_default_print(netdissect_options *, const u_char *, u_int); static void dump_packet_and_trunc(u_char *, const struct pcap_pkthdr *, const u_char *); static void dump_packet(u_char *, const struct pcap_pkthdr *, const u_char *); static void droproot(const char *, const char *); -static void ndo_error(netdissect_options *ndo, const char *fmt, ...); +static void ndo_error(netdissect_options *ndo, const char *fmt, ...) + __attribute__ ((noreturn, format (printf, 2, 3))); static void ndo_warning(netdissect_options *ndo, const char *fmt, ...); -#ifdef SIGINFO +#ifdef SIGNAL_REQ_INFO RETSIGTYPE requestinfo(int); #endif @@ -130,10 +144,6 @@ RETSIGTYPE requestinfo(int); static void info(int); static u_int packets_captured; -typedef u_int (*if_printer)(const struct pcap_pkthdr *, const u_char *); -typedef u_int (*if_ndo_printer)(struct netdissect_options *ndo, - const struct pcap_pkthdr *, const u_char *); - struct printer { if_printer f; int type; @@ -151,7 +161,6 @@ static struct printer printers[] = { #ifdef DLT_ARCNET_LINUX { arcnet_linux_if_print, DLT_ARCNET_LINUX }, #endif - { ether_if_print, DLT_EN10MB }, { token_if_print, DLT_IEEE802 }, #ifdef DLT_LANE8023 { lane_if_print, DLT_LANE8023 }, @@ -303,16 +312,29 @@ static struct printer printers[] = { }; static struct ndo_printer ndo_printers[] = { + { ether_if_print, DLT_EN10MB }, #ifdef DLT_IPNET { ipnet_if_print, DLT_IPNET }, #endif #ifdef DLT_IEEE802_15_4 { ieee802_15_4_if_print, DLT_IEEE802_15_4 }, +#endif +#ifdef DLT_IEEE802_15_4_NOFCS + { ieee802_15_4_if_print, DLT_IEEE802_15_4_NOFCS }, +#endif +#ifdef DLT_PPI + { ppi_if_print, DLT_PPI }, +#endif +#ifdef DLT_NETANALYZER + { netanalyzer_if_print, DLT_NETANALYZER }, +#endif +#ifdef DLT_NETANALYZER_TRANSPARENT + { netanalyzer_transparent_if_print, DLT_NETANALYZER_TRANSPARENT }, #endif { NULL, 0 }, }; -static if_printer +if_printer lookup_printer(int type) { struct printer *p; @@ -325,7 +347,7 @@ lookup_printer(int type) /* NOTREACHED */ } -static if_ndo_printer +if_ndo_printer lookup_ndo_printer(int type) { struct ndo_printer *p; @@ -362,6 +384,40 @@ struct dump_info { pcap_dumper_t *p; }; +#ifdef HAVE_PCAP_SET_TSTAMP_TYPE +static void +show_tstamp_types_and_exit(const char *device, pcap_t *pd) +{ + int n_tstamp_types; + int *tstamp_types = 0; + const char *tstamp_type_name; + int i; + + n_tstamp_types = pcap_list_tstamp_types(pd, &tstamp_types); + if (n_tstamp_types < 0) + error("%s", pcap_geterr(pd)); + + if (n_tstamp_types == 0) { + fprintf(stderr, "Time stamp type cannot be set for %s\n", + device); + exit(0); + } + fprintf(stderr, "Time stamp types for %s (use option -j to set):\n", + device); + for (i = 0; i < n_tstamp_types; i++) { + tstamp_type_name = pcap_tstamp_type_val_to_name(tstamp_types[i]); + if (tstamp_type_name != NULL) { + (void) fprintf(stderr, " %s (%s)\n", tstamp_type_name, + pcap_tstamp_type_val_to_description(tstamp_types[i])); + } else { + (void) fprintf(stderr, " %d\n", tstamp_types[i]); + } + } + pcap_free_tstamp_types(tstamp_types); + exit(0); +} +#endif + static void show_dlts_and_exit(const char *device, pcap_t *pd) { @@ -409,7 +465,9 @@ show_dlts_and_exit(const char *device, pcap_t *pd) dlts[n_dlts]); } } - free(dlts); +#ifdef HAVE_PCAP_FREE_DATALINKS + pcap_free_datalinks(dlts); +#endif exit(0); } @@ -431,6 +489,16 @@ show_dlts_and_exit(const char *device, pcap_t *pd) #define I_FLAG #endif /* HAVE_PCAP_CREATE */ +#ifdef HAVE_PCAP_SET_TSTAMP_TYPE +#define j_FLAG "j:" +#define j_FLAG_USAGE " [ -j tstamptype ]" +#define J_FLAG "J" +#else /* PCAP_ERROR_TSTAMP_TYPE_NOTSUP */ +#define j_FLAG +#define j_FLAG_USAGE +#define J_FLAG +#endif /* PCAP_ERROR_TSTAMP_TYPE_NOTSUP */ + #ifdef HAVE_PCAP_FINDALLDEVS #ifndef HAVE_PCAP_IF_T #undef HAVE_PCAP_FINDALLDEVS @@ -470,6 +538,19 @@ droproot(const char *username, const char *chroot_dir) exit(1); } } +#ifdef HAVE_CAP_NG_H + int ret = capng_change_id(pw->pw_uid, pw->pw_gid, CAPNG_NO_FLAG); + if (ret < 0) { + printf("error : ret %d\n", ret); + } + /* 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) { fprintf(stderr, "tcpdump: Couldn't change to '%.32s' uid=%lu gid=%lu: %s\n", @@ -479,6 +560,7 @@ droproot(const char *username, const char *chroot_dir) pcap_strerror(errno)); exit(1); } +#endif /* HAVE_CAP_NG_H */ } else { fprintf(stderr, "tcpdump: Couldn't find user '%.32s'\n", @@ -507,6 +589,8 @@ static void MakeFilename(char *buffer, char *orig_name, int cnt, int max_chars) { char *filename = malloc(NAME_MAX + 1); + if (filename == NULL) + error("Makefilename: malloc); /* Process with strftime if Gflag is set. */ if (Gflag != 0) { @@ -575,6 +659,7 @@ main(int argc, char **argv) if(wsockinit() != 0) return 1; #endif /* WIN32 */ + jflag=-1; /* not set */ gndo->ndo_Oflag=1; gndo->ndo_Rflag=1; gndo->ndo_dlt=-1; @@ -601,9 +686,8 @@ main(int argc, char **argv) smiInit("tcpdump"); #endif - opterr = 0; while ( - (op = getopt(argc, argv, "aAb" B_FLAG "c:C:d" D_FLAG "eE:fF:G:hi:" I_FLAG "KlLm:M:nNOpqr:Rs:StT:u" U_FLAG "vw:W:xXy:Yz:Z:")) != -1) + (op = getopt(argc, argv, "aAb" B_FLAG "c:C:d" D_FLAG "eE:fF:G:hHi:" I_FLAG j_FLAG J_FLAG "KlLm:M:nNOpqr:Rs:StT:u" U_FLAG "vw:W:xXy:Yz:Z:")) != -1) switch (op) { case 'a': @@ -697,7 +781,11 @@ main(int argc, char **argv) break; case 'h': - ++hflag; + usage(); + break; + + case 'H': + ++Hflag; break; case 'i': @@ -747,6 +835,18 @@ main(int argc, char **argv) break; #endif /* HAVE_PCAP_CREATE */ +#ifdef HAVE_PCAP_SET_TSTAMP_TYPE + case 'j': + jflag = pcap_tstamp_type_name_to_val(optarg); + if (jflag < 0) + error("invalid time stamp type %s", optarg); + break; + + case 'J': + Jflag++; + break; +#endif + case 'l': #ifdef WIN32 /* @@ -861,6 +961,10 @@ main(int argc, char **argv) packettype = PT_TFTP; else if (strcasecmp(optarg, "aodv") == 0) packettype = PT_AODV; + else if (strcasecmp(optarg, "carp") == 0) + packettype = PT_CARP; + else if (strcasecmp(optarg, "radius") == 0) + packettype = PT_RADIUS; else error("unknown packet type `%s'", optarg); break; @@ -1039,6 +1143,10 @@ main(int argc, char **argv) pd = pcap_create(device, ebuf); if (pd == NULL) error("%s", ebuf); +#ifdef HAVE_PCAP_SET_TSTAMP_TYPE + if (Jflag) + show_tstamp_types_and_exit(device, pd); +#endif /* * Is this an interface that supports monitor mode? */ @@ -1048,16 +1156,16 @@ main(int argc, char **argv) supports_monitor_mode = 0; status = pcap_set_snaplen(pd, snaplen); if (status != 0) - error("%s: pcap_set_snaplen failed: %s", + error("%s: Can't set snapshot length: %s", device, pcap_statustostr(status)); status = pcap_set_promisc(pd, !pflag); if (status != 0) - error("%s: pcap_set_promisc failed: %s", + error("%s: Can't set promiscuous mode: %s", device, pcap_statustostr(status)); if (Iflag) { status = pcap_set_rfmon(pd, 1); if (status != 0) - error("%s: pcap_set_rfmon failed: %s", + error("%s: Can't set monitor mode: %s", device, pcap_statustostr(status)); } status = pcap_set_timeout(pd, 1000); @@ -1067,9 +1175,17 @@ main(int argc, char **argv) if (Bflag != 0) { status = pcap_set_buffer_size(pd, Bflag); if (status != 0) - error("%s: pcap_set_buffer_size failed: %s", + error("%s: Can't set buffer size: %s", device, pcap_statustostr(status)); } +#ifdef HAVE_PCAP_SET_TSTAMP_TYPE + if (jflag != -1) { + status = pcap_set_tstamp_type(pd, jflag); + if (status < 0) + error("%s: Can't set time stamp type: %s", + device, pcap_statustostr(status)); + } +#endif status = pcap_activate(pd); if (status < 0) { /* @@ -1175,14 +1291,62 @@ main(int argc, char **argv) (void)setsignal(SIGPIPE, cleanup); (void)setsignal(SIGTERM, cleanup); (void)setsignal(SIGINT, cleanup); - (void)setsignal(SIGCHLD, child_cleanup); #endif /* WIN32 */ +#if defined(HAVE_FORK) || defined(HAVE_VFORK) + (void)setsignal(SIGCHLD, child_cleanup); +#endif /* Cooperate with nohup(1) */ #ifndef WIN32 if ((oldhandler = setsignal(SIGHUP, cleanup)) != SIG_DFL) (void)setsignal(SIGHUP, oldhandler); #endif /* 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, + * to allow tcpdump to be run in a special restricted + * account (if you just want to allow users to open capture + * 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; + * 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 + * to the original user ID before opening a capture file and + * then switch back to the -Z user ID after opening the savefile. + * Switching to the -Z user ID only after opening the first + * 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 (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); + } + } +#endif /* HAVE_CAP_NG_H */ + + if (getuid() == 0 || geteuid() == 0) { + if (username || chroot_dir) + droproot(username, chroot_dir); + + } +#endif /* WIN32 */ + if (pcap_setfilter(pd, &fcode) < 0) error("%s", pcap_geterr(pd)); if (WFileName) { @@ -1200,6 +1364,10 @@ main(int argc, char **argv) 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 if (p == NULL) error("%s", pcap_geterr(pd)); if (Cflag != 0 || Gflag != 0) { @@ -1236,23 +1404,14 @@ main(int argc, char **argv) callback = print_packet; pcap_userdata = (u_char *)&printinfo; } -#ifndef WIN32 - /* - * We cannot do this earlier, because we want to be able to open - * the file (if done) for writing before giving up permissions. - */ - if (getuid() == 0 || geteuid() == 0) { - if (username || chroot_dir) - droproot(username, chroot_dir); - } -#endif /* WIN32 */ -#ifdef SIGINFO + +#ifdef SIGNAL_REQ_INFO /* * We can't get statistics when reading from a file rather * than capturing from a device. */ if (RFileName == NULL) - (void)setsignal(SIGINFO, requestinfo); + (void)setsignal(SIGNAL_REQ_INFO, requestinfo); #endif if (vflag > 0 && WFileName) { @@ -1374,13 +1533,13 @@ cleanup(int signo _U_) On windows, we do not use a fork, so we do not care less about waiting a child processes to die */ -#ifndef WIN32 +#if defined(HAVE_FORK) || defined(HAVE_VFORK) static RETSIGTYPE child_cleanup(int signo _U_) { wait(NULL); } -#endif /* WIN32 */ +#endif /* HAVE_FORK && HAVE_VFORK */ static void info(register int verbose) @@ -1401,34 +1560,41 @@ info(register int verbose) if (!verbose) fprintf(stderr, "%s: ", program_name); - (void)fprintf(stderr, "%u packets captured", packets_captured); + (void)fprintf(stderr, "%u packet%s captured", packets_captured, + PLURAL_SUFFIX(packets_captured)); if (!verbose) fputs(", ", stderr); else putc('\n', stderr); - (void)fprintf(stderr, "%u packets received by filter", stat.ps_recv); + (void)fprintf(stderr, "%u packet%s received by filter", stat.ps_recv, + PLURAL_SUFFIX(stat.ps_recv)); if (!verbose) fputs(", ", stderr); else putc('\n', stderr); - (void)fprintf(stderr, "%u packets dropped by kernel", stat.ps_drop); + (void)fprintf(stderr, "%u packet%s dropped by kernel", stat.ps_drop, + PLURAL_SUFFIX(stat.ps_drop)); if (stat.ps_ifdrop != 0) { if (!verbose) fputs(", ", stderr); else putc('\n', stderr); - (void)fprintf(stderr, "%u packets dropped by interface\n", - stat.ps_ifdrop); + (void)fprintf(stderr, "%u packet%s dropped by interface\n", + stat.ps_ifdrop, PLURAL_SUFFIX(stat.ps_ifdrop)); } else putc('\n', stderr); infoprint = 0; } -#ifndef WIN32 +#if defined(HAVE_FORK) || defined(HAVE_VFORK) static void compress_savefile(const char *filename) { +# ifdef HAVE_FORK if (fork()) +# else + if (vfork()) +# endif return; /* * Set to lowest priority so that this doesn't disturb the capture @@ -1444,15 +1610,20 @@ compress_savefile(const char *filename) zflag, filename, strerror(errno)); +# ifdef HAVE_FORK + exit(1); +# else + _exit(1); +# endif } -#else /* WIN32 */ +#else /* HAVE_FORK && HAVE_VFORK */ static void compress_savefile(const char *filename) { fprintf(stderr, - "compress_savefile failed. Functionality not implemented under windows\n"); + "compress_savefile failed. Functionality not implemented under your system\n"); } -#endif /* WIN32 */ +#endif /* HAVE_FORK && HAVE_VFORK */ static void dump_packet_and_trunc(u_char *user, const struct pcap_pkthdr *h, const u_char *sp) @@ -1528,7 +1699,15 @@ dump_packet_and_trunc(u_char *user, const struct pcap_pkthdr *h, const u_char *s else MakeFilename(dump_info->CurrentFileName, dump_info->WFileName, 0, 0); +#ifdef HAVE_CAP_NG_H + capng_update(CAPNG_ADD, CAPNG_EFFECTIVE, CAP_DAC_OVERRIDE); + capng_apply(CAPNG_EFFECTIVE); +#endif /* HAVE_CAP_NG_H */ dump_info->p = pcap_dump_open(dump_info->pd, dump_info->CurrentFileName); +#ifdef HAVE_CAP_NG_H + capng_update(CAPNG_DROP, CAPNG_EFFECTIVE, CAP_DAC_OVERRIDE); + capng_apply(CAPNG_EFFECTIVE); +#endif /* HAVE_CAP_NG_H */ if (dump_info->p == NULL) error("%s", pcap_geterr(pd)); } @@ -1729,7 +1908,7 @@ default_print(const u_char *bp, u_int length) ndo_default_print(gndo, bp, length); } -#ifdef SIGINFO +#ifdef SIGNAL_REQ_INFO RETSIGTYPE requestinfo(int signo _U_) { if (infodelay) @@ -1791,17 +1970,17 @@ usage(void) #endif /* WIN32 */ #endif /* HAVE_PCAP_LIB_VERSION */ (void)fprintf(stderr, -"Usage: %s [-aAbd" D_FLAG "ef" I_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[ -i interface ] [ -M secret ] [ -r file ]\n"); +"\t\t[ -i interface ]" j_FLAG_USAGE " [ -M secret ]\n"); (void)fprintf(stderr, -"\t\t[ -s snaplen ] [ -T type ] [ -w file ] [ -W filecount ]\n"); +"\t\t[ -r file ] [ -s snaplen ] [ -T type ] [ -w file ]\n"); (void)fprintf(stderr, -"\t\t[ -y datalinktype ] [ -z command ] [ -Z user ]\n"); +"\t\t[ -W filecount ] [ -y datalinktype ] [ -z command ]\n"); (void)fprintf(stderr, -"\t\t[ expression ]\n"); +"\t\t[ -Z user ] [ expression ]\n"); exit(1); }