From 22f4f10a3a6e40245b5444ea2eb0893122696f1f Mon Sep 17 00:00:00 2001
From: "Todd C. Miller" <Todd.Miller@courtesan.com>
Date: Tue, 21 Feb 2012 13:26:02 -0500
Subject: [PATCH] Check for LD_PRELOAD variants in configure instead of
 checkign cpp symbols.  In disable_execute(), compute the length of the new
 envp and allocate it once instead of reallocating on demand.  Also append old
 value of LD_PRELOAD (if any) to the new value.

---
 config.h.in       | 13 +++++++
 configure         | 50 ++++++++++++++++++++++++++
 configure.in      | 44 +++++++++++++++++++++++
 src/exec_common.c | 89 +++++++++++++++++++++++++----------------------
 4 files changed, 155 insertions(+), 41 deletions(-)

diff --git a/config.h.in b/config.h.in
index bab0510c1..277b018f2 100644
--- a/config.h.in
+++ b/config.h.in
@@ -745,6 +745,19 @@
 /* The syslog priority sudo will use for successful attempts. */
 #undef PRI_SUCCESS
 
+/* The default value of preloaded objects (if any). */
+#undef RTLD_PRELOAD_DEFAULT
+
+/* The delimiter to use when defining multiple preloaded objects. */
+#undef RTLD_PRELOAD_DELIM
+
+/* An extra environment variable that is required to enable preloading (if
+   any). */
+#undef RTLD_PRELOAD_ENABLE_VAR
+
+/* The environment variable that controls preloading of dynamic objects. */
+#undef RTLD_PRELOAD_VAR
+
 /* The user sudo should run commands as by default. */
 #undef RUNAS_DEFAULT
 
diff --git a/configure b/configure
index d6a578a49..513129acd 100755
--- a/configure
+++ b/configure
@@ -2946,6 +2946,11 @@ shadow_libs=
 shadow_libs_optional=
 CONFIGURE_ARGS="$@"
 
+RTLD_PRELOAD_VAR="LD_PRELOAD"
+RTLD_PRELOAD_ENABLE_VAR=
+RTLD_PRELOAD_DELIM=":"
+RTLD_PRELOAD_DEFAULT=
+
 
 
 
@@ -13728,6 +13733,9 @@ fi
 
 case "$host" in
     *-*-sunos4*)
+		# LD_PRELOAD is space-delimited
+		RTLD_PRELOAD_DELIM=" "
+
 		# getcwd(3) opens a pipe to getpwd(1)!?!
 		BROKEN_GETCWD=1
 
@@ -13739,6 +13747,9 @@ case "$host" in
 		shadow_funcs="getpwanam issecure"
 		;;
     *-*-solaris2*)
+		# LD_PRELOAD is space-delimited
+		RTLD_PRELOAD_DELIM=" "
+
 		# To get the crypt(3) prototype (so we pass -Wall)
 		OSDEFS="${OSDEFS} -D__EXTENSIONS__"
 		# AFS support needs -lucb
@@ -13855,6 +13866,8 @@ done
 		# LDR_PRELOAD is only supported in AIX 5.3 and later
 		if test $OSMAJOR -lt 5; then
 		    with_noexec=no
+		else
+		    RTLD_PRELOAD_VAR="LDR_PRELOAD"
 		fi
 
 		# AIX-specific functions
@@ -14056,6 +14069,9 @@ $as_echo "yes, fixing locally" >&6; }
 
 fi
 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+		# ":DEFAULT" must be appended to _RLD_LIST
+		RTLD_PRELOAD_VAR="_RLD_LIST"
+		RTLD_PRELOAD_DEFAULT="DEFAULT"
 		: ${mansectsu='8'}
 		: ${mansectform='4'}
 		;;
@@ -14122,6 +14138,9 @@ if test "x$ac_cv_lib_sun_getpwnam" = xyes; then :
 fi
 
 		fi
+		# ":DEFAULT" must be appended to _RLD_LIST
+		RTLD_PRELOAD_VAR="_RLD_LIST"
+		RTLD_PRELOAD_DEFAULT="DEFAULT"
 		: ${mansectsu='1m'}
 		: ${mansectform='4'}
 		;;
@@ -14313,11 +14332,15 @@ done
 		CHECKSHADOW="false"
 		test -z "$with_pam" && AUTH_EXCL_DEF="PAM"
 		: ${with_logincap='yes'}
+		RTLD_PRELOAD_VAR="DYLD_INSERT_LIBRARIES"
+		RTLD_PRELOAD_ENABLE_VAR="DYLD_FORCE_FLAT_NAMESPACE"
 		;;
     *-*-nextstep*)
 		# lockf() on is broken on the NeXT -- use flock instead
 		ac_cv_func_lockf=no
 		ac_cv_func_flock=yes
+		RTLD_PRELOAD_VAR="DYLD_INSERT_LIBRARIES"
+		RTLD_PRELOAD_ENABLE_VAR="DYLD_FORCE_FLAT_NAMESPACE"
 		;;
     *-*-*sysv4*)
 		: ${mansectsu='1m'}
@@ -14333,6 +14356,29 @@ done
 		;;
 esac
 
+if test -n "$with_noexec"; then
+    cat >>confdefs.h <<EOF
+#define RTLD_PRELOAD_VAR "$RTLD_PRELOAD_VAR"
+EOF
+
+    cat >>confdefs.h <<EOF
+#define RTLD_PRELOAD_DELIM "$RTLD_PRELOAD_DELIM"
+EOF
+
+    if test -n "$RTLD_PRELOAD_DEFAULT"; then
+	cat >>confdefs.h <<EOF
+#define RTLD_PRELOAD_DEFAULT "$RTLD_PRELOAD_DEFAULT"
+EOF
+
+    fi
+    if test -n "$RTLD_PRELOAD_ENABLE_VAR"; then
+	cat >>confdefs.h <<EOF
+#define RTLD_PRELOAD_ENABLE_VAR "$RTLD_PRELOAD_ENABLE_VAR"
+EOF
+
+    fi
+fi
+
 AUTH_REG=${AUTH_REG# }
 AUTH_EXCL=${AUTH_EXCL# }
 if test -n "$AUTH_EXCL"; then
@@ -22313,6 +22359,10 @@ fi
 
 
 
+
+
+
+
 
 
 
diff --git a/configure.in b/configure.in
index 5893b7da6..56e1b6042 100644
--- a/configure.in
+++ b/configure.in
@@ -184,6 +184,14 @@ shadow_libs=
 shadow_libs_optional=
 CONFIGURE_ARGS="$@"
 
+dnl
+dnl LD_PRELOAD equivalents
+dnl
+RTLD_PRELOAD_VAR="LD_PRELOAD"
+RTLD_PRELOAD_ENABLE_VAR=
+RTLD_PRELOAD_DELIM=":"
+RTLD_PRELOAD_DEFAULT=
+
 dnl
 dnl libc replacement functions live in compat
 dnl
@@ -1465,6 +1473,9 @@ fi
 
 case "$host" in
     *-*-sunos4*)
+		# LD_PRELOAD is space-delimited
+		RTLD_PRELOAD_DELIM=" "
+
 		# getcwd(3) opens a pipe to getpwd(1)!?!
 		BROKEN_GETCWD=1
 
@@ -1476,6 +1487,9 @@ case "$host" in
 		shadow_funcs="getpwanam issecure"
 		;;
     *-*-solaris2*)
+		# LD_PRELOAD is space-delimited
+		RTLD_PRELOAD_DELIM=" "
+
 		# To get the crypt(3) prototype (so we pass -Wall)
 		OSDEFS="${OSDEFS} -D__EXTENSIONS__"
 		# AFS support needs -lucb
@@ -1538,6 +1552,8 @@ case "$host" in
 		# LDR_PRELOAD is only supported in AIX 5.3 and later
 		if test $OSMAJOR -lt 5; then
 		    with_noexec=no
+		else
+		    RTLD_PRELOAD_VAR="LDR_PRELOAD"
 		fi
 
 		# AIX-specific functions
@@ -1671,6 +1687,9 @@ case "$host" in
 		]], [[exit(0);]])], [AC_MSG_RESULT(no)], [AC_MSG_RESULT([yes, fixing locally])
 		sed 's:<acl.h>:<sys/acl.h>: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'}
 		;;
@@ -1698,6 +1717,9 @@ case "$host" in
 		if test "$OSMAJOR" -le 4; then
 		    AC_CHECK_LIB(sun, getpwnam, [LIBS="${LIBS} -lsun"])
 		fi
+		# ":DEFAULT" must be appended to _RLD_LIST
+		RTLD_PRELOAD_VAR="_RLD_LIST"
+		RTLD_PRELOAD_DEFAULT="DEFAULT"
 		: ${mansectsu='1m'}
 		: ${mansectform='4'}
 		;;
@@ -1840,11 +1862,15 @@ case "$host" in
 		CHECKSHADOW="false"
 		test -z "$with_pam" && AUTH_EXCL_DEF="PAM"
 		: ${with_logincap='yes'}
+		RTLD_PRELOAD_VAR="DYLD_INSERT_LIBRARIES"
+		RTLD_PRELOAD_ENABLE_VAR="DYLD_FORCE_FLAT_NAMESPACE"
 		;;
     *-*-nextstep*)
 		# lockf() on is broken on the NeXT -- use flock instead
 		ac_cv_func_lockf=no
 		ac_cv_func_flock=yes
+		RTLD_PRELOAD_VAR="DYLD_INSERT_LIBRARIES"
+		RTLD_PRELOAD_ENABLE_VAR="DYLD_FORCE_FLAT_NAMESPACE"
 		;;
     *-*-*sysv4*)
 		: ${mansectsu='1m'}
@@ -1860,6 +1886,20 @@ case "$host" in
 		;;
 esac
 
+dnl
+dnl Library preloading to support NOEXEC
+dnl
+if test -n "$with_noexec"; then
+    SUDO_DEFINE_UNQUOTED(RTLD_PRELOAD_VAR, "$RTLD_PRELOAD_VAR")
+    SUDO_DEFINE_UNQUOTED(RTLD_PRELOAD_DELIM, "$RTLD_PRELOAD_DELIM")
+    if test -n "$RTLD_PRELOAD_DEFAULT"; then
+	SUDO_DEFINE_UNQUOTED(RTLD_PRELOAD_DEFAULT, "$RTLD_PRELOAD_DEFAULT")
+    fi
+    if test -n "$RTLD_PRELOAD_ENABLE_VAR"; then
+	SUDO_DEFINE_UNQUOTED(RTLD_PRELOAD_ENABLE_VAR, "$RTLD_PRELOAD_ENABLE_VAR")
+    fi
+fi
+
 dnl
 dnl Check for mixing mutually exclusive and regular auth methods
 dnl
@@ -3308,6 +3348,10 @@ AH_TEMPLATE(HAVE_STRUCT_UTMP_UT_EXIT, [Define to 1 if `ut_exit' is a member of `
 AH_TEMPLATE(HAVE_STRUCT_UTMPX_UT_EXIT, [Define to 1 if `ut_exit' is a member of `struct utmpx'.])
 AH_TEMPLATE(HAVE___FUNC__, [Define to 1 if the compiler supports the C99 __func__ variable.])
 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_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).])
 
 dnl
 dnl Bits to copy verbatim into config.h.in
diff --git a/src/exec_common.c b/src/exec_common.c
index 389aa5c64..a4cd5400b 100644
--- a/src/exec_common.c
+++ b/src/exec_common.c
@@ -53,9 +53,12 @@ static char * const *
 disable_execute(char *const envp[])
 {
 #ifdef _PATH_SUDO_NOEXEC
-    char * const *ev;
-    char *preload, **nenvp;
-    int env_len = 0, env_size = 128;
+    char *preload, **nenvp = NULL;
+    int env_len, env_size;
+    int preload_idx = -1;
+# ifdef RTLD_PRELOAD_ENABLE_VAR
+    bool enabled = false;
+# endif
 #endif /* _PATH_SUDO_NOEXEC */
     debug_decl(disable_execute, SUDO_DEBUG_UTIL)
 
@@ -67,55 +70,59 @@ disable_execute(char *const envp[])
 #endif /* HAVE_PRIV_SET */
 
 #ifdef _PATH_SUDO_NOEXEC
-    nenvp = emalloc2(env_size, sizeof(char *));
-
     /*
      * Preload a noexec file.  For a list of LD_PRELOAD-alikes, see
      * http://www.fortran-2000.com/ArnaudRecipes/sharedlib.html
      * XXX - need to support 32-bit and 64-bit variants
      */
-# if defined(__darwin__) || defined(__APPLE__)
-    nenvp[env_len++] = "DYLD_FORCE_FLAT_NAMESPACE=";
-    preload = fmt_string("DYLD_INSERT_LIBRARIES", sudo_conf_noexec_path());
-# elif defined(__osf__) || defined(__sgi)
-    easprintf(&preload, "_RLD_LIST=%s:DEFAULT", sudo_conf_noexec_path());
-# elif defined(_AIX)
-    preload = fmt_string("LDR_PRELOAD", sudo_conf_noexec_path());
-# else
-    preload = fmt_string("LD_PRELOAD", sudo_conf_noexec_path());
-# endif
-    if (preload == NULL)
-	errorx(1, _("unable to allocate memory"));
-    nenvp[env_len++] = preload;
 
-    for (ev = envp; *ev != NULL; ev++) {
-	/*
-	 * Prune out existing preloaded libraries.
-	 * XXX - should append to new value instead.
-	 */
-# if defined(__darwin__) || defined(__APPLE__)
-	if (strncmp(*ev, "DYLD_INSERT_LIBRARIES=", sizeof("DYLD_INSERT_LIBRARIES=") - 1) == 0)
-	    continue;
-	if (strncmp(*ev, "DYLD_FORCE_FLAT_NAMESPACE=", sizeof("DYLD_INSERT_LIBRARIES=") - 1) == 0)
+    /* Count entries in envp, looking for LD_PRELOAD as we go. */
+    for (env_len = 0; envp[env_len] != NULL; env_len++) {
+	if (strncmp(envp[env_len], RTLD_PRELOAD_VAR "=", sizeof(RTLD_PRELOAD_VAR)) == 0) {
+	    preload_idx = env_len;
 	    continue;
-# elif defined(__osf__) || defined(__sgi)
-	if (strncmp(*ev, "_RLD_LIST=", sizeof("_RLD_LIST=") - 1) == 0)
-	    continue;
-# elif defined(_AIX)
-	if (strncmp(*ev, "LDR_PRELOAD=", sizeof("LDR_PRELOAD=") - 1) == 0)
-	    continue;
-# else
-	if (strncmp(*ev, "LD_PRELOAD=", sizeof("LD_PRELOAD=") - 1) == 0)
+	}
+#ifdef RTLD_PRELOAD_ENABLE_VAR
+	if (strncmp(envp[env_len], RTLD_PRELOAD_ENABLE_VAR "=", sizeof(RTLD_PRELOAD_ENABLE_VAR)) == 0) {
+	    enabled = true;
 	    continue;
-# endif
-	/* Need at least 2 slots for current element and a NULL. */
-	if (env_len + 2 > env_size) {
-	    env_size += 128;
-	    nenvp = erealloc3(nenvp, env_size, sizeof(char *));
 	}
-	nenvp[env_len++] = *ev;
+#endif
     }
+
+    /* Make a new copy of envp as needed. */
+    env_size = env_len + 1 + (preload_idx == -1);
+#ifdef RTLD_PRELOAD_ENABLE_VAR
+    if (!enabled)
+	env_size++;
+#endif
+    nenvp = emalloc2(env_size, sizeof(*envp));
+    memcpy(nenvp, envp, env_len * sizeof(*envp));
     nenvp[env_len] = NULL;
+
+    /* Prepend our LD_PRELOAD to existing value or add new entry at the end. */
+    if (preload_idx == -1) {
+# ifdef RTLD_PRELOAD_DEFAULT
+	easprintf(&preload, "%s=%s%s%s", RTLD_PRELOAD_VAR, sudo_conf_noexec_path(), RTLD_PRELOAD_DELIM, RTLD_PRELOAD_DEFAULT);
+# else
+	preload = fmt_string(RTLD_PRELOAD_VAR, sudo_conf_noexec_path());
+# endif
+	if (preload == NULL)
+	    errorx(1, _("unable to allocate memory"));
+	nenvp[env_len++] = preload;
+	nenvp[env_len] = NULL;
+    } else {
+	easprintf(&preload, "%s=%s%s%s", RTLD_PRELOAD_VAR, sudo_conf_noexec_path(), RTLD_PRELOAD_DELIM, nenvp[preload_idx]);
+	nenvp[preload_idx] = preload;
+    }
+# ifdef RTLD_PRELOAD_ENABLE_VAR
+    if (!enabled) {
+	nenvp[env_len++] = RTLD_PRELOAD_ENABLE_VAR "=";
+	nenvp[env_len] = NULL;
+    }
+# endif
+
+    /* Install new env pointer. */
     envp = nenvp;
 #endif /* _PATH_SUDO_NOEXEC */
 
-- 
2.40.0