From: Arran Cudbard-Bell Date: Mon, 3 Nov 2014 20:00:04 +0000 (-0500) Subject: Link to libcap to determine process capabilities X-Git-Tag: branch_3_1_x~4782 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ef036dfb44761288e4fa59d16ca249bd4be5b171;p=thirdparty%2Ffreeradius-server.git Link to libcap to determine process capabilities This lets us know whether the debugger attach check is failing because we don't have CAP_SYS_PTRACE or because there really is another debugger attached already. --- diff --git a/configure b/configure index 70dc00d678e..61f9213671c 100755 --- a/configure +++ b/configure @@ -763,6 +763,8 @@ with_pcap_lib_dir with_pcap_include_dir with_collectdclient_lib_dir with_collectdclient_include_dir +with_cap_lib_dir +with_cap_include_dir with_execinfo_lib_dir with_execinfo_include_dir with_pcre_lib_dir @@ -1448,6 +1450,9 @@ Optional Packages: --with-collectdclient-include-dir=DIR directory in which to look for collectdclient include files + --with-cap-lib-dir=DIR directory in which to look for cap library files + --with-cap-include-dir=DIR + directory in which to look for cap include files --with-execinfo-lib-dir=DIR directory in which to look for execinfo library files @@ -4390,7 +4395,7 @@ else We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) +#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; @@ -4436,7 +4441,7 @@ else We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) +#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; @@ -4460,7 +4465,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) +#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; @@ -4505,7 +4510,7 @@ else We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) +#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; @@ -4529,7 +4534,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) +#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; @@ -6898,6 +6903,225 @@ else fi LIBS="${old_LIBS}" +cap_lib_dir= + +# Check whether --with-cap-lib-dir was given. +if test "${with_cap_lib_dir+set}" = set; then : + withval=$with_cap_lib_dir; case "$withval" in + no) + as_fn_error $? "Need cap-lib-dir" "$LINENO" 5 + ;; + yes) + ;; + *) + cap_lib_dir="$withval" + ;; + esac +fi + + +cap_include_dir= + +# Check whether --with-cap-include-dir was given. +if test "${with_cap_include_dir+set}" = set; then : + withval=$with_cap_include_dir; case "$withval" in + no) + as_fn_error $? "Need cap-include-dir" "$LINENO" 5 + ;; + yes) + ;; + *) + cap_include_dir="$withval" + ;; + esac +fi + + +smart_try_dir="$cap_lib_dir" + + +sm_lib_safe=`echo "cap" | sed 'y%./+-%__p_%'` +sm_func_safe=`echo "cap_get_proc" | sed 'y%./+-%__p_%'` + +old_LIBS="$LIBS" +old_CPPFLAGS="$CPPFLAGS" +smart_lib= +smart_ldflags= +smart_lib_dir= + +if test "x$smart_try_dir" != "x"; then + for try in $smart_try_dir; do + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for cap_get_proc in -lcap in $try" >&5 +$as_echo_n "checking for cap_get_proc in -lcap in $try... " >&6; } + LIBS="-lcap $old_LIBS" + CPPFLAGS="-L$try -Wl,-rpath,$try $old_CPPFLAGS" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +extern char cap_get_proc(); +int +main () +{ +cap_get_proc() + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + + smart_lib="-lcap" + smart_ldflags="-L$try -Wl,-rpath,$try" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + break + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + done + LIBS="$old_LIBS" + CPPFLAGS="$old_CPPFLAGS" +fi + +if test "x$smart_lib" = "x"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for cap_get_proc in -lcap" >&5 +$as_echo_n "checking for cap_get_proc in -lcap... " >&6; } + LIBS="-lcap $old_LIBS" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +extern char cap_get_proc(); +int +main () +{ +cap_get_proc() + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + + smart_lib="-lcap" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LIBS="$old_LIBS" +fi + +if test "x$smart_lib" = "x"; then + + +if test "x$LOCATE" != "x"; then + DIRS= + file=libcap${libltdl_cv_shlibext} + + for x in `${LOCATE} $file 2>/dev/null`; do + base=`echo $x | sed "s%/${file}%%"` + if test "x$x" = "x$base"; then + continue; + fi + + dir=`${DIRNAME} $x 2>/dev/null` + exclude=`echo ${dir} | ${GREP} /home` + if test "x$exclude" != "x"; then + continue + fi + + already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}` + if test "x$already" = "x"; then + DIRS="$DIRS $dir" + fi + done +fi + +eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\"" + + + +if test "x$LOCATE" != "x"; then + DIRS= + file=libcap.a + + for x in `${LOCATE} $file 2>/dev/null`; do + base=`echo $x | sed "s%/${file}%%"` + if test "x$x" = "x$base"; then + continue; + fi + + dir=`${DIRNAME} $x 2>/dev/null` + exclude=`echo ${dir} | ${GREP} /home` + if test "x$exclude" != "x"; then + continue + fi + + already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}` + if test "x$already" = "x"; then + DIRS="$DIRS $dir" + fi + done +fi + +eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\"" + + + for try in $smart_lib_dir /usr/local/lib /opt/lib; do + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for cap_get_proc in -lcap in $try" >&5 +$as_echo_n "checking for cap_get_proc in -lcap in $try... " >&6; } + LIBS="-lcap $old_LIBS" + CPPFLAGS="-L$try -Wl,-rpath,$try $old_CPPFLAGS" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +extern char cap_get_proc(); +int +main () +{ +cap_get_proc() + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + + smart_lib="-lcap" + smart_ldflags="-L$try -Wl,-rpath,$try" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + break + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + done + LIBS="$old_LIBS" + CPPFLAGS="$old_CPPFLAGS" +fi + +if test "x$smart_lib" != "x"; then + eval "ac_cv_lib_${sm_lib_safe}_${sm_func_safe}=yes" + LIBS="$smart_ldflags $smart_lib $old_LIBS" + SMART_LIBS="$smart_ldflags $smart_lib $SMART_LIBS" +fi + +if test "x$ac_cv_lib_cap_cap_get_proc" != "xyes"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cap library not found, debugger checks will not be enabled. Use --with-cap-lib-dir=." >&5 +$as_echo "$as_me: WARNING: cap library not found, debugger checks will not be enabled. Use --with-cap-lib-dir=." >&2;} +else + +$as_echo "#define HAVE_LIBCAP 1" >>confdefs.h + + HAVE_LIBCAP=1 +fi + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a readline compatible library" >&5 $as_echo_n "checking for a readline compatible library... " >&6; } @@ -8941,6 +9165,247 @@ $as_echo "$as_me: WARNING: collectdclient headers not found. Use --with-collectd fi fi +if test "x$HAVE_LIBCAP" = x; then + { $as_echo "$as_me:${as_lineno-$LINENO}: skipping test for cap.h." >&5 +$as_echo "$as_me: skipping test for cap.h." >&6;} +else + smart_try_dir="$cap_include_dir" + + +ac_safe=`echo "sys/capability.h" | sed 'y%./+-%__pm%'` +old_CPPFLAGS="$CPPFLAGS" +smart_include= +smart_include_dir="/usr/local/include /opt/include" + +_smart_try_dir= +_smart_include_dir= + +for _prefix in $smart_prefix ""; do + for _dir in $smart_try_dir; do + _smart_try_dir="${_smart_try_dir} ${_dir}/${_prefix}" + done + + for _dir in $smart_include_dir; do + _smart_include_dir="${_smart_include_dir} ${_dir}/${_prefix}" + done +done + +if test "x$_smart_try_dir" != "x"; then + for try in $_smart_try_dir; do + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sys/capability.h in $try" >&5 +$as_echo_n "checking for sys/capability.h in $try... " >&6; } + CPPFLAGS="-isystem $try $old_CPPFLAGS" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + #include +int +main () +{ +int a = 1; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + + smart_include="-isystem $try" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + break + +else + + smart_include= + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + done + CPPFLAGS="$old_CPPFLAGS" +fi + +if test "x$smart_include" = "x"; then + for _prefix in $smart_prefix; do + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${_prefix}/sys/capability.h" >&5 +$as_echo_n "checking for ${_prefix}/sys/capability.h... " >&6; } + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + #include +int +main () +{ +int a = 1; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + + smart_include="-isystem ${_prefix}/" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + break + +else + + smart_include= + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + done +fi + +if test "x$smart_include" = "x"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sys/capability.h" >&5 +$as_echo_n "checking for sys/capability.h... " >&6; } + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + #include +int +main () +{ +int a = 1; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + + smart_include=" " + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + break + +else + + smart_include= + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi + +if test "x$smart_include" = "x"; then + + for prefix in $smart_prefix; do + + +if test "x$LOCATE" != "x"; then + DIRS= + file="${_prefix}/${1}" + + for x in `${LOCATE} $file 2>/dev/null`; do + base=`echo $x | sed "s%/${file}%%"` + if test "x$x" = "x$base"; then + continue; + fi + + dir=`${DIRNAME} $x 2>/dev/null` + exclude=`echo ${dir} | ${GREP} /home` + if test "x$exclude" != "x"; then + continue + fi + + already=`echo \$_smart_include_dir ${DIRS} | ${GREP} ${dir}` + if test "x$already" = "x"; then + DIRS="$DIRS $dir" + fi + done +fi + +eval "_smart_include_dir=\"\$_smart_include_dir $DIRS\"" + + done + + +if test "x$LOCATE" != "x"; then + DIRS= + file=sys/capability.h + + for x in `${LOCATE} $file 2>/dev/null`; do + base=`echo $x | sed "s%/${file}%%"` + if test "x$x" = "x$base"; then + continue; + fi + + dir=`${DIRNAME} $x 2>/dev/null` + exclude=`echo ${dir} | ${GREP} /home` + if test "x$exclude" != "x"; then + continue + fi + + already=`echo \$_smart_include_dir ${DIRS} | ${GREP} ${dir}` + if test "x$already" = "x"; then + DIRS="$DIRS $dir" + fi + done +fi + +eval "_smart_include_dir=\"\$_smart_include_dir $DIRS\"" + + + for try in $_smart_include_dir; do + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sys/capability.h in $try" >&5 +$as_echo_n "checking for sys/capability.h in $try... " >&6; } + CPPFLAGS="-isystem $try $old_CPPFLAGS" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + #include +int +main () +{ +int a = 1; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + + smart_include="-isystem $try" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + break + +else + + smart_include= + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + done + CPPFLAGS="$old_CPPFLAGS" +fi + +if test "x$smart_include" != "x"; then + eval "ac_cv_header_$ac_safe=yes" + CPPFLAGS="$smart_include $old_CPPFLAGS" + SMART_CPPFLAGS="$smart_include $SMART_CPPFLAGS" +fi + +smart_prefix= + + if test "x$ac_cv_header_sys_capability_h" == "xyes"; then + +$as_echo "#define HAVE_CAPABILITY_H 1" >>confdefs.h + + else + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cap headers not found, will not perform debugger checks. Use --with-cap-include-dir=." >&5 +$as_echo "$as_me: WARNING: cap headers not found, will not perform debugger checks. Use --with-cap-include-dir=." >&2;} + fi +fi + ac_fn_c_check_type "$LINENO" "off_t" "ac_cv_type_off_t" "$ac_includes_default" if test "x$ac_cv_type_off_t" = xyes; then : diff --git a/configure.ac b/configure.ac index 3daca299f9d..c9ba53e0fe3 100644 --- a/configure.ac +++ b/configure.ac @@ -887,6 +887,50 @@ fi dnl Set by FR_SMART_CHECKLIB LIBS="${old_LIBS}" +dnl Check for cap +dnl extra argument: --with-cap-lib-dir=DIR +cap_lib_dir= +AC_ARG_WITH(cap-lib-dir, + [AS_HELP_STRING([--with-cap-lib-dir=DIR], + [directory in which to look for cap library files])], + [case "$withval" in + no) + AC_MSG_ERROR([Need cap-lib-dir]) + ;; + yes) + ;; + *) + cap_lib_dir="$withval" + ;; + esac]) + +dnl extra argument: --with-cap-include-dir=DIR +cap_include_dir= +AC_ARG_WITH(cap-include-dir, + [AS_HELP_STRING([--with-cap-include-dir=DIR], + [directory in which to look for cap include files])], + [case "$withval" in + no) + AC_MSG_ERROR([Need cap-include-dir]) + ;; + yes) + ;; + *) + cap_include_dir="$withval" + ;; + esac]) + +smart_try_dir="$cap_lib_dir" +FR_SMART_CHECK_LIB(cap, cap_get_proc) +if test "x$ac_cv_lib_cap_cap_get_proc" != "xyes"; then + AC_MSG_WARN([cap library not found, debugger checks will not be enabled. Use --with-cap-lib-dir=.]) +else + AC_DEFINE(HAVE_LIBCAP, 1, + [Define to 1 if you have the `cap' library (-lcap).] + ) + HAVE_LIBCAP=1 +fi + VL_LIB_READLINE dnl ############################################################# @@ -1151,6 +1195,24 @@ else fi fi +dnl # +dnl # Check the CAP includes for debugger checks +dnl # +if test "x$HAVE_LIBCAP" = x; then + AC_MSG_NOTICE([skipping test for cap.h.]) +else + dnl # + dnl # Check for CAP header files + dnl # + smart_try_dir="$cap_include_dir" + FR_SMART_CHECK_INCLUDE([sys/capability.h]) + if test "x$ac_cv_header_sys_capability_h" == "xyes"; then + AC_DEFINE(HAVE_CAPABILITY_H, 1, [Define to 1 if you have the header file.]) + else + AC_MSG_WARN([cap headers not found, will not perform debugger checks. Use --with-cap-include-dir=.]) + fi +fi + dnl ############################################################# dnl # dnl # 4. Checks for typedefs diff --git a/src/include/autoconf.h.in b/src/include/autoconf.h.in index f5000491b6a..85ab10032a9 100644 --- a/src/include/autoconf.h.in +++ b/src/include/autoconf.h.in @@ -33,6 +33,9 @@ /* Define if the compiler supports __builtin_types_compatible_p */ #undef HAVE_BUILTIN_TYPES_COMPATIBLE_P +/* Define to 1 if you have the header file. */ +#undef HAVE_CAPABILITY_H + /* Define to 1 if you have the `closefrom' function. */ #undef HAVE_CLOSEFROM @@ -146,6 +149,9 @@ /* define if you have IP_PKTINFO (Linux) */ #undef HAVE_IP_PKTINFO +/* 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 diff --git a/src/include/libradius.h b/src/include/libradius.h index a9cf09530f4..81ea3247d12 100644 --- a/src/include/libradius.h +++ b/src/include/libradius.h @@ -767,11 +767,18 @@ void fr_cbuff_rp_insert(fr_cbuff_t *cbuff, void *obj); void *fr_cbuff_rp_next(fr_cbuff_t *cbuff, TALLOC_CTX *ctx); /* debug.c */ - +typedef enum { + DEBUG_STATE_UNKNOWN_NO_PTRACE = -3, //!< We don't have ptrace so can't check. + DEBUG_STATE_UNKNOWN_NO_PTRACE_CAP = -2, //!< CAP_SYS_PTRACE not set for the process. + DEBUG_STATE_UNKNOWN = -1, //!< Unknown, likely fr_get_debug_state() not called yet. + DEBUG_STATE_NOT_ATTACHED = 0, //!< We can attach, so a debugger must not be. + DEBUG_STATE_ATTACHED = 1 //!< We can't attach, it's likely a debugger is already tracing. +} fr_debug_state_t; #define FR_FAULT_LOG(fmt, ...) fr_fault_log(fmt "\n", ## __VA_ARGS__) typedef void (*fr_fault_log_t)(char const *msg, ...) CC_HINT(format (printf, 1, 2)); extern fr_fault_log_t fr_fault_log; +extern fr_debug_state_t fr_debug_state; /** Optional callback passed to fr_fault_setup * @@ -785,6 +792,8 @@ extern fr_fault_log_t fr_fault_log; typedef int (*fr_fault_cb_t)(int signum); typedef struct fr_bt_marker fr_bt_marker_t; +void fr_store_debug_state(void); +char const *fr_debug_state_to_msg(fr_debug_state_t state); void fr_debug_break(void); void backtrace_print(fr_cbuff_t *cbuff, void *obj); int fr_backtrace_do(fr_bt_marker_t *marker); diff --git a/src/lib/debug.c b/src/lib/debug.c index c8f6d77062c..780e560d230 100644 --- a/src/lib/debug.c +++ b/src/lib/debug.c @@ -98,7 +98,7 @@ static void CC_HINT(format (printf, 1, 2)) _fr_fault_log(char const *msg, ...); fr_fault_log_t fr_fault_log = _fr_fault_log; //!< Function to use to process logging output. static int fr_fault_log_fd = STDERR_FILENO; //!< Where to write debug output. -static int debugger_attached = -1; //!< Whether were attached to by a debugger. +fr_debug_state_t fr_debug_state = DEBUG_STATE_UNKNOWN; //!< Whether were attached to by a debugger. #ifdef HAVE_SYS_RESOURCE_H static struct rlimit core_limits; @@ -114,30 +114,76 @@ static TALLOC_CTX *talloc_autofree_ctx; # define _PTRACE(_x, _y) ptrace(_x, _y, NULL, 0) # endif +# ifdef HAVE_CAPABILITY_H +# include +# endif + /** Determine if we're running under a debugger by attempting to attach using pattach * - * @return 0 if we're not, EPERM if we are, -1 if we can't tell. + * @return 0 if we're not, 1 if we are, -1 if we can't tell because of an error, + * -2 if we can't tell because we don't have the CAP_SYS_PTRACE capability. */ -static int fr_debugger_attached(void) +static int fr_get_debug_state(void) { int pid; int from_child[2] = {-1, -1}; +#ifdef HAVE_CAPABILITY_H + cap_flag_value_t value; + cap_t current; + + /* + * If we're running under linux, we first need to check if we have + * permission to to ptrace. We do that using the capabilities + * functions. + */ + current = cap_get_proc(); + if (!current) { + fr_strerror_printf("Failed getting process capabilities: %s", fr_syserror(errno)); + return DEBUG_STATE_UNKNOWN; + } + + if (cap_get_flag(current, CAP_SYS_PTRACE, CAP_PERMITTED, &value) < 0) { + fr_strerror_printf("Failed getting permitted ptrace capability state: %s", + fr_syserror(errno)); + cap_free(current); + return DEBUG_STATE_UNKNOWN; + } + + if ((value == CAP_SET) && (cap_get_flag(current, CAP_SYS_PTRACE, CAP_EFFECTIVE, &value) < 0)) { + fr_strerror_printf("Failed getting effective ptrace capability state: %s", + fr_syserror(errno)); + cap_free(current); + return DEBUG_STATE_UNKNOWN; + } + + /* + * We don't have permission to ptrace, so this test will always fail. + */ + if (value == CAP_CLEAR) { + fr_strerror_printf("ptrace capability not set. If debugger detection is required run as root or: " + "setcap cap_sys_ptrace+ep "); + cap_free(current); + return DEBUG_STATE_UNKNOWN_NO_PTRACE_CAP; + } + cap_free(current); +#endif + if (pipe(from_child) < 0) { - fr_strerror_printf("Debugger check failed: Error opening internal pipe: %s", fr_syserror(errno)); - return -1; + fr_strerror_printf("Error opening internal pipe: %s", fr_syserror(errno)); + return DEBUG_STATE_UNKNOWN; } pid = fork(); if (pid == -1) { - fr_strerror_printf("Debugger check failed: Error forking: %s", fr_syserror(errno)); - return -1; + fr_strerror_printf("Error forking: %s", fr_syserror(errno)); + return DEBUG_STATE_UNKNOWN; } /* Child */ if (pid == 0) { - int ret = 0; + int8_t ret = DEBUG_STATE_NOT_ATTACHED; int ppid = getppid(); /* Close parent's side */ @@ -165,7 +211,7 @@ static int fr_debugger_attached(void) exit(0); } - ret = errno; + ret = DEBUG_STATE_ATTACHED; /* Tell the parent what happened */ if (write(from_child[1], &ret, sizeof(ret)) < 0) { fprintf(stderr, "Writing ptrace status to parent failed: %s", fr_syserror(errno)); @@ -174,7 +220,7 @@ static int fr_debugger_attached(void) exit(0); /* Parent */ } else { - int ret = -1; + int8_t ret = DEBUG_STATE_UNKNOWN; /* * The child writes errno (reason) if pattach failed else 0. @@ -184,14 +230,6 @@ static int fr_debugger_attached(void) */ while ((read(from_child[0], &ret, sizeof(ret)) < 0) && (errno == EINTR)); - /* Ret not updated */ - if (ret < 0) { - fr_strerror_printf("Debugger check failed: Error getting status from child: %s", - fr_syserror(errno)); - } else { - fr_strerror_printf("Failed attaching to process: %s", fr_syserror(errno)); - } - /* Close the pipes here (if we did it above, it might race with pattach) */ close(from_child[1]); close(from_child[0]); @@ -203,14 +241,60 @@ static int fr_debugger_attached(void) } } #else -static int fr_debugger_attached(void) +static int fr_get_debug_state(void) { - fr_strerror_printf("Debugger check failed: PTRACE not available"); + fr_strerror_printf("PTRACE not available"); - return -1; + return DEBUG_STATE_UNKNOWN_NO_PTRACE; } #endif +/** Should be run before using setuid or setgid to get useful results + * + * @note sets the fr_debug_state global. + */ +void fr_store_debug_state(void) +{ + fr_debug_state = fr_get_debug_state(); + +#ifndef NDEBUG + /* + * There are many reasons why this might happen with + * a vanilla install, so we don't want to spam users + * with messages they won't understand and may not + * want to resolve. + */ + if (fr_debug_state < 0) fprintf(stderr, "Getting debug state failed: %s\n", fr_strerror()); +#endif +} + +/** Return current value of debug_state + * + * @param state to translate into a humanly readable value. + * @return humanly readable version of debug state. + */ +char const *fr_debug_state_to_msg(fr_debug_state_t state) +{ + switch (state) { + case DEBUG_STATE_UNKNOWN_NO_PTRACE: + return "Debug state unknown (ptrace functionality not available)"; + + case DEBUG_STATE_UNKNOWN_NO_PTRACE_CAP: + return "Debug state unknown (cap_sys_ptrace capability not set)"; + + case DEBUG_STATE_UNKNOWN: + return "Debug state unknown"; + + case DEBUG_STATE_ATTACHED: + return "Found debugger attached"; + + case DEBUG_STATE_NOT_ATTACHED: + return "Debugger not attached"; + } + + return ""; +} + /** Break in debugger (if were running under a debugger) * * If the server is running under a debugger this will raise a @@ -220,11 +304,8 @@ static int fr_debugger_attached(void) */ void fr_debug_break(void) { - if (debugger_attached == -1) { - debugger_attached = fr_debugger_attached(); - } - - if (debugger_attached == 1) { + if (fr_debug_state < 0) fr_debug_state = fr_get_debug_state(); + if (fr_debug_state == DEBUG_STATE_ATTACHED) { fprintf(stderr, "Debugger detected, raising SIGTRAP\n"); fflush(stderr); @@ -545,7 +626,7 @@ void fr_fault(int sig) * The only exception are SIGUSR1 and SIGUSR2 which print out various * debugging info, and should be allowed to continue. */ - if (debugger_attached && (sig != SIGUSR1) && (sig != SIGUSR2)) { + if (fr_debug_state && (sig != SIGUSR1) && (sig != SIGUSR2)) { FR_FAULT_LOG("RAISING SIGNAL: %s", strsignal(sig)); raise(sig); goto finish; @@ -802,8 +883,6 @@ int fr_fault_setup(char const *cmd, char const *program) /* Unsure what the side effects of changing the signal handler mid execution might be */ if (!setup) { - int ret; - /* * Setup the default logger */ @@ -813,15 +892,20 @@ int fr_fault_setup(char const *cmd, char const *program) /* * Figure out if we were started under a debugger */ - - ret = fr_debugger_attached(); + if (fr_debug_state < 0) fr_debug_state = fr_get_debug_state(); /* * These signals can't be properly dealt with in the debugger - * if we set our own signal handlers + * if we set our own signal handlers. */ - if (ret == 0) { - debugger_attached = 0; + switch (fr_debug_state) { + default: +#ifndef NDEBUG + FR_FAULT_LOG("Debugger check failed: %s", fr_strerror()); + FR_FAULT_LOG("Signal processing in debuggers may not work as expected"); +#endif + + case 0: #ifdef SIGABRT if (fr_set_signal(SIGABRT, fr_fault) < 0) return -1; @@ -840,12 +924,10 @@ int fr_fault_setup(char const *cmd, char const *program) #ifdef SIGSEGV if (fr_set_signal(SIGSEGV, fr_fault) < 0) return -1; #endif + break; - - } else { - debugger_attached = 1; - - FR_FAULT_LOG("Not enabling panic action signal handlers: %s", fr_strerror()); + case 1: + break; } #ifdef SIGUSR1 if (fr_set_signal(SIGUSR1, fr_fault) < 0) return -1; diff --git a/src/main/radiusd.c b/src/main/radiusd.c index bc285bd7a1f..75cd6e52cd3 100644 --- a/src/main/radiusd.c +++ b/src/main/radiusd.c @@ -106,17 +106,6 @@ int main(int argc, char *argv[]) */ TALLOC_CTX *autofree = talloc_init("main"); - /* - * If the server was built with debugging enabled always install - * the basic fatal signal handlers. - */ -#ifndef NDEBUG - if (fr_fault_setup(getenv("PANIC_ACTION"), argv[0]) < 0) { - fr_perror("radiusd"); - exit(EXIT_FAILURE); - } -#endif - #ifdef OSFC2 set_auth_parameters(argc,argv); #endif @@ -333,9 +322,14 @@ int main(int argc, char *argv[]) exit(EXIT_SUCCESS); } - if (debug_flag) { - version(); - } + if (debug_flag) version(); + + /* + * Under linux CAP_SYS_PTRACE is usually only available before + * setuid/setguid, so we need to check whether we can attach before + * calling those functions (in main_config_init()). + */ + fr_store_debug_state(); /* * Initialising OpenSSL once, here, is safer than having individual @@ -358,6 +352,9 @@ int main(int argc, char *argv[]) exit(EXIT_FAILURE); } + /* This is very useful in figuring out why the panic_action didn't fire */ + DEBUG("radiusd: #### %s ####", fr_debug_state_to_msg(fr_debug_state)); + /* Check for vulnerabilities in the version of libssl were linked against */ #ifdef HAVE_OPENSSL_CRYPTO_H #ifdef ENABLE_OPENSSL_VERSION_CHECK