Index: Makefile.in =================================================================== --- Makefile.in (revision 33553) +++ Makefile.in (working copy) @@ -50,7 +50,7 @@ # CFLAGS = $(DEFS) $(INCLS) STATIC = LDFLAGS = @LDFLAGS@ $(DBGFLAGS) $(STATIC) -LIBS = @LIBNBASE_LIBS@ @LIBNSOCK_LIBS@ @LIBPCRE_LIBS@ @LIBPCAP_LIBS@ $(OPENSSL_LIBS) libnetutil/libnetutil.a @LIBDNET_LIBS@ @LIBLUA_LIBS@ @LIBLINEAR_LIBS@ @LIBS@ +LIBS = @LIBNBASE_LIBS@ @LIBNSOCK_LIBS@ @LIBPCRE_LIBS@ @LIBPCAP_LIBS@ @LIBCAP_LIBS@ $(OPENSSL_LIBS) libnetutil/libnetutil.a @LIBDNET_LIBS@ @LIBLUA_LIBS@ @LIBLINEAR_LIBS@ @LIBS@ OPENSSL_LIBS = @OPENSSL_LIBS@ # LIBS = -lefence @LIBS@ # LIBS = -lrmalloc @LIBS@ @@ -253,6 +253,12 @@ rm -f Makefile Makefile.bak makefile.dep nmap_config.h stamp-h stamp-h.in \ config.cache config.log config.status +setcap: + setcap cap_net_raw,cap_net_admin,cap_net_bind_service+eip nmap + +setcap-install: + setcap cap_net_raw,cap_net_admin,cap_net_bind_service+eip $(DESTDIR)$(bindir)/nmap + install-nmap: $(TARGET) $(INSTALL) -d $(DESTDIR)$(bindir) $(DESTDIR)$(mandir)/man1 $(DESTDIR)$(nmapdatadir) $(INSTALL) -c -m 755 nmap $(DESTDIR)$(bindir)/nmap Index: NmapOps.cc =================================================================== --- NmapOps.cc (revision 33553) +++ NmapOps.cc (working copy) @@ -140,6 +140,10 @@ } NmapOps::~NmapOps() { + if (drop_user) { + free(drop_user); + drop_user = NULL; + } if (xsl_stylesheet) { free(xsl_stylesheet); xsl_stylesheet = NULL; @@ -281,9 +285,14 @@ isr00t = 1; else if (getenv("NMAP_UNPRIVILEGED")) isr00t = 0; +#ifdef HAVE_LIBCAP + else if (has_capabilities()) + isr00t = 1; +#endif else isr00t = !(geteuid()); #endif + drop_user = NULL; have_pcap = true; debugging = 0; verbose = 0; Index: NmapOps.h =================================================================== --- NmapOps.h (revision 33553) +++ NmapOps.h (working copy) @@ -171,6 +171,7 @@ adjustments (quietly or with a warning to the user). */ int isr00t; + char *drop_user; /* User to drop privileges to, incase run as root */ /* Whether we have pcap functions (can be false on Windows). */ bool have_pcap; int debugging; Index: configure =================================================================== --- configure (revision 33553) +++ configure (working copy) @@ -672,6 +672,7 @@ PCAP_CLEAN PCAP_BUILD PCAP_DEPENDS +LIBCAP_LIBS OPENSSL_LIBS NPING_DIST_CLEAN NPING_CLEAN @@ -779,6 +780,7 @@ with_zenmap with_nping with_openssl +with_libcap with_libpcap with_libpcre with_libdnet @@ -1434,6 +1436,8 @@ --without-nping Skip installation of the Nping utility --with-openssl=DIR Use optional openssl libs and includes from [DIR]/lib/ and [DIR]/include/openssl/) + --with-libcap=DIR Use optional libcap libs and includes from + [DIR]/lib/ and [DIR]/include/) --with-libpcap=DIR Look for pcap in DIR/include and DIR/libs. --with-libpcap=included Always use version included with Nmap --with-libpcre=DIR Use an existing (compiled) pcre lib from DIR/include @@ -6169,6 +6173,107 @@ fi + +# We test whether they specified libcap desires explicitly +use_libcap="yes" +specialcapdir="" + +# Check whether --with-libcap was given. +if test "${with_libcap+set}" = set; then : + withval=$with_libcap; case "$with_libcap" in + yes) + ;; + no) + use_libcap="no" + ;; + *) + specialcapdir="$with_libcap" + CPPFLAGS="$CPPFLAGS -I$with_libcap/include" + LDFLAGS="$LDFLAGS -L$with_libcap/lib" + ;; + esac + +fi + + +# If they didn't specify it, we try to find it +if test "$use_libcap" = "yes" -a -z "$specialcapdir"; then + ac_fn_c_check_header_mongrel "$LINENO" "sys/capability.h" "ac_cv_header_sys_capability_h" "$ac_includes_default" +if test "x$ac_cv_header_sys_capability_h" = xyes; then : + +else + use_libcap="no" + if test "$with_cap" = "yes"; then + as_fn_error $? "libcap was explicitly requested but sys/capability.h was not found. Try the --with-libcap=DIR argument to give the location of libcap or run configure with --without-libcap." "$LINENO" 5 + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Failed to find sys/capability.h so libcap will not be used. If it is installed you can try the --with-libcap=DIR argument" >&5 +$as_echo "$as_me: WARNING: Failed to find sys/capability.h so libcap will not be used. If it is installed you can try the --with-libcap=DIR argument" >&2;} + +fi + + + +# use_libcap="yes" given explicitly in next rule to avoid adding lib to $LIBS + if test "$use_libcap" = "yes"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for cap_get_bound in -lcap" >&5 +$as_echo_n "checking for cap_get_bound in -lcap... " >&6; } +if ${ac_cv_lib_cap_cap_get_bound+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lcap $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char cap_get_bound (); +int +main () +{ +return cap_get_bound (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_cap_cap_get_bound=yes +else + ac_cv_lib_cap_cap_get_bound=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_cap_cap_get_bound" >&5 +$as_echo "$ac_cv_lib_cap_cap_get_bound" >&6; } +if test "x$ac_cv_lib_cap_cap_get_bound" = xyes; then : + use_libcap="yes" +else + use_libcap="no" + if test "$with_libcap" = "yes"; then + as_fn_error $? "libcap was explicitly requested but libcap was not found. Try the --with-libcap=DIR argument to give the location of libcap or run configure with --without-libcap." "$LINENO" 5 + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Failed to find libcap so libcap will not be used. If it is installed you can try the --with-libcap=DIR argument" >&5 +$as_echo "$as_me: WARNING: Failed to find libcap so libcap will not be used. If it is installed you can try the --with-libcap=DIR argument" >&2;} +fi + + fi +fi + +LIBCAP_LIBS= +if test "$use_libcap" = "yes"; then + +$as_echo "#define HAVE_LIBCAP 1" >>confdefs.h + + LIBCAP_LIBS="-lcap" +fi + + have_libpcap=no Index: configure.ac =================================================================== --- configure.ac (revision 33553) +++ configure.ac (working copy) @@ -365,6 +365,55 @@ AC_SUBST(OPENSSL_LIBS) +# We test whether they specified libcap desires explicitly +use_libcap="yes" +specialcapdir="" +AC_ARG_WITH(libcap, +AC_HELP_STRING([--with-libcap=DIR],[Use optional libcap libs and includes from [DIR]/lib/ and [DIR]/include/)]), +[ case "$with_libcap" in + yes) + ;; + no) + use_libcap="no" + ;; + *) + specialcapdir="$with_libcap" + CPPFLAGS="$CPPFLAGS -I$with_libcap/include" + LDFLAGS="$LDFLAGS -L$with_libcap/lib" + ;; + esac] +) + +# If they didn't specify it, we try to find it +if test "$use_libcap" = "yes" -a -z "$specialcapdir"; then + AC_CHECK_HEADER(sys/capability.h,, + [ use_libcap="no" + if test "$with_cap" = "yes"; then + AC_MSG_ERROR([libcap was explicitly requested but sys/capability.h was not found. Try the --with-libcap=DIR argument to give the location of libcap or run configure with --without-libcap.]) + fi + AC_MSG_WARN([Failed to find sys/capability.h so libcap will not be used. If it is installed you can try the --with-libcap=DIR argument]) + ]) + +# use_libcap="yes" given explicitly in next rule to avoid adding lib to $LIBS + if test "$use_libcap" = "yes"; then + AC_CHECK_LIB(cap, cap_get_bound, + [ use_libcap="yes" ], + [ use_libcap="no" + if test "$with_libcap" = "yes"; then + AC_MSG_ERROR([libcap was explicitly requested but libcap was not found. Try the --with-libcap=DIR argument to give the location of libcap or run configure with --without-libcap.]) + fi + AC_MSG_WARN([Failed to find libcap so libcap will not be used. If it is installed you can try the --with-libcap=DIR argument]) ]) + fi +fi + +LIBCAP_LIBS= +if test "$use_libcap" = "yes"; then + AC_DEFINE(HAVE_LIBCAP, 1, [We can check for capabilities and use them if possible]) + LIBCAP_LIBS="-lcap" +fi + +AC_SUBST(LIBCAP_LIBS) + dnl Check whether libpcap is already available have_libpcap=no Index: nbase/configure =================================================================== --- nbase/configure (revision 33553) +++ nbase/configure (working copy) @@ -3700,7 +3700,7 @@ done -for ac_header in string.h getopt.h strings.h sys/param.h sys/time.h unistd.h errno.h sys/select.h sys/types.h sys/socket.h netinet/in.h arpa/inet.h sys/stat.h netdb.h sys/wait.h fcntl.h sys/resource.h inttypes.h mach-o/dyld.h +for ac_header in string.h getopt.h strings.h sys/param.h sys/time.h unistd.h errno.h sys/select.h sys/types.h sys/socket.h netinet/in.h arpa/inet.h sys/stat.h netdb.h sys/wait.h fcntl.h sys/resource.h inttypes.h mach-o/dyld.h sys/prctl.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" Index: nbase/configure.ac =================================================================== --- nbase/configure.ac (revision 33553) +++ nbase/configure.ac (working copy) @@ -67,7 +67,7 @@ dnl Checks for header files. AC_HEADER_STDC -AC_CHECK_HEADERS( string.h getopt.h strings.h sys/param.h sys/time.h unistd.h errno.h sys/select.h sys/types.h sys/socket.h netinet/in.h arpa/inet.h sys/stat.h netdb.h sys/wait.h fcntl.h sys/resource.h inttypes.h mach-o/dyld.h) +AC_CHECK_HEADERS( string.h getopt.h strings.h sys/param.h sys/time.h unistd.h errno.h sys/select.h sys/types.h sys/socket.h netinet/in.h arpa/inet.h sys/stat.h netdb.h sys/wait.h fcntl.h sys/resource.h inttypes.h mach-o/dyld.h sys/prctl.h) AC_HEADER_TIME dnl A special check required for on Darwin. See dnl http://www.gnu.org/software/autoconf/manual/html_node/Header-Portability.html. Index: nbase/nbase_config.h.in =================================================================== --- nbase/nbase_config.h.in (revision 33553) +++ nbase/nbase_config.h.in (working copy) @@ -214,6 +214,8 @@ #undef HAVE_MACH_O_DYLD_H +#undef HAVE_SYS_PRCTL_H + #undef HAVE_RPC_TYPES_H #undef HAVE_SYS_STAT_H Index: nmap.cc =================================================================== --- nmap.cc (revision 33553) +++ nmap.cc (working copy) @@ -486,6 +486,7 @@ this->pre_max_retries = -1; this->pre_host_timeout = -1; this->iflist = false; + this->keep_capabilities = true; } // Pre-specified timing parameters. @@ -501,6 +502,7 @@ char *exclude_spec, *exclude_file; char *spoofSource; const char *spoofmac; + bool keep_capabilities; } delayed_options; struct tm *local_time; @@ -931,6 +933,7 @@ o.isr00t = 1; } else if (strcmp(long_options[option_index].name, "unprivileged") == 0) { o.isr00t = 0; + delayed_options.keep_capabilities = false; } else if (strcmp(long_options[option_index].name, "mtu") == 0) { o.fragscan = atoi(optarg); if (o.fragscan <= 0 || o.fragscan % 8 != 0) @@ -1420,6 +1423,9 @@ if (delayed_options.pre_host_timeout != -1) o.host_timeout = delayed_options.pre_host_timeout; + if (!o.drop_user) + o.drop_user = strdup(DEFAULT_DROP_USER); + if (o.osscan) { o.reference_FPs = parse_fingerprint_reference_file("nmap-os-db"); @@ -1629,6 +1635,12 @@ } o.exclude_spec = delayed_options.exclude_spec; + /* If process is actually running as root and the user has not told us to + not drop privileges, then we should drop the root privileges to improve + security */ + if (!geteuid() && strcmp(o.drop_user,"-") != 0) { + drop_privileges_to(o.drop_user, delayed_options.keep_capabilities); + } } int nmap_main(int argc, char *argv[]) { @@ -2955,6 +2967,60 @@ return "unknown"; } +#ifdef HAVE_LIBCAP +bool has_capabilities() { + bool has_all_capabilities = true; + + cap_t current_cap = cap_get_proc(); + cap_flag_value_t flag_value; + + for (int i = 0; i < NUM_OF_CAPABILITIES; i++) { + if (cap_get_flag(current_cap, CAPABILITY_LIST[i], CAP_EFFECTIVE, &flag_value) == -1) { + has_all_capabilities = false; // since cap_get_flag is unable to check for capabilities + if (o.debugging) + error("cap_get_flag returned error on CAPABILITY_LIST[%d]\n", i); + } + has_all_capabilities &= flag_value; + } + + cap_free(current_cap); + + return has_all_capabilities; +} +#endif + +void drop_privileges_to(const char *username, bool keep_capabilities) { + if (geteuid()) + assert(0); + +#ifdef HAVE_SYS_PRCTL_H + if (keep_capabilities) { + prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0); + } +#endif + + struct passwd *pw = getpwnam(username); + if (!pw) + fatal("Tried to drop privileges to unknown account %s", username); + if (setgid(pw->pw_gid) == -1) + fatal("Unable to setgid to %d", pw->pw_gid); + if (setuid(pw->pw_uid) == -1) + fatal("Unable to setuid to %d", pw->pw_uid); + +#ifdef HAVE_LIBCAP + if (keep_capabilities) { + cap_t caps = cap_get_proc(); + cap_set_flag(caps, CAP_EFFECTIVE, NUM_OF_CAPABILITIES, CAPABILITY_LIST, CAP_SET); + cap_set_flag(caps, CAP_INHERITABLE, NUM_OF_CAPABILITIES, CAPABILITY_LIST, CAP_SET); + cap_set_proc(caps); + cap_free(caps); + } +#endif + + if (o.debugging) + log_write(LOG_PLAIN, "Changed to UID/GID = %d/%d\n", getuid(), getgid()); +} + static char *executable_dir(const char *argv0) { char *path, *dir; Index: nmap.h =================================================================== --- nmap.h (revision 33553) +++ nmap.h (working copy) @@ -242,6 +242,14 @@ #include #endif +#ifdef HAVE_LIBCAP +#include +#endif + +#ifdef HAVE_SYS_PRCTL_H +#include +#endif + /*#include *//* defines struct arphdr needed for if_ether.h */ // #if HAVE_NET_IF_H // #ifndef NET_IF_H /* why doesn't OpenBSD do this?! */ @@ -418,6 +426,8 @@ # define recvfrom6_t int #endif +#define DEFAULT_DROP_USER "nobody" + /********************** LOCAL INCLUDES *****************************/ #include "global_structures.h" @@ -458,5 +468,13 @@ int nmap_fileexistsandisreadable(const char* pathname); int gather_logfile_resumption_state(char *fname, int *myargc, char ***myargv); +/* functions and constants related to privileges and capabilities */ +#ifdef HAVE_LIBCAP +bool has_capabilities(); +const cap_value_t CAPABILITY_LIST[] = {CAP_NET_RAW, CAP_NET_ADMIN, CAP_NET_BIND_SERVICE}; +const int NUM_OF_CAPABILITIES = sizeof(CAPABILITY_LIST)/sizeof(cap_value_t); +#endif +void drop_privileges_to(const char *username, bool keep_capabilities = false); + #endif /* NMAP_H */ Index: nmap_config.h.in =================================================================== --- nmap_config.h.in (revision 33553) +++ nmap_config.h.in (working copy) @@ -187,6 +187,8 @@ #undef HAVE_OPENSSL +#undef HAVE_LIBCAP + #undef STUPID_SOLARIS_CHECKSUM_BUG #undef SOLARIS_BPF_PCAP_CAPTURE