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:
M | Makefile | | | 3 | ++- |
M | ngrep.c | | | 90 | +++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------- |
M | ngrep.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;