ongrep

A cleaned up fork of ngrep for OpenBSD
git clone git://git.sgregoratto.me/ongrep
Log | Files | Refs | README | LICENSE

commit e9f2f7e63daa9c3d9a0502a707dfed959aa4f6e3
parent b04abb4f48b81b5ee3b62ce6b86ae5f658898f7e
Author: Stephen Gregoratto <dev@sgregoratto.me>
Date:   Wed, 17 Jun 2020 13:05:30 +1000

Overhaul the bpf filter setup stage, unveil early

The original code was sketchy and didn't do proper error checking!
I've rewritten most of it to rely on strlcat/cpy instead.
This kills the get_filter_from_string() function in the process.

Also, I realised that none of the functions were opening any new files,
so we can do the final call to unveil early.

Diffstat:
Mngrep.c | 184++++++++++++++++++++++++++++++++++++++++++-------------------------------------
Mngrep.h | 13+++++--------
2 files changed, 102 insertions(+), 95 deletions(-)

diff --git a/ngrep.c b/ngrep.c @@ -5,10 +5,12 @@ * Please refer to the LICENSE file for more information. * */ -#include <sys/types.h> -#include <sys/socket.h> #include <sys/ioctl.h> +#include <sys/limits.h> +#include <sys/socket.h> +#include <sys/stat.h> #include <sys/time.h> +#include <sys/types.h> #include <net/if.h> #include <net/ethertypes.h> @@ -246,6 +248,11 @@ main(int argc, char **argv) } } + if (unveil(NULL, NULL) == -1) { + warn("unveil"); + clean_exit(2); + } + if (show_hex && dump_func != &dump_formatted) { warnx("-x is incompatible with -W"); usage(); @@ -257,31 +264,21 @@ main(int argc, char **argv) if (setup_pcap_source()) clean_exit(2); - if (unveil(NULL, NULL) == -1) { - warn("unveil"); + /* + * XXX: Originally, the filter would be setup again without the vlan + * hack if the first one failed. Not sure if we need to do this. + */ + if (setup_bpf_filter(argv) == -1) { + warnx("cannot compile filter: %s", pcap_geterr(pd)); clean_exit(2); } - /* Setup BPF filter */ - if (setup_bpf_filter(argv)) { - include_vlan = 0; - if (filter) { - free(filter); - filter = NULL; - } - if (setup_bpf_filter(argv)) { - warnx("pcap: %s", pcap_geterr(pd)); - clean_exit(2); - } - } - drop_privs(); - if (filter) { - if (quiet < 2) - printf("filter: %s\n", filter); - free(filter); - } + if (quiet < 2) + printf("filter: %s\n", filter); + free(filter); + filter = NULL; /* Setup matcher */ if (match_data) { @@ -389,7 +386,6 @@ setup_pcap_source(void) pcap_datalink(pd)); return 1; } - return 0; } @@ -397,20 +393,17 @@ int setup_bpf_filter(char **argv) { if (filter_file) { - char buf[1024] = {0}; - FILE *f = fopen(filter_file, "r"); - - if (!f || !fgets(buf, sizeof(buf) - 1, f)) { - warn("cannot read filter from %s", filter_file); - usage(); - } - fclose(f); - - filter = get_filter_from_string(buf); - - if (pcap_compile(pd, &pcapfilter, filter, 0, mask.s_addr)) - return 1; + filter = get_filter_from_file(); + if (pcap_compile(pd, &pcapfilter, filter, 0, mask.s_addr) == -1) + return -1; } else if (argv[optind]) { + /* + * XXX: Find a smarter way of doing this that doesn't eat memory. + * + * If the filter doesn't compile the first time, + * we assume that there is no match expression and include the + * previous argument in the filter. + */ filter = get_filter_from_argv(&argv[optind]); if (pcap_compile(pd, &pcapfilter, filter, 0, mask.s_addr)) { @@ -418,19 +411,22 @@ setup_bpf_filter(char **argv) filter = get_filter_from_argv(&argv[optind - 1]); if (pcap_compile(pd, &pcapfilter, filter, 0, mask.s_addr)) - return 1; + return -1; match_data = NULL; } } else { - filter = include_vlan ? strdup(BPF_TEMPLATE_IP_VLAN) : strdup(BPF_TEMPLATE_IP); + filter = include_vlan ? + strdup(BPF_TEMPLATE_IP_VLAN) : strdup(BPF_TEMPLATE_IP); + if (filter == NULL) + err(2, "strdup"); if (pcap_compile(pd, &pcapfilter, filter, 0, mask.s_addr)) - return 1; + return -1; } if (pcap_setfilter(pd, &pcapfilter)) - return 1; + return -1; return 0; } @@ -958,76 +954,90 @@ dump_formatted(unsigned char *data, uint32_t len, uint16_t mindex, } char * -get_filter_from_string(char *str) +get_filter_from_file(void) { + char *fstr; + size_t size, prefix; const char *template; - char *mine, *s; - uint32_t len; - - template = include_vlan ? - BPF_TEMPLATE_USERSPEC_IP_VLAN : - BPF_TEMPLATE_USERSPEC_IP; - - if (!str || !*str) - return NULL; + int fd; + struct stat st; + ssize_t flen; - len = (uint32_t) strlen(str); + /* filter = prefix + " and ( " + file + " )" */ + template = include_vlan ? BPF_TEMPLATE_IP_VLAN : BPF_TEMPLATE_IP; + prefix = strlen(template) + 5; - for (s = str; *s; s++) - if (*s == '\r' || *s == '\n') - *s = ' '; - - if (!(mine = (char *) malloc(len + strlen(template) + 1))) - return NULL; - - memset(mine, 0, len + strlen(template) + 1); + if ((fd = open(filter_file, O_RDONLY)) == -1) { + warn("cannot open %s", filter_file); + clean_exit(2); + } + if (fstat(fd, &st) == -1) { + warn("cannot stat %s", filter_file); + clean_exit(2); + } + if (st.st_size >= SSIZE_MAX) { + warn("file too long: %s", filter_file); + clean_exit(2); + } - sprintf(mine, template, str); + size = prefix + 8 + st.st_size + 2; + if (size >= SIZE_MAX) + warnx("fstr too long: %s", filter_file); + if ((fstr = malloc(size)) == NULL) + err(1, "malloc"); + + (void)strlcpy(fstr, template, size); + (void)strlcat(fstr, " and ( ", size); + flen = read(fd, fstr + prefix, st.st_size); + if (flen == -1) { + warn("read %s", filter_file); + clean_exit(2); + } else if (flen != st.st_size) { + warn("short read of %s: expected %lld, got %zu", filter_file, st.st_size, flen); + clean_exit(2); + } + (void)strlcat(fstr, " )", size); - return mine; + return fstr; } char * get_filter_from_argv(char **argv) { + char *fstr; + size_t size; + char **arg = argv; const char *template; - char **arg = argv, *theirs, *mine; - char *from, *to; - uint32_t len = 0; - template = include_vlan ? - BPF_TEMPLATE_USERSPEC_IP_VLAN : - BPF_TEMPLATE_USERSPEC_IP; + /* filter = prefix + " and ( " + args + " )" */ + template = include_vlan ? BPF_TEMPLATE_IP_VLAN : BPF_TEMPLATE_IP; + size = strlen(template) + 8 + 2; - if (!*arg) + if (arg == NULL) return NULL; - while (*arg) - len += (uint32_t) strlen(*arg++) + 1; + for (; *arg != NULL; arg++) + size += strlen(*arg) + 1; - if (!(theirs = (char *) malloc(len + 1)) || - !(mine = (char *) malloc(len + strlen(template) + 1))) - return NULL; - - memset(theirs, 0, len + 1); - memset(mine, 0, len + strlen(template) + 1); - - arg = argv; - to = theirs; - - while ((from = *arg++)) { - while ((*to++ = *from++)); - *(to - 1) = ' '; + if (size >= SIZE_MAX) { + warnx("filter too long"); + clean_exit(2); } - sprintf(mine, template, theirs); + if ((fstr = malloc(size)) == NULL) + err(2, "malloc"); - free(theirs); + (void)strlcpy(fstr, template, size); + (void)strlcat(fstr, " and ( ", size); + for (arg = argv; *arg != NULL; arg++) { + (void)strlcat(fstr, *arg, size); + (void)strlcat(fstr, " ", size); + } + (void)strlcat(fstr, ")", size); - return mine; + return fstr; } - uint8_t strishex(char *str) { diff --git a/ngrep.h b/ngrep.h @@ -48,14 +48,11 @@ * targeting IP traffic, and compensating for the variable offset in the packet * decoder. */ +#define BPF_FILTER_IP_TYPE "(ip || ip6)" +#define BPF_TEMPLATE_IP BPF_FILTER_IP_TYPE +#define BPF_TEMPLATE_IP_VLAN "(" BPF_FILTER_IP_TYPE " || (vlan && " BPF_FILTER_IP_TYPE "))" -#define BPF_FILTER_IP_TYPE "(ip || ip6)" -#define BPF_TEMPLATE_IP BPF_FILTER_IP_TYPE -#define BPF_TEMPLATE_IP_VLAN "(" BPF_FILTER_IP_TYPE " || (vlan && " BPF_FILTER_IP_TYPE "))" -#define BPF_TEMPLATE_USERSPEC_IP "( %s) and " BPF_TEMPLATE_IP -#define BPF_TEMPLATE_USERSPEC_IP_VLAN "( %s) and " BPF_TEMPLATE_IP_VLAN - -#define WORD_REGEX "((^%s\\W)|(\\W%s$)|(\\W%s\\W))" +#define WORD_REGEX "((^%s\\W)|(\\W%s$)|(\\W%s\\W))" /* * Single-char packet "ident" flags. @@ -97,7 +94,7 @@ void print_time_absolute(struct pcap_pkthdr *); void print_time_diff(struct pcap_pkthdr *); void print_time_offset(struct pcap_pkthdr *); -char *get_filter_from_string(char *); +char *get_filter_from_file(void); char *get_filter_from_argv(char **); uint8_t strishex(char *);