diff --git a/Makefile.am b/Makefile.am index 29435e10..f7c9de84 100644 --- a/Makefile.am +++ b/Makefile.am @@ -41,7 +41,7 @@ dist_man_MANS = mtr.8 mtr-packet.8 PATHFILES += man/mtr.8 man/mtr-packet.8 install-exec-hook: - `setcap cap_net_raw+ep $(DESTDIR)$(sbindir)/mtr-packet` \ + `setcap cap_net_raw+p $(DESTDIR)$(sbindir)/mtr-packet` \ || chmod u+s $(DESTDIR)$(sbindir)/mtr-packet mtr_SOURCES = ui/mtr.c ui/mtr.h \ diff --git a/SECURITY b/SECURITY index 82c697c4..8714fe80 100644 --- a/SECURITY +++ b/SECURITY @@ -22,7 +22,7 @@ On Linux, privileges are known as capabilities. The only additional capability that mtr-packet needs is cap_net_raw. To give this capability to the mtr-packet binary, run the following command as root: -# setcap cap_net_raw+ep mtr-packet +# setcap cap_net_raw+p mtr-packet 2. Run mtr as the root user. diff --git a/configure.ac b/configure.ac index ad813322..450b32d8 100644 --- a/configure.ac +++ b/configure.ac @@ -67,6 +67,7 @@ AC_CHECK_HEADERS([ \ AC_CHECK_FUNCS([ \ __fpending \ fcntl \ + prctl \ ]) AC_CHECK_FUNC([error], [with_error=no], @@ -113,6 +114,11 @@ AM_CONDITIONAL([WITH_CURSES], [test "x$with_ncurses" = xyes]) AC_CHECK_LIB([cap], [cap_set_proc], [have_cap="yes"], AS_IF([test "$host_os" = linux-gnu], AC_MSG_WARN([Capabilities support is strongly recommended for increased security. See SECURITY for more information.]))) +AS_IF([test "x$have_cap" = "xyes"], [ + AC_DEFINE([HAVE_LIBCAP], [1], [Define when libcap is in use]) + AC_SUBST([CAP_LIBS], [-lcap]) +]) + # Enable ipinfo AC_ARG_WITH([ipinfo], diff --git a/packet/packet.c b/packet/packet.c index a8c2b6e3..ca951a3f 100644 --- a/packet/packet.c +++ b/packet/packet.c @@ -33,6 +33,13 @@ #include #endif +#ifdef HAVE_PRCTL +# include +# ifndef PR_SET_NO_NEW_PRIVS +# define PR_SET_NO_NEW_PRIVS 38 +# endif +#endif + #include "wait.h" /* Drop SUID privileges. To be used after accquiring raw sockets. */ @@ -69,6 +76,11 @@ int drop_elevated_permissions( return -1; } #endif +#ifdef HAVE_PRCTL + if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { + return -1; + } +#endif return 0; } diff --git a/packet/probe_unix.c b/packet/probe_unix.c index 363930f1..6460a12d 100644 --- a/packet/probe_unix.c +++ b/packet/probe_unix.c @@ -35,6 +35,10 @@ #include #include +#ifdef HAVE_LIBCAP +# include +#endif + #include "platform.h" #include "protocols.h" #include "sockaddr.h" @@ -402,6 +406,43 @@ int open_ip6_sockets_dgram( } #endif +#ifdef HAVE_LIBCAP +static int modify_capability(cap_value_t cap, cap_flag_value_t on) +{ + cap_t cap_p = cap_get_proc(); + cap_flag_value_t cap_ok; + int rc = -1; + + if (!cap_p) { + error(0, errno, "cap_get_proc"); + goto out; + } + + cap_ok = CAP_CLEAR; + cap_get_flag(cap_p, cap, CAP_PERMITTED, &cap_ok); + if (cap_ok == CAP_CLEAR) { + rc = on ? -1 : 0; + goto out; + } + + cap_set_flag(cap_p, CAP_EFFECTIVE, 1, &cap, on); + + if (cap_set_proc(cap_p) < 0) { + error(0, errno, "cap_set_proc"); + goto out; + } + + cap_free(cap_p); + cap_p = NULL; + + rc = 0; + out: + if (cap_p) + cap_free(cap_p); + return rc; +} +#endif + /* The first half of the net state initialization. Since this happens with elevated privileges, this is kept as minimal @@ -417,6 +458,9 @@ void init_net_state_privileged( net_state->platform.next_sequence = MIN_PORT; +#ifdef HAVE_LIBCAP + modify_capability(CAP_NET_RAW, CAP_SET); +#endif if (open_ip4_sockets_raw(net_state)) { #ifdef HAVE_LINUX_ERRQUEUE_H /* fall back to using unprivileged sockets */ @@ -433,7 +477,9 @@ void init_net_state_privileged( } #endif } - +#ifdef HAVE_LIBCAP + modify_capability(CAP_NET_RAW, CAP_CLEAR); +#endif /* If we couldn't open either IPv4 or IPv6 sockets, we can't do much, so print errors and exit.