tcpdump mailing list archives

[PATCH] Drop unneeded capabilities


From: Matt Beaumont <mbeaumon () cs hmc edu>
Date: Wed, 23 Jun 2004 13:26:27 -0700

I've written a little patch to drop all but the CAP_NET_ADMIN and
CAP_NET_RAW capabilities immediately if tcpdump is running with root
privileges.  The idea is to limit the damage done by an exploit
against tcpdump.

Some of the inspiration for this patch came from here:
<http://www.dwheeler.com/secure-programs/Secure-Programs-HOWTO/minimize-privileges.html>

This is the first patch I've ever submitted, so I'd love to hear some
feedback :)

Cheers,
Matt

diff -upr tcpdump-3.8.3/config.h.in tcpdump-3.8.3-cap/config.h.in
--- tcpdump-3.8.3/config.h.in   2004-03-28 13:06:09.000000000 -0800
+++ tcpdump-3.8.3-cap/config.h.in       2004-06-23 11:08:46.000000000 -0700
@@ -145,6 +145,9 @@
 /* Define to 1 if you have the <inttypes.h> header file. */
 #undef HAVE_INTTYPES_H
 
+/* Define to 1 if you have the `cap' library (-lcap). */
+#undef HAVE_LIBCAP
+
 /* Define to 1 if you have the `crypto' library (-lcrypto). */
 #undef HAVE_LIBCRYPTO
 
@@ -235,6 +238,9 @@
 /* Define to 1 if you have the `strsep' function. */
 #undef HAVE_STRSEP
 
+/* Define to 1 if you have the <sys/capability.h> header file. */
+#undef HAVE_SYS_CAPABILITY_H
+
 /* Define to 1 if you have the <sys/stat.h> header file. */
 #undef HAVE_SYS_STAT_H
 
diff -upr tcpdump-3.8.3/configure tcpdump-3.8.3-cap/configure
--- tcpdump-3.8.3/configure     2004-03-28 13:06:09.000000000 -0800
+++ tcpdump-3.8.3-cap/configure 2004-06-23 11:08:42.000000000 -0700
@@ -9951,6 +9951,213 @@ fi
 fi;
 
 
+echo "$as_me:$LINENO: checking for cap_init in -lcap" >&5
+echo $ECHO_N "checking for cap_init in -lcap... $ECHO_C" >&6
+if test "${ac_cv_lib_cap_cap_init+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lcap  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+#line $LINENO "configure"
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char cap_init ();
+int
+main ()
+{
+cap_init ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+         { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_lib_cap_cap_init=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_cap_cap_init=no
+fi
+rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_cap_cap_init" >&5
+echo "${ECHO_T}$ac_cv_lib_cap_cap_init" >&6
+if test $ac_cv_lib_cap_cap_init = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBCAP 1
+_ACEOF
+
+  LIBS="-lcap $LIBS"
+
+fi
+
+
+for ac_header in sys/capability.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+else
+  # Is the header compilable?
+echo "$as_me:$LINENO: checking $ac_header usability" >&5
+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+#line $LINENO "configure"
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+         { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_header_compiler=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_header_compiler=no
+fi
+rm -f conftest.$ac_objext conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6
+
+# Is the header present?
+echo "$as_me:$LINENO: checking $ac_header presence" >&5
+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+#line $LINENO "configure"
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <$ac_header>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  ac_header_preproc=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+rm -f conftest.err conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc in
+  yes:no )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    (
+      cat <<\_ASBOX
+## ------------------------------------ ##
+## Report this to bug-autoconf () gnu org. ##
+## ------------------------------------ ##
+_ASBOX
+    ) |
+      sed "s/^/$as_me: WARNING:     /" >&2
+    ;;
+  no:yes )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    (
+      cat <<\_ASBOX
+## ------------------------------------ ##
+## Report this to bug-autoconf () gnu org. ##
+## ------------------------------------ ##
+_ASBOX
+    ) |
+      sed "s/^/$as_me: WARNING:     /" >&2
+    ;;
+esac
+echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  eval "$as_ac_Header=$ac_header_preproc"
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+
 
 
 
diff -upr tcpdump-3.8.3/configure.in tcpdump-3.8.3-cap/configure.in
--- tcpdump-3.8.3/configure.in  2004-03-28 13:04:48.000000000 -0800
+++ tcpdump-3.8.3-cap/configure.in      2004-06-22 16:55:39.000000000 -0700
@@ -739,6 +739,9 @@ if test "$ac_cv_ssleay_path" != no; then
 fi
 ])
 
+AC_CHECK_LIB(cap, cap_init)
+AC_CHECK_HEADERS(sys/capability.h)
+
 AC_SUBST(V_CCOPT)
 AC_SUBST(V_DEFS)
 AC_SUBST(V_GROUP)
diff -upr tcpdump-3.8.3/tcpdump.c tcpdump-3.8.3-cap/tcpdump.c
--- tcpdump-3.8.3/tcpdump.c     2004-03-17 11:47:48.000000000 -0800
+++ tcpdump-3.8.3-cap/tcpdump.c 2004-06-23 11:32:41.000000000 -0700
@@ -62,6 +62,10 @@ extern int SIZE_BUF;
 #include <stdlib.h>
 #include <string.h>
 
+#ifdef HAVE_SYS_CAPABILITY_H
+#include <sys/capability.h>
+#endif /* HAVE_SYS_CAPABILITY_H */
+
 #include "interface.h"
 #include "addrtoname.h"
 #include "machdep.h"
@@ -327,6 +331,27 @@ main(int argc, char **argv)
        if(wsockinit() != 0) return 1;
 #endif /* WIN32 */
 
+#ifdef HAVE_LIBCAP
+        /* 
+         * If we're running as root, drop all capabilities
+         * not needed by the program.
+         */
+        if (geteuid() == 0) {
+                cap_t caps = cap_init();
+                static cap_value_t capv[] = {CAP_NET_ADMIN, CAP_NET_RAW};
+                const int numcaps = sizeof(capv) / sizeof(capv[0]);
+                if (caps == NULL)
+                        error("cap_init() failed; errno = %d", errno);
+                if (cap_set_flag(caps, CAP_PERMITTED, numcaps, capv, CAP_SET) < 0)
+                        error("Could not set permitted capabilities; errno = %d", errno);
+                if (cap_set_flag(caps, CAP_EFFECTIVE, numcaps, capv, CAP_SET) < 0)
+                        error("Could not set effective capabilities; errno = %d", errno);
+                if (cap_set_proc(caps) < 0)
+                        error("Could not apply capability set; errno = %d", errno);
+                cap_free(caps);
+        }
+#endif /* HAVE_LIBCAP */
+
        cnt = -1;
        device = NULL;
        infile = NULL;
-
This is the tcpdump-workers list.
Visit https://lists.sandelman.ca/ to unsubscribe.


Current thread: