Index: arpwatch/arpwatch.8 diff -u arpwatch/arpwatch.8:1.1.1.1.2.2 arpwatch/arpwatch.8:1.1.1.1.20.3 --- arpwatch/arpwatch.8:1.1.1.1.2.2 Thu Aug 12 22:31:09 2004 +++ arpwatch/arpwatch.8 Sat Aug 14 02:21:59 2004 @@ -63,6 +63,20 @@ ] .\" ** .\" ** +.br +.ti +8 +[ +.B -u +.I username +] +.br +.ti +8 +[ +.B -R +.I seconds +] +.\" ** +.\" ** .ad .SH DESCRIPTION .B Arpwatch @@ -127,6 +141,34 @@ .\" ** .\" ** .LP +(Debian) The +.B -u +flag instructs +.B arpwatch +to drop root privileges and change the UID to +.I username +and GID to the primary group of +.I username . +This is recommended for security reasons, but +.I username +has to have write access to the default directory. +.LP +(Debian) The +.B -R +flag instructs +.B arpwatch +to restart in +.I seconds +seconds after the interface went down. By default, in such cases +arpwatch would print an error message and exit. This option is +ignored if either the +.B -r +or +.B -u +flags are used. +.\" ** +.\" ** +.LP Note that an empty .I arp.dat file must be created before the first time you run Index: arpwatch/arpwatch.c diff -u arpwatch/arpwatch.c:1.1.1.1.2.5 arpwatch/arpwatch.c:1.1.1.1.2.1.10.7 --- arpwatch/arpwatch.c:1.1.1.1.2.5 Sat Aug 14 02:33:07 2004 +++ arpwatch/arpwatch.c Sat Aug 14 02:36:15 2004 @@ -62,7 +62,8 @@ #include #include #include - +#include +#include #include #include "gnuc.h" @@ -141,6 +142,24 @@ int sanity_fddi(struct fddi_header *, struct ether_arp *, int); __dead void usage(void) __attribute__((volatile)); +void dropprivileges(const char* user) +{ + struct passwd* pw; + pw = getpwnam( user ); + if ( pw ) { + if ( initgroups(pw->pw_name, 0) != 0 || setgid(pw->pw_gid) != 0 || + setuid(pw->pw_uid) != 0 ) { + syslog(LOG_ERR, "Couldn't change to '%.32s' uid=%d gid=%d", user,pw->pw_uid, pw->pw_gid); + exit(1); + } + } + else { + syslog(LOG_ERR, "Couldn't find user '%.32s' in /etc/passwd", user); + exit(1); + } + syslog(LOG_INFO, "Running as uid=%d gid=%d", getuid(), getgid()); +} + int main(int argc, char **argv) { @@ -153,6 +172,8 @@ register char *interface, *rfilename; struct bpf_program code; char errbuf[PCAP_ERRBUF_SIZE]; + char* username = NULL; + int restart = 0; char options[] = "d" /**/ @@ -172,6 +193,10 @@ "r:" /**/ /**/ + "u:" + "R:" + /**/ + /**/ ; if (argv[0] == NULL) @@ -223,6 +248,19 @@ break; /**/ /**/ + case 'u': + if ( optarg ) { + username = strdup(optarg); + } else { + fprintf(stderr, "%s: Need username after -u\n", prog); + usage(); + } + break; + case 'R': + restart = atoi(optarg); + break; + /**/ + /**/ default: usage(); } @@ -233,6 +271,8 @@ if (rfilename != NULL) { net = 0; netmask = 0; + interface = "(from file)"; + restart = 0; } else { /* Determine interface if not specified */ if (interface == NULL && @@ -279,6 +319,7 @@ syslog(LOG_ERR, "(using current working directory)"); } +label_restart: if (rfilename != NULL) { pd = pcap_open_offline(rfilename, errbuf); if (pd == NULL) { @@ -293,19 +334,29 @@ pd = pcap_open_live(interface, snaplen, 1, timeout, errbuf); if (pd == NULL) { syslog(LOG_ERR, "pcap open %s: %s", interface, errbuf); - exit(1); + if (restart) { + syslog(LOG_ERR, "restart in %d secs", restart); + } else { + exit(1); + } + sleep(restart); + goto label_restart; } #ifdef WORDS_BIGENDIAN swapped = 1; #endif } + if ( username && !restart ) { + dropprivileges( username ); + } else { /* * Revert to non-privileged user after opening sockets * (not needed on most systems). */ - setgid(getgid()); - setuid(getuid()); + setgid(getgid()); + setuid(getuid()); + } /* Must be ethernet or fddi */ linktype = pcap_datalink(pd); @@ -785,6 +836,10 @@ "[-r file] " /**/ /**/ + "[-u username] " + "[-R seconds ] " + /**/ + /**/ "\n" ;