ongrep

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

commit b04abb4f48b81b5ee3b62ce6b86ae5f658898f7e
parent 5dd590e58a1a2cb4c4235847da630646d3e20daf
Author: Stephen Gregoratto <dev@sgregoratto.me>
Date:   Tue, 16 Jun 2020 20:20:28 +1000

Re-enable priv dropping and unveil files

This is a botch for now as everything must run as root. Down the line
the program should be split in two, where a privileged process
sends data and fds to the unprivileged one.

Diffstat:
MMakefile | 3++-
Mngrep.c | 90+++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------
Mngrep.h | 2+-
3 files changed, 63 insertions(+), 32 deletions(-)

diff --git a/Makefile b/Makefile @@ -10,8 +10,9 @@ INSTALL_SBIN = install -m 0555 INSTALL_MAN = install -m 0444 INSTALL_EXAMPLE = install -m 0644 +UNPRIV_USER ?= _ngrep CFLAGS += -Wall -Wextra -pedantic-errors -CPPFLAGS += -I$(PREFIX)/include +CPPFLAGS += -DUNPRIV_USER='"$(UNPRIV_USER)"' -I$(PREFIX)/include LDFLAGS += -L/usr/local/lib -lpcre -lpcap ngrep: ngrep.c diff --git a/ngrep.c b/ngrep.c @@ -26,8 +26,10 @@ #include <ctype.h> #include <err.h> #include <errno.h> +#include <fcntl.h> #include <pcap.h> #include <pcre.h> +#include <pwd.h> #include <signal.h> #include <stdint.h> #include <stdio.h> @@ -47,7 +49,6 @@ uint8_t re_match_word = 0, re_ignore_case = 0, re_multiline_match = 1; uint8_t show_empty = 0, show_hex = 0, show_proto = 0, quiet = 0; uint8_t invert_match = 0, bin_match = 0; uint8_t live_read = 1, want_delay = 0; -uint8_t dont_dropprivs = 0; uint8_t enable_hilite = 0; char *read_file = NULL, *dump_file = NULL; @@ -91,7 +92,7 @@ void (*print_time)() = NULL, (*dump_delay)() = dump_delay_proc_init; */ uint32_t ws_row, ws_col = 80, ws_col_forced = 0; -const char *optstring = "A:c:d:DeF:hiI:lMn:NO:pP:qRs:S:tTvwW:xX"; +const char *optstring = "A:c:d:DeF:hiI:lMn:NO:pP:qs:S:tTvwW:xX"; int main(int argc, char **argv) @@ -99,6 +100,24 @@ main(int argc, char **argv) int c; const char *errstr; + if (unveil("/etc/services", "r") == -1) + err(2, "unveil"); + if (unveil("/etc/protocols", "r") == -1) + err(2, "unveil"); + if (unveil("/etc/pwd.db", "r") == -1) + err(2, "unveil"); + /* + * XXX: pcap_open_live opens /dev/bpf as RW, + * even though we don't write to it. + * + * The base programs work around this by implementing their own version + * of pcap_open_live and setting the hidden members of pcap_t. Lame. + */ + if (unveil("/dev/bpf", "rw") == -1) + err(1, "unveil"); + if (unveil("/var/empty", "r") == -1) + err(1, "unveil"); + signal(SIGINT, clean_exit); signal(SIGABRT, clean_exit); signal(SIGQUIT, clean_exit); @@ -133,12 +152,16 @@ main(int argc, char **argv) break; case 'F': filter_file = optarg; + if (unveil(filter_file, "r") == -1) + err(2, "unveil"); break; case 'i': re_ignore_case++; break; case 'I': read_file = optarg; + if (unveil(read_file, "r") == -1) + err(2, "unveil"); break; case 'l': setvbuf(stdout, NULL, _IOLBF, 0); @@ -156,6 +179,8 @@ main(int argc, char **argv) break; case 'O': dump_file = optarg; + if (unveil(dump_file, "wc") == -1) + err(2, "unveil"); break; case 'p': promisc = 0; @@ -166,9 +191,6 @@ main(int argc, char **argv) case 'q': quiet++; break; - case 'R': - dont_dropprivs = 1; - break; case 's': snaplen = strtonum(optarg, 1, UINT32_MAX, &errstr); if (errstr != NULL) @@ -235,6 +257,11 @@ main(int argc, char **argv) if (setup_pcap_source()) clean_exit(2); + if (unveil(NULL, NULL) == -1) { + warn("unveil"); + clean_exit(2); + } + /* Setup BPF filter */ if (setup_bpf_filter(argv)) { include_vlan = 0; @@ -247,6 +274,9 @@ main(int argc, char **argv) clean_exit(2); } } + + drop_privs(); + if (filter) { if (quiet < 2) printf("filter: %s\n", filter); @@ -277,8 +307,6 @@ main(int argc, char **argv) } update_windowsize(0); - drop_privs(); - while (pcap_loop(pd, -1, (pcap_handler)process, 0)); clean_exit(0); @@ -1122,36 +1150,38 @@ update_windowsize(int32_t e) void drop_privs(void) { -#if 0 - struct passwd *pw; - uid_t newuid; - gid_t newgid; + struct passwd *pw; + gid_t gidset[1]; - if ((getuid() || geteuid()) || dont_dropprivs) - return; - - pw = getpwnam("_ngrep"); - if (!pw) { - perror("attempt to drop privileges failed: getpwnam failed"); + if ((pw = getpwnam(UNPRIV_USER)) == NULL) { + warn("cannot drop privileges: getpwnam"); clean_exit(2); } - newgid = pw->pw_gid; - newuid = pw->pw_uid; + endpwent(); - if (getgroups(0, NULL) > 0) - if (setgroups(1, &newgid) == -1) { - perror("attempt to drop privileges failed"); - clean_exit(2); - } - if (((getgid() != newgid) && (setgid(newgid) == -1)) || - ((getegid() != newgid) && (setegid(newgid) == -1)) || - ((getuid() != newuid) && (setuid(newuid) == -1)) || - ((geteuid() != newuid) && (seteuid(newuid) == -1))) { + if (chroot("/var/empty") == -1) { + warn("cannot drop privileges: chroot"); + clean_exit(2); + } + if (chdir("/") == -1) { + warn("cannot drop privileges: chdir"); + clean_exit(2); + } - perror("attempt to drop privileges failed"); + gidset[0] = pw->pw_gid; + if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1) { + warn("cannot drop privileges: setresgid"); + clean_exit(2); + } + if (setgroups(1, gidset) == -1) { + warn("cannot drop privileges: setgroups"); clean_exit(2); } -#endif + if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1) { + warn("cannot drop privileges: setresuid"); + clean_exit(2); + } + } void diff --git a/ngrep.h b/ngrep.h @@ -102,7 +102,7 @@ char *get_filter_from_argv(char **); uint8_t strishex(char *); -void drop_privs(void); +void drop_privs(void); struct NGREP_rtaphdr_t { uint8_t it_version;