From 980e0216ac12e89fe79797da43b513921e41e37b Mon Sep 17 00:00:00 2001 From: "Todd C. Miller" Date: Mon, 31 Jul 2023 13:46:57 -0600 Subject: [PATCH] Add basic support for 32-bit and 64-bit LD_PRELOAD equivalents. The noexec and intercept DSO settings may now include both a 32-bit DSO and a 64-bit DSO specified by a colon. For example: /usr/libexec/sudo/sudo_intercept.so:/usr/libexec/sudo/sudo_intercept_64.so. --- INSTALL.md | 32 +++-- config.h.in | 11 ++ configure | 273 ++++++++++++++++++++++++++--------------- configure.ac | 163 +++++++++++++++--------- docs/sudo.conf.man.in | 41 +++++-- docs/sudo.conf.mdoc.in | 41 +++++-- src/exec_preload.c | 73 +++++++++-- 7 files changed, 439 insertions(+), 195 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index 1bb3ce38ef..d354d2e0df 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -434,24 +434,30 @@ Defaults are listed in brackets after the description. This is also used to support the "log_subcmds" sudoers setting. For example, this means that for a shell run through sudo, the individual commands run by the shell are - also subject to rules in the sudoers file. See the - "Preventing Shell Escapes" section in the sudoers man page - for details. If specified, PATH should be a fully qualified - path name, e.g. /usr/local/libexec/sudo/sudo_intercept.so. - If PATH is "no", intercept support will not be compiled in. - The default is to compile intercept support if libtool - supports building shared objects on your system. + also subject to rules in the sudoers file. See the "Preventing + Shell Escapes" section in the sudoers man page for details. + If specified, PATH should either be a fully-qualified path + name such as /usr/local/libexec/sudo/sudo_intercept.so, or, + for AIX and Solaris systems, it may optionally be set to a + 32-bit shared library followed by a 64-bit shared library, + separated by a colon. If PATH is "no", intercept support + will not be compiled in. The default is to compile intercept + support if libtool supports building shared objects on your + system. --with-noexec[=PATH] Enable support for the "noexec" functionality which prevents a dynamically-linked program being run by sudo from executing another program (think shell escapes). See the "Preventing Shell Escapes" section in the sudoers man page for details. - If specified, PATH should be a fully qualified path name, - e.g. /usr/local/libexec/sudo/sudo_noexec.so. If PATH is - "no", noexec support will not be compiled in. The default - is to compile noexec support if libtool supports building - shared objects on your system. + If specified, PATH should either be a fully-qualified path + name such as /usr/local/libexec/sudo/sudo_noexec.so, or, + for AIX and Solaris systems, it may optionally be set to a + 32-bit shared library followed by a 64-bit shared library, + separated by a colon. If PATH is "no", noexec support + will not be compiled in. The default is to compile noexec + support if libtool supports building shared objects on your + system. --with-selinux Enable support for role based access control (RBAC) on systems @@ -836,7 +842,7 @@ Defaults are listed in brackets after the description. Sudoers option: exempt_group --with-fqdn - Define this if you want to put fully qualified host names in the sudoers + Define this if you want to put fully-qualified host names in the sudoers file. Ie: instead of myhost you would use myhost.mydomain.edu. You may still use the short form if you wish (and even mix the two). Beware that turning FQDN on requires sudo to make DNS lookups which may make diff --git a/config.h.in b/config.h.in index a89f527f60..043cf96d16 100644 --- a/config.h.in +++ b/config.h.in @@ -1257,6 +1257,14 @@ /* The environment variable that controls preloading of dynamic objects. */ #undef RTLD_PRELOAD_VAR +/* The environment variable that controls preloading of 32-bit dynamic + objects. */ +#undef RTLD_PRELOAD_VAR_32 + +/* The environment variable that controls preloading of 64-bit dynamic + objects. */ +#undef RTLD_PRELOAD_VAR_64 + /* The user sudo should run commands as by default. */ #undef RUNAS_DEFAULT @@ -1286,6 +1294,9 @@ /* The size of 'id_t', as computed by sizeof. */ #undef SIZEOF_ID_T +/* The size of 'long', as computed by sizeof. */ +#undef SIZEOF_LONG + /* The size of 'long long', as computed by sizeof. */ #undef SIZEOF_LONG_LONG diff --git a/configure b/configure index cd17399050..d4ddc9f929 100755 --- a/configure +++ b/configure @@ -3749,11 +3749,6 @@ shadow_funcs= shadow_libs= OS_INIT=os_init_common -RTLD_PRELOAD_VAR="LD_PRELOAD" -RTLD_PRELOAD_ENABLE_VAR= -RTLD_PRELOAD_DELIM=":" -RTLD_PRELOAD_DEFAULT= - @@ -17314,7 +17309,7 @@ esac fi INTERCEPTFILE="sudo_intercept.so" -INTERCEPTDIR="`echo $intercept_file|sed -e 's:^${\([^}]*\)}:$(\1):' -e 's:^\(.*\)/[^/]*:\1:'`" +INTERCEPTDIR="`echo $intercept_file|sed -e 's:^${\([^}]*\)}:$(\1):' -e 's:^\([^:]*\)/[^/].*$:\1:'`" # Check whether --with-noexec was given. @@ -17332,7 +17327,7 @@ esac fi NOEXECFILE="sudo_noexec.so" -NOEXECDIR="`echo $noexec_file|sed -e 's:^${\([^}]*\)}:$(\1):' -e 's:^\(.*\)/[^/]*:\1:'`" +NOEXECDIR="`echo $noexec_file|sed -e 's:^${\([^}]*\)}:$(\1):' -e 's:^\([^:]*\)/[^/].*$:\1:'`" # Extract the first word of "sha1sum", so it can be a program name with args. set dummy sha1sum; ac_word=$2 @@ -17590,9 +17585,6 @@ case "$host" in printf "%s\n" "#define PAM_SUN_CODEBASE 1" >>confdefs.h - # LD_PRELOAD is space-delimited - RTLD_PRELOAD_DELIM=" " - # illumos has a broken fmemopen(3) if test X"`uname -o 2>/dev/null`" = X"illumos"; then : ${ac_cv_func_fmemopen='no'} @@ -17663,13 +17655,6 @@ then : fi - # LDR_PRELOAD is only supported in AIX 5.3 and later - case "$OSREV" in - [1-4].*) with_noexec=no;; - 5.[1-2]*) with_noexec=no;; - *) RTLD_PRELOAD_VAR="LDR_PRELOAD";; - esac - # cfmakeraw is broken on AIX (and is not documented) : ${ac_cv_func_cfmakeraw='no'} @@ -17917,9 +17902,6 @@ fi shadow_funcs="getprpwnam dispcrypt" shadow_libs="-lsecurity" - # ":DEFAULT" must be appended to _RLD_LIST - RTLD_PRELOAD_VAR="_RLD_LIST" - RTLD_PRELOAD_DEFAULT="DEFAULT" : ${mansectsu='8'} : ${mansectform='4'} : ${mansectmisc='5'} @@ -18176,9 +18158,7 @@ printf "%s\n" "yes, fixing locally" >&6; } esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - # ":DEFAULT" must be appended to _RLD_LIST - RTLD_PRELOAD_VAR="_RLD_LIST" - RTLD_PRELOAD_DEFAULT="DEFAULT" + : ${mansectsu='8'} : ${mansectform='4'} : ${mansectmisc='5'} @@ -18256,9 +18236,7 @@ fi fi - # ":DEFAULT" must be appended to _RLD_LIST - RTLD_PRELOAD_VAR="_RLD_LIST" - RTLD_PRELOAD_DEFAULT="DEFAULT" + : ${mansectsu='1m'} : ${mansectform='4'} : ${mansectmisc='5'} @@ -18566,21 +18544,6 @@ fi : ${with_logincap='yes'} # Darwin has a broken poll(), Apple radar 3710161 : ${enable_poll='no'} - # Darwin 8 and above can interpose library symbols cleanly - if test $OSMAJOR -ge 8 -then : - - printf "%s\n" "#define HAVE___INTERPOSE 1" >>confdefs.h - - dlyld_interpose=yes - -else case e in #( - e) - RTLD_PRELOAD_ENABLE_VAR="DYLD_FORCE_FLAT_NAMESPACE" - ;; -esac -fi - RTLD_PRELOAD_VAR="DYLD_INSERT_LIBRARIES" # Build sudo_noexec.so as a shared library, not a module. # On Darwin, modules and shared libraries are incompatible. @@ -18742,8 +18705,6 @@ fi *-*-nextstep*) # lockf() is broken on the NeXT ac_cv_func_lockf=no - RTLD_PRELOAD_VAR="DYLD_INSERT_LIBRARIES" - RTLD_PRELOAD_ENABLE_VAR="DYLD_FORCE_FLAT_NAMESPACE" ;; *-*-*sysv4*) : ${mansectsu='1m'} @@ -18779,50 +18740,6 @@ fi ;; esac -if test X"$enable_intercept" = X"no" -then : - - intercept_file=disabled - -fi -if test X"$with_noexec" = X"no" -then : - - noexec_file=disabled - -fi -if test X"${intercept_file} ${noexec_file}" != X"disabled disabled" -then : - - cat >>confdefs.h <>confdefs.h <>confdefs.h <>confdefs.h <= 0]];'. # This bug is HP SR number 8606223364. -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of id_t" >&5 -printf %s "checking size of id_t... " >&6; } -if test ${ac_cv_sizeof_id_t+y} +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of long" >&5 +printf %s "checking size of long... " >&6; } +if test ${ac_cv_sizeof_long+y} then : printf %s "(cached) " >&6 else case e in #( - e) if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (id_t))" "ac_cv_sizeof_id_t" "$ac_includes_default" + e) if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (long))" "ac_cv_sizeof_long" "$ac_includes_default" then : else case e in #( - e) if test "$ac_cv_type_id_t" = yes; then + e) if test "$ac_cv_type_long" = yes; then { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} -as_fn_error 77 "cannot compute sizeof (id_t) +as_fn_error 77 "cannot compute sizeof (long) See 'config.log' for more details" "$LINENO" 5; } else - ac_cv_sizeof_id_t=0 + ac_cv_sizeof_long=0 fi ;; esac fi ;; esac fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_id_t" >&5 -printf "%s\n" "$ac_cv_sizeof_id_t" >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_long" >&5 +printf "%s\n" "$ac_cv_sizeof_long" >&6; } -printf "%s\n" "#define SIZEOF_ID_T $ac_cv_sizeof_id_t" >>confdefs.h +printf "%s\n" "#define SIZEOF_LONG $ac_cv_sizeof_long" >>confdefs.h # The cast to long int works around a bug in the HP C Compiler @@ -21079,6 +20996,41 @@ printf "%s\n" "$ac_cv_sizeof_long_long" >&6; } printf "%s\n" "#define SIZEOF_LONG_LONG $ac_cv_sizeof_long_long" >>confdefs.h +# The cast to long int works around a bug in the HP C Compiler +# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects +# declarations like 'int a3[[(sizeof (unsigned char)) >= 0]];'. +# This bug is HP SR number 8606223364. +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of id_t" >&5 +printf %s "checking size of id_t... " >&6; } +if test ${ac_cv_sizeof_id_t+y} +then : + printf %s "(cached) " >&6 +else case e in #( + e) if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (id_t))" "ac_cv_sizeof_id_t" "$ac_includes_default" +then : + +else case e in #( + e) if test "$ac_cv_type_id_t" = yes; then + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} +as_fn_error 77 "cannot compute sizeof (id_t) +See 'config.log' for more details" "$LINENO" 5; } + else + ac_cv_sizeof_id_t=0 + fi ;; +esac +fi + ;; +esac +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_id_t" >&5 +printf "%s\n" "$ac_cv_sizeof_id_t" >&6; } + + + +printf "%s\n" "#define SIZEOF_ID_T $ac_cv_sizeof_id_t" >>confdefs.h + + # The cast to long int works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects # declarations like 'int a3[[(sizeof (unsigned char)) >= 0]];'. @@ -21461,6 +21413,131 @@ fi esac fi +RTLD_PRELOAD_VAR="LD_PRELOAD" +if test $ac_cv_sizeof_long -eq 4; then + RTLD_PRELOAD_VAR_32="LD_PRELOAD" +else + RTLD_PRELOAD_VAR_64="LD_PRELOAD" +fi +RTLD_PRELOAD_ENABLE_VAR= +RTLD_PRELOAD_DELIM=":" +RTLD_PRELOAD_DEFAULT= + +case "$host" in + *-*-solaris2*) + # LD_PRELOAD is space-delimited + RTLD_PRELOAD_DELIM=" " + RTLD_PRELOAD_VAR_32="LD_PRELOAD_32" + RTLD_PRELOAD_VAR_64="LD_PRELOAD_64" + ;; + *-*-aix*) + # LDR_PRELOAD and LDR_PRELOAD64 are only supported on + # AIX 5.3 and above. + case "$OSREV" in + [1-4].*|5.[1-2]*) + with_noexec=no + ;; + *) + # AIX uses LDR_PRELOAD for 32-bit executables + # and LDR_PRELOAD64 for 64-bit executable. + RTLD_PRELOAD_VAR_32="LDR_PRELOAD" + RTLD_PRELOAD_VAR_64="LDR_PRELOAD64" + if test $ac_cv_sizeof_long -eq 4; then + RTLD_PRELOAD_VAR="LDR_PRELOAD" + else + RTLD_PRELOAD_VAR="LDR_PRELOAD64" + fi + ;; + esac + ;; + *-dec-osf*|*-*-hiuxmpp*|*-*-irix*) + # ":DEFAULT" must be appended to _RLD_LIST + RTLD_PRELOAD_VAR="_RLD_LIST" + RTLD_PRELOAD_DEFAULT="DEFAULT" + ;; + *-*-darwin*) + # Darwin 8 and above can interpose library symbols cleanly + if test $OSMAJOR -ge 8 +then : + + printf "%s\n" "#define HAVE___INTERPOSE 1" >>confdefs.h + + dlyld_interpose=yes + +else case e in #( + e) + RTLD_PRELOAD_ENABLE_VAR="DYLD_FORCE_FLAT_NAMESPACE" + ;; +esac +fi + RTLD_PRELOAD_VAR="DYLD_INSERT_LIBRARIES" + + # Build sudo_noexec.so as a shared library, not a module. + # On Darwin, modules and shared libraries are incompatible. + PRELOAD_MODULE= + ;; + *-*-nextstep*) + RTLD_PRELOAD_VAR="DYLD_INSERT_LIBRARIES" + RTLD_PRELOAD_ENABLE_VAR="DYLD_FORCE_FLAT_NAMESPACE" + ;; +esac + +if test X"$enable_intercept" = X"no" +then : + + intercept_file=disabled + +fi +if test X"$with_noexec" = X"no" +then : + + noexec_file=disabled + +fi +if test X"${intercept_file} ${noexec_file}" != X"disabled disabled" +then : + + cat >>confdefs.h <>confdefs.h <>confdefs.h <>confdefs.h <>confdefs.h <>confdefs.h </dev/null`" = X"illumos"; then : ${ac_cv_func_fmemopen='no'} @@ -1822,13 +1811,6 @@ case "$host" in with_netsvc="/etc/netsvc.conf" ]) - # LDR_PRELOAD is only supported in AIX 5.3 and later - case "$OSREV" in - [[1-4]].*) with_noexec=no;; - 5.[[1-2]]*) with_noexec=no;; - *) RTLD_PRELOAD_VAR="LDR_PRELOAD";; - esac - # cfmakeraw is broken on AIX (and is not documented) : ${ac_cv_func_cfmakeraw='no'} @@ -1890,9 +1872,6 @@ case "$host" in shadow_funcs="getprpwnam dispcrypt" shadow_libs="-lsecurity" - # ":DEFAULT" must be appended to _RLD_LIST - RTLD_PRELOAD_VAR="_RLD_LIST" - RTLD_PRELOAD_DEFAULT="DEFAULT" : ${mansectsu='8'} : ${mansectform='4'} : ${mansectmisc='5'} @@ -2017,9 +1996,7 @@ case "$host" in ]], [[return(0);]])], [AC_MSG_RESULT(no)], [AC_MSG_RESULT([yes, fixing locally]) sed 's:::g' < /usr/include/prot.h > prot.h ]) - # ":DEFAULT" must be appended to _RLD_LIST - RTLD_PRELOAD_VAR="_RLD_LIST" - RTLD_PRELOAD_DEFAULT="DEFAULT" + : ${mansectsu='8'} : ${mansectform='4'} : ${mansectmisc='5'} @@ -2037,9 +2014,7 @@ case "$host" in AS_IF([test "$OSMAJOR" -le 4], [ AC_CHECK_LIB([sun], [getpwnam], [LIBS="${LIBS} -lsun"]) ]) - # ":DEFAULT" must be appended to _RLD_LIST - RTLD_PRELOAD_VAR="_RLD_LIST" - RTLD_PRELOAD_DEFAULT="DEFAULT" + : ${mansectsu='1m'} : ${mansectform='4'} : ${mansectmisc='5'} @@ -2222,14 +2197,6 @@ case "$host" in : ${with_logincap='yes'} # Darwin has a broken poll(), Apple radar 3710161 : ${enable_poll='no'} - # Darwin 8 and above can interpose library symbols cleanly - AS_IF([test $OSMAJOR -ge 8], [ - AC_DEFINE(HAVE___INTERPOSE) - dlyld_interpose=yes - ], [ - RTLD_PRELOAD_ENABLE_VAR="DYLD_FORCE_FLAT_NAMESPACE" - ]) - RTLD_PRELOAD_VAR="DYLD_INSERT_LIBRARIES" # Build sudo_noexec.so as a shared library, not a module. # On Darwin, modules and shared libraries are incompatible. @@ -2259,8 +2226,6 @@ case "$host" in *-*-nextstep*) # lockf() is broken on the NeXT ac_cv_func_lockf=no - RTLD_PRELOAD_VAR="DYLD_INSERT_LIBRARIES" - RTLD_PRELOAD_ENABLE_VAR="DYLD_FORCE_FLAT_NAMESPACE" ;; *-*-*sysv4*) : ${mansectsu='1m'} @@ -2296,26 +2261,6 @@ case "$host" in ;; esac -dnl -dnl Library preloading to support NOEXEC -dnl -AS_IF([test X"$enable_intercept" = X"no"], [ - intercept_file=disabled -]) -AS_IF([test X"$with_noexec" = X"no"], [ - noexec_file=disabled -]) -AS_IF([test X"${intercept_file} ${noexec_file}" != X"disabled disabled"], [ - SUDO_DEFINE_UNQUOTED(RTLD_PRELOAD_VAR, "$RTLD_PRELOAD_VAR") - SUDO_DEFINE_UNQUOTED(RTLD_PRELOAD_DELIM, '$RTLD_PRELOAD_DELIM') - AS_IF([test -n "$RTLD_PRELOAD_DEFAULT"], [ - SUDO_DEFINE_UNQUOTED(RTLD_PRELOAD_DEFAULT, "$RTLD_PRELOAD_DEFAULT") - ]) - AS_IF([test -n "$RTLD_PRELOAD_ENABLE_VAR"], [ - SUDO_DEFINE_UNQUOTED(RTLD_PRELOAD_ENABLE_VAR, "$RTLD_PRELOAD_ENABLE_VAR") - ]) -]) - dnl dnl Check for mixing mutually exclusive and regular auth methods dnl @@ -2545,8 +2490,9 @@ AC_INCLUDES_DEFAULT SUDO_UID_T_LEN SUDO_SOCK_SA_LEN SUDO_SOCK_SIN_LEN -AC_CHECK_SIZEOF([id_t]) +AC_CHECK_SIZEOF([long]) AC_CHECK_SIZEOF([long long]) +AC_CHECK_SIZEOF([id_t]) AC_CHECK_SIZEOF([time_t]) AS_IF([test X"$ac_cv_header_utmps_h" = X"yes"], [ SUDO_CHECK_UTMP_MEMBERS([utmps]) @@ -2556,6 +2502,101 @@ AS_IF([test X"$ac_cv_header_utmps_h" = X"yes"], [ SUDO_CHECK_UTMP_MEMBERS([utmp]) ]) +dnl +dnl Default values for LD_PRELOAD and related settings. +dnl +RTLD_PRELOAD_VAR="LD_PRELOAD" +if test $ac_cv_sizeof_long -eq 4; then + RTLD_PRELOAD_VAR_32="LD_PRELOAD" +else + RTLD_PRELOAD_VAR_64="LD_PRELOAD" +fi +RTLD_PRELOAD_ENABLE_VAR= +RTLD_PRELOAD_DELIM=":" +RTLD_PRELOAD_DEFAULT= + +dnl +dnl System-specific LD_PRELOAD equivalents. +dnl The below tests rely on ac_cv_sizeof_long being defined. +dnl +case "$host" in + *-*-solaris2*) + # LD_PRELOAD is space-delimited + RTLD_PRELOAD_DELIM=" " + RTLD_PRELOAD_VAR_32="LD_PRELOAD_32" + RTLD_PRELOAD_VAR_64="LD_PRELOAD_64" + ;; + *-*-aix*) + # LDR_PRELOAD and LDR_PRELOAD64 are only supported on + # AIX 5.3 and above. + case "$OSREV" in + [[1-4]].*|5.[[1-2]]*) + with_noexec=no + ;; + *) + # AIX uses LDR_PRELOAD for 32-bit executables + # and LDR_PRELOAD64 for 64-bit executable. + RTLD_PRELOAD_VAR_32="LDR_PRELOAD" + RTLD_PRELOAD_VAR_64="LDR_PRELOAD64" + if test $ac_cv_sizeof_long -eq 4; then + RTLD_PRELOAD_VAR="LDR_PRELOAD" + else + RTLD_PRELOAD_VAR="LDR_PRELOAD64" + fi + ;; + esac + ;; + *-dec-osf*|*-*-hiuxmpp*|*-*-irix*) + # ":DEFAULT" must be appended to _RLD_LIST + RTLD_PRELOAD_VAR="_RLD_LIST" + RTLD_PRELOAD_DEFAULT="DEFAULT" + ;; + *-*-darwin*) + # Darwin 8 and above can interpose library symbols cleanly + AS_IF([test $OSMAJOR -ge 8], [ + AC_DEFINE(HAVE___INTERPOSE) + dlyld_interpose=yes + ], [ + RTLD_PRELOAD_ENABLE_VAR="DYLD_FORCE_FLAT_NAMESPACE" + ]) + RTLD_PRELOAD_VAR="DYLD_INSERT_LIBRARIES" + + # Build sudo_noexec.so as a shared library, not a module. + # On Darwin, modules and shared libraries are incompatible. + PRELOAD_MODULE= + ;; + *-*-nextstep*) + RTLD_PRELOAD_VAR="DYLD_INSERT_LIBRARIES" + RTLD_PRELOAD_ENABLE_VAR="DYLD_FORCE_FLAT_NAMESPACE" + ;; +esac + +dnl +dnl Library preloading to support NOEXEC +dnl +AS_IF([test X"$enable_intercept" = X"no"], [ + intercept_file=disabled +]) +AS_IF([test X"$with_noexec" = X"no"], [ + noexec_file=disabled +]) +AS_IF([test X"${intercept_file} ${noexec_file}" != X"disabled disabled"], [ + SUDO_DEFINE_UNQUOTED(RTLD_PRELOAD_VAR, "$RTLD_PRELOAD_VAR") + if test -n "$RTLD_PRELOAD_VAR_32"; then + SUDO_DEFINE_UNQUOTED(RTLD_PRELOAD_VAR_32, "$RTLD_PRELOAD_VAR_32") + fi + if test -n "$RTLD_PRELOAD_VAR_64"; then + SUDO_DEFINE_UNQUOTED(RTLD_PRELOAD_VAR_64, "$RTLD_PRELOAD_VAR_64") + fi + SUDO_DEFINE_UNQUOTED(RTLD_PRELOAD_DELIM, '$RTLD_PRELOAD_DELIM') + AS_IF([test -n "$RTLD_PRELOAD_DEFAULT"], [ + SUDO_DEFINE_UNQUOTED(RTLD_PRELOAD_DEFAULT, "$RTLD_PRELOAD_DEFAULT") + ]) + AS_IF([test -n "$RTLD_PRELOAD_ENABLE_VAR"], [ + SUDO_DEFINE_UNQUOTED(RTLD_PRELOAD_ENABLE_VAR, "$RTLD_PRELOAD_ENABLE_VAR") + ]) +]) + dnl dnl Python plugin support dnl @@ -4694,6 +4735,8 @@ AH_TEMPLATE(HAVE___FUNC__, [Define to 1 if the compiler supports the C99 __func_ AH_TEMPLATE(HAVE___INTERPOSE, [Define to 1 if you have dyld with __interpose attribute support.]) AH_TEMPLATE(SUDO_KRB5_INSTANCE, [An instance string to append to the username (separated by a slash) for Kerberos V authentication.]) AH_TEMPLATE(RTLD_PRELOAD_VAR, [The environment variable that controls preloading of dynamic objects.]) +AH_TEMPLATE(RTLD_PRELOAD_VAR_32, [The environment variable that controls preloading of 32-bit dynamic objects.]) +AH_TEMPLATE(RTLD_PRELOAD_VAR_64, [The environment variable that controls preloading of 64-bit dynamic objects.]) AH_TEMPLATE(RTLD_PRELOAD_ENABLE_VAR, [An extra environment variable that is required to enable preloading (if any).]) AH_TEMPLATE(RTLD_PRELOAD_DELIM, [The delimiter to use when defining multiple preloaded objects.]) AH_TEMPLATE(RTLD_PRELOAD_DEFAULT, [The default value of preloaded objects (if any).]) diff --git a/docs/sudo.conf.man.in b/docs/sudo.conf.man.in index 2494b26af7..34ad92e23e 100644 --- a/docs/sudo.conf.man.in +++ b/docs/sudo.conf.man.in @@ -17,7 +17,7 @@ .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .nr SL @SEMAN@ -.TH "SUDO.CONF" "@mansectform@" "January 16, 2023" "Sudo @PACKAGE_VERSION@" "File Formats Manual" +.TH "SUDO.CONF" "@mansectform@" "July 29, 2023" "Sudo @PACKAGE_VERSION@" "File Formats Manual" .nh .if n .ad l .SH "NAME" @@ -210,7 +210,7 @@ versions, if \fBsudoers\fR is configured as the security policy, it will be used as an audit plugin as well. -This guarantees that the logging behavior will be consistnet with that of +This guarantees that the logging behavior will be consistent with that of \fBsudo\fR versions 1.9.0 and below. .PP @@ -287,7 +287,7 @@ BSD, macOS and Solaris. .TP 6n intercept -The fully-qualified path to a shared library containing a wrappers for the +The path to a shared library containing a wrappers for the execve(2), execl(3), execle(3), @@ -301,15 +301,29 @@ library functions that intercepts attempts to run further commands and performs a policy check before allowing them to be executed. This is used to implement the \fIintercept\fR +and +\fIlog_subcmds\fR functionality on systems that support \fRLD_PRELOAD\fR -or its equivalent. +or the equivalent. +.sp +The +\fIintercept\fR +path may be set to either a single fully-qualified path, or, for systems +that support separate +\fRLD_PRELOAD\fR +environment variables for 32-bit and 64-bit executables, it may optionally +be set to two fully-qualified paths separated by a colon +(\(oq:\&\(cq). +The first path should be the 32-bit version and the second the +64-bit version. +This two-path form is currently only supported on AIX and Solaris +systems. The default value is \fI@intercept_file@\fR. .TP 6n noexec -The fully-qualified path to a shared library containing wrappers -for the +The path to a shared library containing wrappers for the execve(2), execl(3), execle(3), @@ -332,7 +346,20 @@ This is used to implement the \fInoexec\fR functionality on systems that support \fRLD_PRELOAD\fR -or its equivalent. +or the equivalent. +.sp +The +\fInoexec\fR +path may be set to either a single fully-qualified path, or, for systems +that support separate +\fRLD_PRELOAD\fR +environment variables for 32-bit and 64-bit executables, it may optionally +be set to two fully-qualified paths separated by a colon +(\(oq:\&\(cq). +The first path should be the 32-bit version and the second the +64-bit version. +This two-path form is currently only supported on AIX and Solaris +systems. The default value is \fI@noexec_file@\fR. .TP 6n diff --git a/docs/sudo.conf.mdoc.in b/docs/sudo.conf.mdoc.in index 05155e9110..471014d51f 100644 --- a/docs/sudo.conf.mdoc.in +++ b/docs/sudo.conf.mdoc.in @@ -16,7 +16,7 @@ .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .nr SL @SEMAN@ -.Dd January 16, 2023 +.Dd July 29, 2023 .Dt SUDO.CONF @mansectform@ .Os Sudo @PACKAGE_VERSION@ .Sh NAME @@ -192,7 +192,7 @@ versions, if .Nm sudoers is configured as the security policy, it will be used as an audit plugin as well. -This guarantees that the logging behavior will be consistnet with that of +This guarantees that the logging behavior will be consistent with that of .Nm sudo versions 1.9.0 and below. .Pp @@ -264,7 +264,7 @@ functions, for example .Bx , macOS and Solaris. .It intercept -The fully-qualified path to a shared library containing a wrappers for the +The path to a shared library containing a wrappers for the .Xr execve 2 , .Xr execl 3 , .Xr execle 3 , @@ -278,14 +278,28 @@ library functions that intercepts attempts to run further commands and performs a policy check before allowing them to be executed. This is used to implement the .Em intercept +and +.Em log_subcmds functionality on systems that support .Ev LD_PRELOAD -or its equivalent. +or the equivalent. +.Pp +The +.Em intercept +path may be set to either a single fully-qualified path, or, for systems +that support separate +.Dv LD_PRELOAD +environment variables for 32-bit and 64-bit executables, it may optionally +be set to two fully-qualified paths separated by a colon +.Pq Ql :\& . +The first path should be the 32-bit version and the second the +64-bit version. +This two-path form is currently only supported on AIX and Solaris +systems. The default value is .Pa @intercept_file@ . .It noexec -The fully-qualified path to a shared library containing wrappers -for the +The path to a shared library containing wrappers for the .Xr execve 2 , .Xr execl 3 , .Xr execle 3 , @@ -308,7 +322,20 @@ This is used to implement the .Em noexec functionality on systems that support .Ev LD_PRELOAD -or its equivalent. +or the equivalent. +.Pp +The +.Em noexec +path may be set to either a single fully-qualified path, or, for systems +that support separate +.Dv LD_PRELOAD +environment variables for 32-bit and 64-bit executables, it may optionally +be set to two fully-qualified paths separated by a colon +.Pq Ql :\& . +The first path should be the 32-bit version and the second the +64-bit version. +This two-path form is currently only supported on AIX and Solaris +systems. The default value is .Pa @noexec_file@ . .It plugin_dir diff --git a/src/exec_preload.c b/src/exec_preload.c index 3ae70c3add..2a88e5bb1e 100644 --- a/src/exec_preload.c +++ b/src/exec_preload.c @@ -173,9 +173,11 @@ fmtstr(sudo_alloc_fn_t alloc_fn, sudo_free_fn_t free_fn, const char * restrict o * Add a DSO file to LD_PRELOAD or the system equivalent. */ static char ** -sudo_preload_dso_alloc(char *const envp[], const char *dso_file, - int intercept_fd, sudo_alloc_fn_t alloc_fn, sudo_free_fn_t free_fn) +sudo_preload_dso_alloc(char *const envp[], const char *preload_var, + const char *dso_file, int intercept_fd, + sudo_alloc_fn_t alloc_fn, sudo_free_fn_t free_fn) { + const size_t preload_var_len = strlen(preload_var); char *preload = NULL; char **nep, **nenvp = NULL; char *const *ep; @@ -232,12 +234,13 @@ sudo_preload_dso_alloc(char *const envp[], const char *dso_file, goto oom; /* - * Shallow copy envp, with special handling for RTLD_PRELOAD_VAR, + * Shallow copy envp, with special handling for preload_var, * RTLD_PRELOAD_ENABLE_VAR and SUDO_INTERCEPT_FD. */ for (ep = envp, nep = nenvp; *ep != NULL; ep++) { - if (strncmp(*ep, RTLD_PRELOAD_VAR "=", sizeof(RTLD_PRELOAD_VAR)) == 0) { - const char *cp = *ep + sizeof(RTLD_PRELOAD_VAR); + if (strncmp(*ep, preload_var, preload_var_len) == 0 && + (*ep)[preload_var_len] == '=') { + const char *cp = *ep + preload_var_len + 1; const size_t dso_len = strlen(dso_file); /* Skip duplicates. */ @@ -291,13 +294,13 @@ sudo_preload_dso_alloc(char *const envp[], const char *dso_file, if (!dso_present) { if (preload_ptr == NULL) { # ifdef RTLD_PRELOAD_DEFAULT - preload = fmtstr(alloc_fn, free_fn, "%s=%s%c%s", RTLD_PRELOAD_VAR, + preload = fmtstr(alloc_fn, free_fn, "%s=%s%c%s", preload_var, dso_file, RTLD_PRELOAD_DELIM, RTLD_PRELOAD_DEFAULT); if (preload == NULL) { goto oom; } # else - preload = fmtstr(alloc_fn, free_fn, "%s=%s", RTLD_PRELOAD_VAR, + preload = fmtstr(alloc_fn, free_fn, "%s=%s", preload_var, dso_file); if (preload == NULL) { goto oom; @@ -305,8 +308,8 @@ sudo_preload_dso_alloc(char *const envp[], const char *dso_file, # endif *nep++ = preload; } else { - const char *old_val = *preload_ptr + sizeof(RTLD_PRELOAD_VAR); - preload = fmtstr(alloc_fn, free_fn, "%s=%s%c%s", RTLD_PRELOAD_VAR, + const char *old_val = *preload_ptr + preload_var_len + 1; + preload = fmtstr(alloc_fn, free_fn, "%s=%s%c%s", preload_var, dso_file, RTLD_PRELOAD_DELIM, old_val); if (preload == NULL) { goto oom; @@ -350,11 +353,59 @@ sudo_preload_dso_alloc(char *const envp[], const char *dso_file, debug_return_ptr(NULL); } +static char ** +sudo_preload_dso_path(char *const envp[], const char *dso_file, + int intercept_fd, sudo_alloc_fn_t alloc_fn, sudo_free_fn_t free_fn) +{ + char **ret = NULL; + const char *ep; + debug_decl(sudo_preload_dso_path, SUDO_DEBUG_UTIL); + + ep = strchr(dso_file, ':'); + if (ep == NULL) { + /* Use default LD_PRELOAD */ + return sudo_preload_dso_alloc(envp, RTLD_PRELOAD_VAR, dso_file, + intercept_fd, alloc_fn, free_fn); + } + + /* Add 32-bit LD_PRELOAD if present. */ + if (ep != dso_file) { +#ifdef RTLD_PRELOAD_VAR_32 + const size_t len = (size_t)(ep - dso_file); + char name[PATH_MAX]; + + if (len >= sizeof(name)) { + sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, + "%.*s: path too long", (int)len, dso_file); + } else { + memcpy(name, dso_file, len); + name[len] = '\0'; + ret = sudo_preload_dso_alloc(envp, RTLD_PRELOAD_VAR_32, name, + intercept_fd, alloc_fn, free_fn); + envp = ret; + } +#endif /* RTLD_PRELOAD_VAR_32 */ + dso_file = ep + 1; + } + +#ifdef RTLD_PRELOAD_VAR_64 + /* Add 64-bit LD_PRELOAD if present. */ + if (*dso_file != '\0') { + char **new_envp = sudo_preload_dso_alloc(envp, RTLD_PRELOAD_VAR_64, + dso_file, intercept_fd, alloc_fn, free_fn); + free_fn(ret); + ret = new_envp; + } +#endif /* RTLD_PRELOAD_VAR_64 */ + + debug_return_ptr(ret); +} + char ** sudo_preload_dso_mmap(char *const envp[], const char *dso_file, int intercept_fd) { - return sudo_preload_dso_alloc(envp, dso_file, intercept_fd, + return sudo_preload_dso_path(envp, dso_file, intercept_fd, sudo_mmap_allocarray_v1, sudo_mmap_free_v1); } @@ -362,7 +413,7 @@ char ** sudo_preload_dso(char *const envp[], const char *dso_file, int intercept_fd) { - return sudo_preload_dso_alloc(envp, dso_file, intercept_fd, + return sudo_preload_dso_path(envp, dso_file, intercept_fd, sudo_allocarray, free); } #endif /* RTLD_PRELOAD_VAR */