Add, and document, a -u argument to change to a specified unprivileged user after establishing sockets. This patch rebases and combines arpwatch-drop.patch, which provided -u; arpwatch-drop-man.patch, which documented it; and arpwatch-2.1a15-dropgroup.patch, which fixed CVE-2012-2653 (RHBZ #825328) in the original arpwatch-drop.patch, into a single combined patch. It also removes an unnecessary and unchecked strdup() in the original patch that could have theoretically led to a null pointer dereference. diff -Naur arpwatch-3.2-original/arpwatch.8.in arpwatch-3.2/arpwatch.8.in --- arpwatch-3.2-original/arpwatch.8.in 2021-12-14 19:47:54.000000000 -0500 +++ arpwatch-3.2/arpwatch.8.in 2021-12-16 08:18:21.803266980 -0500 @@ -43,6 +43,7 @@ .Op Fl n Ar net[/width] .Op Fl x Ar net[/width] .Op Fl r Ar file +.Op Fl u Ar username .Sh DESCRIPTION .Nm keeps track of ethernet/ip address pairings. It syslogs activity @@ -137,13 +138,30 @@ Note that an empty .Ar arp.dat file must be created before the first time you run -.Fl arpwatch . +.Nm . +Also, the default directory (where +.Ar arp.dat +is stored) must be owned by +.Ar username +if the +.Fl u +flag is used. .Pp The .Fl s flag suppresses reports sent by email. .Pp The +.Fl u +flag causes +.Nm +to drop root privileges and change user ID to +.Ar username +and group ID to that of the primary group of +.Ar username . +This is recommended for security reasons. +.Pp +The .Fl v flag disables the reporting of VRRP/CARP ethernet prefixes as described in RFC5798 (@MACZERO@0:@MACZERO@0:5e:@MACZERO@0:@MACZERO@1:xx). diff -Naur arpwatch-3.2-original/arpwatch.c arpwatch-3.2/arpwatch.c --- arpwatch-3.2-original/arpwatch.c 2019-11-30 13:35:23.000000000 -0500 +++ arpwatch-3.2/arpwatch.c 2021-12-16 08:18:21.812267045 -0500 @@ -72,6 +72,8 @@ #include #include +#include +#include #include #include "gnuc.h" @@ -170,6 +172,24 @@ int toskip(u_int32_t); void usage(void) __attribute__((noreturn)); +void dropprivileges(const char* user) +{ + struct passwd* const pw = getpwnam(user); + if (pw) { + if (setgid(pw->pw_gid) != 0 || setgroups(0, NULL) != 0 || + setuid(pw->pw_uid) != 0) { + lg(LOG_ERR, "Couldn't change to '%.32s' uid=%d gid=%d", + user, pw->pw_uid, pw->pw_gid); + exit(1); + } + } else { + lg(LOG_ERR, "Couldn't find user '%.32s' in /etc/passwd", + user); + exit(1); + } + lg(LOG_DEBUG, "Running as uid=%d gid=%d", getuid(), getgid()); +} + int main(int argc, char **argv) { @@ -181,6 +201,7 @@ char *interface, *rfilename; struct bpf_program code; char errbuf[PCAP_ERRBUF_SIZE]; + char* serveruser = NULL; if (argv[0] == NULL) prog = "arpwatch"; @@ -198,7 +219,7 @@ interface = NULL; rfilename = NULL; pd = NULL; - while ((op = getopt(argc, argv, "CdD:Ff:i:n:NpP:qr:svw:W:x:zZ")) != EOF) + while ((op = getopt(argc, argv, "CdD:Ff:i:n:NpP:qr:svw:W:x:zZu:")) != EOF) switch (op) { case 'C': @@ -283,6 +304,17 @@ zeropad = 1; break; + case 'u': + if (optarg) { + /* no need to strdup() a pointer into the + * original arguments vector */ + serveruser = optarg; + } else { + fprintf(stderr, "%s: Need username after -u\n", prog); + usage(); + } + break; + default: usage(); } @@ -379,6 +411,11 @@ } } + /* Explicit user change (privilege drop) with -u? */ + if (serveruser) { + dropprivileges(serveruser); + } + /* * Revert to non-privileged user after opening sockets * (not needed on most systems). @@ -927,6 +964,7 @@ "usage: %s [-CdFNpqsvzZ] [-D arpdir] [-f datafile]" " [-i interface]\n\t" " [-P pidfile] [-w watcher@email] [-W watchee@email]\n\t" - " [-n net[/width]] [-x net[/width]] [-r file]\n", prog); + " [-n net[/width]] [-x net[/width]] [-r file] [-u username]\n", + prog); exit(1); }