summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Habets <thomas@habets.se>2014-06-17 10:23:14 +0100
committerThomas Habets <thomas@habets.se>2014-06-17 10:23:14 +0100
commit8fd8171def7f033d50cce443dc91587bdfca3d28 (patch)
treeda91786554fc9e7c9287ee08c03f9e6f69b0f87c
parent4eb0c7b508e7764b5ba3067a083fc2e65595e7e1 (diff)
downloadarping-8fd8171def7f033d50cce443dc91587bdfca3d28.tar.gz
Drop privileges, where possible.
Fixes #9.
-rw-r--r--INSTALL7
-rw-r--r--README7
-rw-r--r--configure.ac5
-rw-r--r--src/arping.c135
4 files changed, 147 insertions, 7 deletions
diff --git a/INSTALL b/INSTALL
index 27254f4..af0182e 100644
--- a/INSTALL
+++ b/INSTALL
@@ -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
-------------------------
diff --git a/README b/README
index dfcb7cb..cc4ccfd 100644
--- a/README
+++ b/README
@@ -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);