addrs_dlpi.c | 3 +- addrs_ioctl.c | 3 +- cfgfile.c | 12 ++------ cfgfile.h | 2 + configure.in | 22 ++++++++++++++ get_addrs.h | 7 ++++ iftop.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++------ iftop.control | 17 +++++++++++ iftop.h | 3 -- options.c | 75 +------------------------------------------------- options.h | 3 +- ui.c | 17 +++++------ 12 files changed, 143 insertions(+), 107 deletions(-) diff --git a/addrs_dlpi.c b/addrs_dlpi.c index 188fef8..baa8648 100644 --- a/addrs_dlpi.c +++ b/addrs_dlpi.c @@ -28,6 +28,7 @@ #include #include "dlcommon.h" +#include "get_addrs.h" extern char *split_dname(char *device, int *unitp); extern char *strncpy2(char *dest, char *src, int n); @@ -50,7 +51,7 @@ extern char *strncat2(char *dest, char *src, int n); */ int -get_addrs_dlpi(char *interface, char if_hw_addr[], struct in_addr *if_ip_addr) +get_addrs_dlpi(char *interface, unsigned char if_hw_addr[], struct in_addr *if_ip_addr) { int got_hw_addr = 0; int got_ip_addr = 0; diff --git a/addrs_ioctl.c b/addrs_ioctl.c index 3d58d0d..c98ed6c 100644 --- a/addrs_ioctl.c +++ b/addrs_ioctl.c @@ -25,6 +25,7 @@ #endif #include "iftop.h" +#include "get_addrs.h" /* * This function identifies the IP address and ethernet address for the requested @@ -40,7 +41,7 @@ */ int -get_addrs_ioctl(char *interface, char if_hw_addr[], struct in_addr *if_ip_addr) +get_addrs_ioctl(char *interface, unsigned char if_hw_addr[], struct in_addr *if_ip_addr) { int s; struct ifreq ifr = {}; diff --git a/cfgfile.c b/cfgfile.c index 442316c..43f83c9 100644 --- a/cfgfile.c +++ b/cfgfile.c @@ -6,6 +6,7 @@ */ #include +#include #include #include @@ -56,12 +57,12 @@ int config_init() { return config != NULL; } -/* read_config_file: +/* read_config: * Read a configuration file consisting of key: value tuples, returning a * stringmap of the results. Prints errors to stderr, rather than using * syslog, since this file is called at program startup. Returns 1 on success * or 0 on failure. */ -int read_config_file(const char *f, int whinge) { +int read_config(const char *f, int whinge) { int ret = 0; FILE *fp; char *line; @@ -164,7 +165,6 @@ int config_get_int(const char *directive, int *value) { * failure, or 0 if no value was found. */ int config_get_float(const char *directive, float *value) { stringmap S; - item *I; char *s, *t; if (!value) return -1; @@ -233,9 +233,3 @@ void config_set_string(const char *directive, const char* s) { if (S) stringmap_delete_free(S); stringmap_insert(config, directive, item_ptr(xstrdup(s))); } - -int read_config(char *file, int whinge_on_error) { - void* o; - - return read_config_file(file, whinge_on_error); -} diff --git a/cfgfile.h b/cfgfile.h index 38532ad..24baba0 100644 --- a/cfgfile.h +++ b/cfgfile.h @@ -19,6 +19,8 @@ char *config_get_string(const char *directive); int config_get_bool(const char *directive); int config_get_int(const char *directive, int *value); int config_get_float(const char *directive, float *value); +void config_set_string(const char *directive, const char* s); +int config_get_enum(const char *directive, config_enumeration_type *enumeration, int *value); int config_init(); diff --git a/configure.in b/configure.in index 12055e0..12795d9 100644 --- a/configure.in +++ b/configure.in @@ -34,6 +34,7 @@ AM_INIT_AUTOMAKE(iftop, "0.17") AC_DEFINE_UNQUOTED(IFTOP_VERSION, "$VERSION", [The iftop version number]) dnl Make sure we have a C compiler.... +AC_GNU_SOURCE AC_PROG_CC AC_HEADER_STDC @@ -69,6 +70,26 @@ AC_ARG_WITH(libpcap, [libpcap_prefix=$withval], [libpcap_prefix=""]) +AC_ARG_WITH(user, + [ --with-user=USERNAME Drop privileges by default to USERNAME]) +AC_MSG_CHECKING([whether to drop root privileges by default]) +if test -n "$withval"; then + AC_DEFINE_UNQUOTED(WITH_USER, "$withval", [Drop privileges by default to this username]) + AC_MSG_RESULT(to \"$withval\") +else + AC_MSG_RESULT(no) +fi + +AC_ARG_WITH(chroot, + [ --with-chroot=DIRECTORY When dropping privileges, chroot to DIRECTORY]) +AC_MSG_CHECKING([whether to chroot by default]) +if test -n "$withval"; then + AC_DEFINE_UNQUOTED(WITH_CHROOT, "$withval", [Chroot to this directory when dropping privileges]) + AC_MSG_RESULT(to \"$withval\") +else + AC_MSG_RESULT(no) +fi + dnl dnl Fairly generic checks. dnl @@ -92,6 +113,7 @@ AC_CHECK_FUNCS(regcomp select strdup strerror strspn) AC_SEARCH_LIBS(socket, socket) AC_SEARCH_LIBS(log, m) +AC_SEARCH_LIBS(dlopen, dl) AC_CHECK_FUNC(gethostbyname, , [AC_CHECK_LIB(nsl, gethostbyname)] ) diff --git a/get_addrs.h b/get_addrs.h new file mode 100644 index 0000000..21a942c --- /dev/null +++ b/get_addrs.h @@ -0,0 +1,7 @@ +#ifndef GET_ADDRS_H_ /* include guard */ +#define GET_ADDRS_H_ + +int get_addrs_dlpi(char *interface, unsigned char if_hw_addr[], struct in_addr *if_ip_addr); +int get_addrs_ioctl(char *interface, unsigned char if_hw_addr[], struct in_addr *if_ip_addr); + +#endif /* GET_ADDRS_H_ */ diff --git a/iftop.c b/iftop.c index 5a7b41e..7e126d4 100644 --- a/iftop.c +++ b/iftop.c @@ -25,6 +25,10 @@ #include #include #include +#include +#include +#include +#include #include "iftop.h" #include "addr_hash.h" @@ -44,6 +48,7 @@ #include "ethertype.h" #include "cfgfile.h" #include "ppp.h" +#include "get_addrs.h" /* ethernet address of interface. */ @@ -477,7 +482,6 @@ char *set_filter_code(const char *filter) { void packet_init() { char errbuf[PCAP_ERRBUF_SIZE]; char *m; - int s; int i; int dlt; int result; @@ -507,7 +511,6 @@ void packet_init() { } // exit(0); - resolver_initialise(); pd = pcap_open_live(options.interface, CAPTURE_LENGTH, options.promiscuous, 1000, errbuf); // DEBUG: pd = pcap_open_offline("tcpdump.out", errbuf); @@ -557,10 +560,69 @@ void packet_loop(void* ptr) { } +static int droproot (void) { +#ifdef WITH_USER + uid_t uid; + gid_t gid; + + if (geteuid()) + return 0; + + if ((uid = getuid()) == 0) { + const char *username = WITH_USER; + struct passwd *pw = getpwnam(username); + + if (!pw) { + fprintf(stderr, "lookup of user \"%s\" failed\n", username); + return 1; + } + + gid = pw->pw_gid; + uid = pw->pw_uid; + + if (uid == 0) { + fprintf(stderr, "user \"%s\" should not be privileged\n", + username); + return 1; + } + + if (initgroups(pw->pw_name, gid) != 0) { + fprintf(stderr, + "Failed to initialize supplementary group list: %m\n"); + return 1; + } + + endpwent(); + +#ifdef WITH_CHROOT + (void) dlopen("libgcc_s.so.1", RTLD_LAZY); + + const char *chroot_dir = WITH_CHROOT; + if (chdir(chroot_dir) < 0 || chroot(".") < 0) { + fprintf(stderr, "chroot/chdir to \"%s\" failed: %s\n", + chroot_dir, strerror(errno)); + return 1; + } +#endif /* WITH_CHROOT */ + + } else { + gid = getgid(); + } + + if (setgid(gid) < 0 || setuid(uid) < 0) { + fprintf(stderr, "setuid/setgid to %u:%u failed: %s\n", + uid, gid, strerror(errno)); + return 1; + } + +#endif /* WITH_USER */ + return 0; +} + /* main: * Entry point. See usage(). */ int main(int argc, char **argv) { - pthread_t thread; + int rc; struct sigaction sa = {}; /* TODO: tidy this up */ @@ -575,21 +637,27 @@ int main(int argc, char **argv) { sa.sa_handler = finish; sigaction(SIGINT, &sa, NULL); - pthread_mutex_init(&tick_mutex, NULL); - packet_init(); init_history(); ui_init(); - pthread_create(&thread, NULL, (void*)&packet_loop, NULL); + if ((rc = droproot()) == 0) { + pthread_t thread; + + pthread_mutex_init(&tick_mutex, NULL); + + resolver_initialise(); - ui_loop(); + pthread_create(&thread, NULL, (void*)&packet_loop, NULL); - pthread_cancel(thread); + ui_loop(); + + pthread_cancel(thread); + } ui_finish(); - return 0; + return rc; } diff --git a/iftop.control b/iftop.control new file mode 100644 index 0000000..9b2e0a3 --- /dev/null +++ b/iftop.control @@ -0,0 +1,17 @@ +#!/bin/sh + +. /etc/control.d/functions + +BINARY=/usr/bin/iftop + +new_summary 'Network bandwidth monitor' + +new_fmode public 4711 root root +new_fmode netadmin 4710 root netadmin +new_fmode restricted 700 root root + +new_help public "Any user can execute $BINARY" +new_help netadmin "Only \"netadmin\" group members can execute $BINARY" +new_help restricted "Only root can execute $BINARY" + +control_fmode "$BINARY" "$*" || exit 1 diff --git a/iftop.h b/iftop.h index 42947af..b3101c2 100644 --- a/iftop.h +++ b/iftop.h @@ -32,8 +32,5 @@ void xfree(void *v); void analyse_data(void); void ui_init(void); -/* options.c */ -void options_read(int argc, char **argv); - #endif /* __IFTOP_H_ */ diff --git a/options.c b/options.c index 8f385de..32d4106 100644 --- a/options.c +++ b/options.c @@ -158,79 +158,6 @@ void options_set_defaults() { } -static void die(char *msg) { - fprintf(stderr, msg); - exit(1); -} - -static void set_max_bandwidth(char* arg) { - char* units; - long long mult = 1; - long long value; - units = arg + strspn(arg, "0123456789"); - if(strlen(units) > 1) { - die("Invalid units\n"); - } - if(strlen(units) == 1) { - if(*units == 'k' || *units == 'K') { - mult = 1024; - } - else if(*units == 'm' || *units == 'M') { - mult = 1024 * 1024; - } - else if(*units == 'g' || *units == 'G') { - mult = 1024 * 1024 * 1024; - } - } - *units = '\0'; - if(sscanf(arg, "%lld", &value) != 1) { - die("Error reading max bandwidth\n"); - } - options.max_bandwidth = value * mult; -} - -static void set_net_filter(char* arg) { - char* mask; - - mask = strchr(arg, '/'); - if (mask == NULL) { - die("Could not parse net/mask\n"); - } - *mask = '\0'; - mask++; - if (inet_aton(arg, &options.netfilternet) == 0) - die("Invalid network address\n"); - /* Accept a netmask like /24 or /255.255.255.0. */ - if (mask[strspn(mask, "0123456789")] == '\0') { - /* Whole string is numeric */ - int n; - n = atoi(mask); - if (n > 32) { - die("Invalid netmask"); - } - else { - if(n == 32) { - /* This needs to be special cased, although I don't fully - * understand why -pdw - */ - options.netfiltermask.s_addr = htonl(0xffffffffl); - } - else { - u_int32_t mm = 0xffffffffl; - mm >>= n; - options.netfiltermask.s_addr = htonl(~mm); - } - } - } - else if (inet_aton(mask, &options.netfiltermask) == 0) { - die("Invalid netmask\n"); - } - options.netfilternet.s_addr = options.netfilternet.s_addr & options.netfiltermask.s_addr; - - options.netfilter = 1; - -} - /* usage: * Print usage information. */ static void usage(FILE *fp) { @@ -481,7 +408,7 @@ int options_config_get_net_filter() { } -void options_make() { +void options_make(void) { options_config_get_string("interface", &options.interface); options_config_get_bool("dns-resolution", &options.dnsresolution); options_config_get_bool("port-resolution", &options.portresolution); diff --git a/options.h b/options.h index d6b64ea..317d253 100644 --- a/options.h +++ b/options.h @@ -85,6 +85,7 @@ typedef struct { void options_set_defaults(); -void options_read(int argc, char **argv); +void options_read_args(int argc, char **argv); +void options_make(void); #endif /* __OPTIONS_H_ */ diff --git a/ui.c b/ui.c index 72ceeb0..c624b55 100644 --- a/ui.c +++ b/ui.c @@ -263,7 +263,7 @@ static void draw_bar_scale(int* y) { char s[40], *p; int x; /* This 1024 vs 1000 stuff is just plain evil */ - readable_size(i, s, sizeof s, options.log_scale ? 1000 : 1024, 0); + readable_size(i, s, sizeof s, options.log_scale ? 1000 : 1024, options.bandwidth_in_bytes); p = s + strspn(s, " "); x = get_bar_length(i * 8); mvaddch(*y + 1, x, ACS_BTEE); @@ -298,10 +298,6 @@ void draw_line_total(float sent, float recv, int y, int x, option_linedisplay_t char buf[10]; float n; switch(linedisplay) { - case OPTION_LINEDISPLAY_TWO_LINE: - draw_line_total(sent, recv, y, x, OPTION_LINEDISPLAY_ONE_LINE_SENT, bytes); - draw_line_total(sent, recv, y+1, x, OPTION_LINEDISPLAY_ONE_LINE_RECV, bytes); - break; case OPTION_LINEDISPLAY_ONE_LINE_SENT: n = sent; break; @@ -311,11 +307,14 @@ void draw_line_total(float sent, float recv, int y, int x, option_linedisplay_t case OPTION_LINEDISPLAY_ONE_LINE_BOTH: n = recv + sent; break; + case OPTION_LINEDISPLAY_TWO_LINE: + draw_line_total(sent, recv, y, x, OPTION_LINEDISPLAY_ONE_LINE_SENT, bytes); + draw_line_total(sent, recv, y+1, x, OPTION_LINEDISPLAY_ONE_LINE_RECV, bytes); + default: + return; } - if(linedisplay != OPTION_LINEDISPLAY_TWO_LINE) { - readable_size(n, buf, 10, 1024, bytes); - mvaddstr(y, x, buf); - } + readable_size(n, buf, 10, 1024, bytes); + mvaddstr(y, x, buf); } void draw_bar(float n, int y) {