diff options
author | Thomas Habets <thomas@habets.se> | 2014-06-17 10:23:14 +0100 |
---|---|---|
committer | Thomas Habets <thomas@habets.se> | 2014-06-17 10:23:14 +0100 |
commit | 8fd8171def7f033d50cce443dc91587bdfca3d28 (patch) | |
tree | da91786554fc9e7c9287ee08c03f9e6f69b0f87c | |
parent | 4eb0c7b508e7764b5ba3067a083fc2e65595e7e1 (diff) | |
download | arping-8fd8171def7f033d50cce443dc91587bdfca3d28.tar.gz |
Drop privileges, where possible.
Fixes #9.
-rw-r--r-- | INSTALL | 7 | ||||
-rw-r--r-- | README | 7 | ||||
-rw-r--r-- | configure.ac | 5 | ||||
-rw-r--r-- | src/arping.c | 135 |
4 files changed, 147 insertions, 7 deletions
@@ -5,13 +5,16 @@ This is the short version. For more info see the README. Dependencies ------------ libnet 1.1.x - www.packetfactory.net/libnet + https://github.com/sam-github/libnet or http://ftp.debian.org/debian/pool/main/libn/libnet/libnet_1.1.2.1.orig.tar.gz + Note: Libnet 1.1.5 or later needed for Linux capability support. libpcap: www.tcpdump.org +optional: libcap + Adds support to drop Linux privileges. -For debian, just: apt-get install libpcap-dev libnet1-dev +For debian, just: apt-get install libpcap-dev libnet1-dev libcap-dev Installing, short version ------------------------- @@ -197,11 +197,10 @@ A: Be my guest, but if care about security *at all* you will have to restrict That being said, on Linux you can add the CAP_NET_RAW capability to arping limiting the damage if arping were to be compromised: sudo setcap cap_net_raw+ep /usr/local/sbin/arping - This requires a libnet which does not explicitly check for uid 0. The - current version of libnet does check this, so unless you patch it it will - not help. + This requires a libnet 1.1.5 or higher, which does not explicitly check for + uid 0. - Patch: + For older versions of Libnet: http://github.com/ThomasHabets/libnet/commit/aaa383b5c816107082508b7646929a9479b81645 --- Q: What's this -A switch all about, I don't understand it. diff --git a/configure.ac b/configure.ac index 26c7cc4..d96a9d2 100644 --- a/configure.ac +++ b/configure.ac @@ -19,6 +19,7 @@ AC_PROG_MAKE_SET AC_CHECK_LIB([m], [sqrt]) AC_CHECK_LIB([socket], [socket]) AC_CHECK_LIB([nsl], [gethostbyname]) +AC_CHECK_LIB([cap], [cap_init]) AC_CHECK_LIB([rt], [clock_gettime]) AC_CHECK_LIB([net], [libnet_name_resolve], [AC_MSG_ERROR([libnet 1.0.x found. Arping 2.x requires libnet 1.1.x])]) @@ -36,10 +37,12 @@ sys/socket.h \ time.h \ sys/time.h \ sys/types.h \ +sys/capability.h \ stdint.h \ libnet.h \ win32/libnet.h \ net/bpf.h \ +pwd.h \ unistd.h]) # Libnet include file is not optional @@ -62,7 +65,7 @@ AC_FUNC_SELECT_ARGTYPES AC_FUNC_SETVBUF_REVERSED AC_TYPE_SIGNAL AC_CHECK_FUNCS([gettimeofday memset select strchr strdup strerror strstr \ -getifaddrs]) +getifaddrs cap_init]) if test x$ac_cv_func_getifaddrs = xyes; then AC_LIBOBJ([findif_getifaddrs]) diff --git a/src/arping.c b/src/arping.c index 54aa2a9..508f084 100644 --- a/src/arping.c +++ b/src/arping.c @@ -85,6 +85,14 @@ #include <win32/libnet.h> #endif +#if HAVE_PWD_H +#include <pwd.h> +#endif + +#if HAVE_SYS_CAPABILITY_H +#include <sys/capability.h> +#endif + #if HAVE_NET_BPF_H #include <net/bpf.h> #endif @@ -184,6 +192,132 @@ int verbose = 0; /* Increase with -v */ static volatile sig_atomic_t time_to_die = 0; /** + * If possible, chroot. + * + * The sshd user is used for privilege separation in OpenSSH. + * Let's assume it's installed and chroot() to there. + */ +static void +drop_fs_root() +{ + const char* chroot_user = "sshd"; + struct passwd *pw; + errno = 0; + if (!(pw = getpwnam(chroot_user))) { + if (verbose) { + printf("arping: getpwnam(%s): %s", + chroot_user, strerror(errno)); + } + return; + } + if (chdir(pw->pw_dir)) { + if (verbose) { + printf("arping: chdir(%s): %s", + pw->pw_dir, strerror(errno)); + } + return; + } + if (chroot(pw->pw_dir)) { + if (verbose) { + printf("arping: chroot(%s): %s", + pw->pw_dir, strerror(errno)); + } + return; + } + if (verbose > 1) { + printf("arping: Successfully chrooted to %s\n", pw->pw_dir); + } +} + +/** + * If possible, drop uid to nobody. + * + * This code only successfully sets all [ug]ids if running as + * root. ARPing is most likely running as root unless using + * capabilities, and those are dropped elsewhere. + */ +static void +drop_uid(uid_t uid, gid_t gid) +{ + int fail = 0; + if (setgroups(0, NULL)) { + if (verbose) { + printf("arping: setgroups(0, NULL): %s\n", strerror(errno)); + } + fail++; + } + if (gid && setgid(gid)) { + if (verbose) { + printf("arping: setgid(): %s\n", strerror(errno)); + } + fail++; + } + if (uid && setuid(uid)) { + if (verbose) { + printf("arping: setuid(): %s\n", strerror(errno)); + } + fail++; + } + if (!fail && verbose > 1) { + printf("arping: Successfully dropped uid/gid to %d/%d.\n", + uid, gid); + } +} + +/** + * Drop any and all capabilities. + */ +static void +drop_capabilities() +{ +#if HAVE_CAP_INIT + cap_t no_cap; + if (!(no_cap = cap_init())) { + if (verbose) { + printf("arping: cap_init(): %s\n", strerror(errno)); + } + return; + } + if (cap_set_proc(no_cap)) { + if (verbose) { + printf("arping: cap_set_proc(): %s\n", strerror(errno)); + } + } + if (verbose > 1) { + printf("arping: Successfully dropped all capabilities.\n"); + } + cap_free(no_cap); +#endif +} + +/** + * drop all privileges. + */ +static void +drop_privileges() +{ + // Need to get uid/gid of 'nobody' before chroot(). + const char* drop_user = "nobody"; + struct passwd *pw; + errno = 0; + uid_t uid = 0; + gid_t gid = 0; + if (!(pw = getpwnam(drop_user))) { + if (verbose) { + printf("arping: getpwnam(%s): %s\n", + drop_user, strerror(errno)); + } + return; + } else { + uid = pw->pw_uid; + gid = pw->pw_gid; + } + drop_fs_root(); + drop_uid(uid, gid); + drop_capabilities(); +} + +/** * Some stupid OSs (Solaris) think it's a good idea to put network * devices in /dev and then play musical chairs with them. * @@ -1399,6 +1533,7 @@ int main(int argc, char **argv) fprintf(stderr, "arping: pcap_open_live(): %s\n", ebuf); exit(1); } + drop_privileges(); if (pcap_setnonblock(pcap, 1, ebuf)) { strip_newline(ebuf); fprintf(stderr, "arping: pcap_set_nonblock(): %s\n", ebuf); |