public inbox for gentoo-portage-dev@lists.gentoo.org
 help / color / mirror / Atom feed
* [gentoo-portage-dev] [gavin@vess.com: Hardening the Gentoo Portage Sandbox [rev 2 - edits re: glibc's use of weak aliases]]
@ 2004-04-12 19:43 Nick Jones
  0 siblings, 0 replies; only message in thread
From: Nick Jones @ 2004-04-12 19:43 UTC (permalink / raw
  To: dev-portage, scox, gentoo-portage-dev

[-- Attachment #1: Type: text/plain, Size: 9 bytes --]

Forward.

[-- Attachment #2: Type: message/rfc822, Size: 31495 bytes --]

[-- Attachment #2.1.1: Type: text/plain, Size: 13452 bytes --]

Greetings,

I found some weaknesses in the Gentoo Portage sandbox with consequences similar to the recent posting about the critical issue with Portage and /tmp.  FYI, Chris posted an selinux one-liner solution =)

>                     create_dir_notdevfile(portage_t,{ file_type -user_tmp_t })
>                     Therefore, portage_t can modify all files, except those created by users, in /tmp.

Given this group's focus on hardening Gentoo, I am curious about your opinions regarding hardening of Portage.  For example, given the options available with selinux and grsecurity patches, perhaps a more efficient alternative exists to using the sandbox as implemented by libsandbox.so and the sandbox binary in Portage?  Also, if 1) Gentoo developers use the sandbox when testing, 2) the sandbox works flawlessly for all tested/installed packages, and 3) there were no sandbox violations, then how much practical value does an end user forfeit if they don't use the sandbox FEATURE, but continue using the userpriv FEATURE?  Since a user's unique configuration might result in different code executed during installation, a user might benefit from using the sandbox, even if the developers/ebuild maintainers did not witness any sandbox violations, but, does this occur in practice?

I hope the discussion below about Portage is constructive, informative, and encouraging, without diminishing either the value and accomplishments of the Portage team or the relevance/importance of weaknesses (discussed below) to security-conscious Gentoo users and administrators.  I have invested a great deal of effort in switching our software from Red Hat to Gentoo, and have a vested interest in improving the security of Gentoo.

I welcome all feedback.

Thanks,
Gavin


Abstract
=========
I reviewed some of Gentoo's sandbox code in preparation for integrating an enhanced compiler-caching technology derived from ccache.  Although I personally believe the Portage sandbox is extremely helpful and useful, I found that the sandbox is not completely self-enforcing and relies on the cooperation of the installation code in source packages to maintain the integrity of the sandbox during an emerge.

Using the 'sandbox' FEATURE in /etc/make.conf without the FEATURES userpriv, and usersandbox (i.e. emerging a package as the root user) is not sufficient to prevent an ebuild from modifying sensitive files (e.g. /etc/passwd) outside the sandbox while performing an ebuild. Similarly, the use of all three FEATURES does not prevent an ebuild from writing to any file writable by the portage user (subject to security context limitations, etc. established by selinux/grsecurity/etc.).

Conclusion
==========
1) The attached patch helps detect and prevent "accidental" breakage of the sandbox by "whitehat" package/ebuild authors.

2) Hardened Gentoo (either grsecurity or selinux) provides a kernel-based capability to enforcing the sandbox.  After this article has been edited appropriately, perhaps the hardened team might want to contribute some advice?

3) I suggest either updating Portage to eliminate the documented weaknesses, or changing Portage documentation to reflect the weaknesses, documented below, contradicting documentation claims like, ".. as long as the package isn't installed on your system, the package cannot touch any file outside the sandbox." [http://www.gentoo.org/doc/en/handbook/handbook-x86.xml?part=2&chap=3]  

Personal Opinions
================
I've found the sandbox an invaluable part of Gentoo and whole-heartedly recommend its continued use via FEATURES="sandbox userpriv usersandbox", but with an understanding for security-conscious administrators that emerge'ing packages poses more risks than implied by the documentation (e.g. "cannot touch any file outside the sandbox").  However, a virtual sandbox enforced by the kernel might provide an appealing alternative to reliance on the current pre-loaded library method.

I believe the Portage team has already created a "best in class" solution.


Weaknesses in Portage's Sandbox
================================================
The following weaknesses allow for the possibility of a similar type of exploit as: http://bugs.gentoo.org/show_bug.cgi?id=21923 

Essentially, a blackhat might be able to trick portage into installing some form of a rootkit that the root user eventually executes unknowingly. More specifically, a blackhat who is aware of the following weakness might be able to find an existing ebuild that scribbles outside the sandbox to some pre-determined location (e.g. /tmp), and then hardlink/symlink that location to somewhere else under their control, thus allowing them to insert code (i.e. a rootkit) during the emerge'ing of a package.  Regardless, such writable, vulnerable locations are not supposed to exist at all on a secured/hardened system.

First, since the correct operation of the sandbox depends on the presence and definition of the LD_PRELOAD(7) and SANDBOX_* environment variables to enable the sandbox and control its functioning, package code executed during emerge can easily escape the sandbox by unsetting these variables (e.g. SANDBOX_ON), as libsandbox.so lacks enforcement (except for LD_PRELOAD with execve, which is practically useless if the SANDBOX_* variables have been unset).

Second, when the execve wrapper finds that libsandbox.so is not present in LD_PRELOAD, it appends this library, instead of pre-pending it, potentially allowing interception of further system calls by a library other than libsandbox.so(6).  Example(8):

- - - b e g i n   e x a m p l e   m a k e f  i l e - - -
export LD_PRELOAD=whitehat_lib.so

breaksandbox:
    env /bin/echo 'misc. conf. stuff' > /etc/whitehat.conf
- - - e n d   e x a m p l e m a k e f  i l e - - -

Third, when using a version of libc (e.g. recent versions of glibc) that implements execv, execl, execlp, or execvp without calling execve, the execve wrapper code in libsandbox.c is bypassed(2) by some executables (e.g. GNU's make).  Note that glibc's implementation of __execve defines a weak alias, "weak_alias (__execve, execve)".  When we provide our own execve in libsandbox.so, glibc's execve is no longer "available for reference from other files" [http://gcc.gnu.org/onlinedocs/gccint/].  However, execl, execle, execv, and execvp (but not execlp which calls execvp) all call __execve (glibc's "true" execve).  When libsandbox.c replaces execve, these other functions know nothing about the libsandbox.so execve, and continue using __execve.  From a practical perspective, GNU's make uses execvp, while gcc's frontend uses both execv and execvp.  In contrast, the exec family in uClibc is written using execvp and execve.  Modifying libsandbox.c to wrap __execve might work with glibc, but not with uClibc, unless additional changes are made.

Consider that glibc's implementation of __execve defines a weak alias, "weak_alias (__execve, execve)", and implements execv as follows:

       int execv (const char *path, char *const argv[])
       {  return __execve (path, argv, __environ); }

Thus, when libsandbox.so provides its own execve, execv continues to use glibc's __execve().  The attached patch adds support for wrapping execvp [the others are left as an "exercise" for the reader ;) ], factors functionality common to the execve and execvp wrappers, and ensures that libsandbox.so is listed first in LD_PRELOAD.   As an example demonstrating the consequences of a missing wrapper for execvp in libsandbox.c, please consider a hypothetical Makefile in a package with a corresponding ebuild:

- - - b e g i n   e x a m p l e   m a k e f  i l e - - -
export LD_PRELOAD=whitehat_lib.so

breaksandbox:
    /bin/echo 'blackhat was here' > /etc/passwd
    /bin/echo 'misc. conf. stuff' > /etc/whitehat.conf
    env - ./scribble_anywhere_portage_user_can_scribble.sh
- - - e n d   e x a m p l e m a k e f  i l e - - -

The script "scribble_anywhere_portage_user_can_scribble.sh" runs as the portage user ('root', unless FEATURES="sandbox usersandbox userpriv ..." in /etc/make.conf).

The first and second rules for the breaksandbox(1) target above exploit the missing execvp wrapper weakness, while the third rule exploits the lack of enforcement of the sandbox due to missing "SANDBOX_*" environment variables.  The second rule also highlights how easily a package author might accidentally break the sandbox.

Proposed Changes
=====================
The attached example patch(5) addresses the execvp weakness (the first of the two example rules above), and ensures that libsandbox.so is listed first in LD_PRELOAD.

The "env - script" weakness might be avoided by storing the data in the SANDBOX_* environment variables somewhere that the portage user lacks write access to.  However, that would necessitate other changes totaling a non-trivial effort.

Although one might endeavor to enforce application of sandbox protections requested via the SANDBOX_* environment variables, other weaknesses in relying on the LD_PRELOAD tactic might ameliorate the value of such endeavors.  Since the hardened Gentoo project team provides hardened solutions that specifically include the capability of isolating a user using ACLs enforced by the kernel, leveraging their work might prove more efficient in seeking a self-enforcing sandbox capable of preventing one emerge from affecting unrelated files owned by the portage user.

Notes
=======
1) The "breaksandbox" Makefile rule also breaks anything depending on other preloaded shared objects (via LD_PRELOAD), but the code in libsandbox.c was already broken anyway for IA32/gcc 3.3/glibc 2.3 combo with multiple shared objects in LD_PRELOAD [50-r3 just fixed that bug].

2) The execvp problem can be hidden in a "generation skip", where a process uses execvp (e.g. GNU make), but its child uses one of the exec's implemented by execve.  In the latter case, the sandbox resumes functioning normally, even though the sandbox execve wrapper code was skipped by the parent using execvp.  The use of the terms 'child' and 'parent' are inappropriate to examples where there was a pure exec without a fork, but this is already too wordy.

3) Unlike glibc, uClibc implements execvp using execve.  I have not tried to determine whether versions of glibc older than glibc-2.3.3 implement execvp using execve.

4) Why hasn't this problem been noticed before? Perhaps it has, and I haven't heard.   Also, see #2 above.  Perhaps most programs that use execvp during an ebuild don't alter the environment (i.e. clean up the environment / alter LD_PRELOAD environment variable) before executing execvp?  Perhaps the practical consequences are minimal.  I'm not aware of any practical, already-existing examples exploiting these sandbox weaknesses.

5) The patch contains some code inappropriate for most any purpose other than testing/evaluating the sandbox system.   However, it shouldn't require much effort to integrate and adapt the patch to one's own preferences and style.  Also, some debugging statements left in the patch require /tmp/jlog to exist before emerg'ing and belong to the portage user and group.  These debugging statements are left intentionally in the patch to highlight how much GNU's make uses execvp (see the log file /tmp/jlog after performing an emerge using a patched version of libsandbox using the supplied patch).

The list of SANDBOX_* environment variables checked by the patch for non-empty definitions should be tuned, and possibly extended to check for unreasonable combinations. Also what is reported and how needs "adjusting" to suit the typical Gentoo user.  The patch also includes an experimental performance tweak for x86 architectures that is automatically selected if ARCH == 'x86'.  The patch uses a different (experimental) method of wrapping execve and execvp using assembly. I've only tested the patch on a single system (x86, the latest version of almost everything).  #include <std_disclaimer.txt>

6) Prior to 50-r3, this weakness was limited to special cases where an ebuild explicitly set 'LD_PRELOAD=/mylib.so /lib/libsandbox.so', since libsandbox.so was partly broken when using multiple libraries in LD_PRELOAD, at least for the current version of GNU's ld that requires spaces between libs(prior versions of libsandbox.c used a ':' instead of ' ' when appending).  Thus, prior to 50-r3, 'export LD_PRELOAD=/mylib.so' resulted in something like 'LD_PRELOAD=/mylib.so:/lib/libsandbox.so', causing ld to report an error.

7) Actually, an LD_SO_PRELOAD option exists in sandbox.c that tries to enforce the preloading of libsandbox.so via /etc/ld.so.preload.  However, if the sandbox executable is interrupted at an inopportune time, the contents of ld.so.preload could be corrupted. Also, negative consequences might arise for unrelated system processes that happen to start while the sandbox exists.  Perhaps if ld.so were tweaked to provide for enforced, per-user versions of ld.so.preload ...  Regardless, the portage ebuild states this approach has been deprecated, and it still doesn't prevent installation code from unsetting SANDBOX_ON.

8) If we append ' /lib/libsandbox.so' to the 'export LD_PRELOAD=whitehat_lib.so' in the first makefile example, the supplied patch does not check to make sure that libsandbox.so is listed first.  I consider that a "todo" item.

[-- Attachment #2.1.2: portage-2.0.50-r3.GB.patch --]
[-- Type: application/octet-stream, Size: 14234 bytes --]

diff -ruN portage-2.0.50-r3/src/sandbox-1.1/Makefile portage-2.0.50-r3.GB/src/sandbox-1.1/Makefile
--- portage-2.0.50-r3/src/sandbox-1.1/Makefile	2004-03-21 17:40:58.000000000 -0800
+++ portage-2.0.50-r3.GB/src/sandbox-1.1/Makefile	2004-04-07 15:33:50.699788600 -0700
@@ -31,6 +31,10 @@
 	TARGETS = libsandbox.so sandbox
 endif
 
+ifeq ($(ARCH),"x86")
+	ARCH_CFLAGS += -DARCH_X86
+endif
+
 all:	$(TARGETS)
 
 sandbox: sandbox.o sandbox_futils.o getcwd.c
diff -ruN portage-2.0.50-r3/src/sandbox-1.1/libsandbox.c portage-2.0.50-r3.GB/src/sandbox-1.1/libsandbox.c
--- portage-2.0.50-r3/src/sandbox-1.1/libsandbox.c	2004-03-21 17:40:58.000000000 -0800
+++ portage-2.0.50-r3.GB/src/sandbox-1.1/libsandbox.c	2004-04-07 15:43:02.163953304 -0700
@@ -24,6 +24,7 @@
  *  Partly Copyright (C) 1998-9 Pancrazio `Ezio' de Mauro <p@demauro.net>,
  *  as some of the InstallWatch code was used.
  *
+ *  Partly Copyright (C) 2004 GoldenBlue, LLC. http://www.goldenbluellc.com/
  *
  *  $Header: /home/cvsroot/gentoo-src/portage/src/sandbox-1.1/libsandbox.c,v 1.16 2004/03/22 01:40:58 carpaski Exp $
  *
@@ -200,6 +201,8 @@
 
 extern int execve(const char *filename, char *const argv[], char *const envp[]);
 static int (*true_execve) (const char *, char *const[], char *const[]);
+extern int execvp(const char *filename, char *const argv[]);
+static int (*true_execvp) (const char *, char *const[]);
 
 /*
  * Initialize the shabang
@@ -244,6 +247,7 @@
 #endif
 
 	true_execve = dlsym(libc_handle, "execve");
+	true_execvp = dlsym(libc_handle, "execvp");
 }
 
 void
@@ -725,93 +729,140 @@
 
 #endif													/* GLIBC_MINOR >= 1 */
 
+char *
+get_new_ldp(char *orig_ldp)
+/* Usage: orig_ldp can be NULL, returns malloc'd new LD_PRELOAD with sandbox lib prepended.
+ * Bug not fixed: the relative position of sandbox_lib amongst other libs in
+ * LD_PRELOAD can alter the program's behavior, but we merely check for presence!
+ */
+{
+	int orig_ldp_len = 0;
+    if (orig_ldp)
+    {
+        if (!strncmp(orig_ldp, "LD_PRELOAD=", 11)) orig_ldp += 11;
+        orig_ldp_len = strlen(orig_ldp);
+    }
+    char *new_ldp = NULL;
+    int sandbox_lib_len = strlen(sandbox_lib);
+    /* new_ldp_len = 'LD_PRELOAD=' + sanboxlib + space + original libs + null + fudge */
+    int new_ldp_len = 11 + sandbox_lib_len + 1   + orig_ldp_len  +  1   +  1;
+    if ((new_ldp_len >= 4096) || (NULL == (new_ldp = malloc(new_ldp_len * sizeof(char)))))
+    {
+		perror("LD_PRELOAD exceeds hard-coded limit of 4096 characters, or malloc failed!");
+    	errno = ENOMEM;
+    	return NULL;
+	}
+
+	/* If the sanbox.so is appended, then the sandbox might easily break, without
+	 * the user knowing.  Also, the semantics of the sandbox suggest it should
+	 * have higher priority than other libs in LD_PRELOAD. Default to prepend.
+     */ 
+	strcpy(new_ldp, "LD_PRELOAD=");
+#ifndef APPEND_SANDBOX_SO
+	strncpy(&(new_ldp[11]), sandbox_lib, sandbox_lib_len +1);
+	/* PREPEND sandbox.so to the beginning of the LD_PRELOAD environment variable */
+    if (orig_ldp_len)
+    {
+	    new_ldp[sandbox_lib_len + 11] = ' ';
+    	/* automatically null-terminated by strncpy, if sandbox_lib is */
+    	strncpy(&(new_ldp[sandbox_lib_len + 12]), orig_ldp, orig_ldp_len +1);
+    }
+fprintf(stderr, "libsandbox.so: DEBUG: orig_ldp='%s'/%d; new_ldp='%s'/%d\n", orig_ldp, orig_ldp_len, new_ldp, new_ldp_len);
+#else
+	if (orig_ldp_len)
+    {
+    	strncpy(&(new_ldp[11]), orig_ldp, orig_ldp_len +1);
+    	new_ldp[orig_ldp_len +11] = ' '; /* append sandbox.so */
+    }
+    else orig_ldp_len--;
+   	strncpy(&(new_ldp[orig_ldp_len +11 +1]), sandbox_lib, sandbox_lib_len +1);
+#	endif
+    return new_ldp;
+}
+
 /*
  * Exec Wrappers
  */
 
 int
-execve(const char *filename, char *const argv[], char *const envp[])
+follow_execve(const char *filename, char *const argv[], char *const envp[])
 {
 	int old_errno = errno;
 	int result = -1;
 	int count = 0;
-	int env_len = 0;
 	char canonic[SB_PATH_MAX];
-	char **my_env = NULL;
-	/* We limit the size LD_PRELOAD can be here, but it should be enough */
-	char tmp_str[4096];
 
 	canonicalize_int(filename, canonic);
 
 	if FUNCTION_SANDBOX_SAFE
-		("execve", canonic) {
-		while (envp[count] != NULL) {
-			if (strstr(envp[count], "LD_PRELOAD=") == envp[count]) {
-				if (NULL != strstr(envp[count], sandbox_lib)) {
-					my_env = (char **) envp;
-					break;
-				} else {
-					int i = 0;
-					const int max_envp_len =
-							strlen(envp[count]) + strlen(sandbox_lib) + 1;
-
-					/* Fail safe ... */
-					if (max_envp_len > 4096) {
-						fprintf(stderr, "sandbox:  max_envp_len too big!\n");
-						errno = ENOMEM;
-						return result;
-					}
-
-					/* Calculate envp size */
-					my_env = (char **) envp;
-					do
-						env_len += 1;
-					while (*my_env++);
-
-					my_env = (char **) malloc((env_len + 2) * sizeof (char *));
-					if (NULL == my_env) {
-						errno = ENOMEM;
-						return result;
-					}
-					/* Copy envp to my_env */
-					do
-						my_env[i] = envp[i];
-					while (envp[i++]);
-
-					/* Set tmp_str to envp[count] */
-					strncpy(tmp_str, envp[count], max_envp_len - 1);
-
-					/* LD_PRELOAD already have variables other than sandbox_lib,
-					 * thus we have to add sandbox_lib seperated via a whitespace. */
-					if (0 != strncmp(envp[count], "LD_PRELOAD=", max_envp_len - 1)) {
-						strncat(tmp_str, " ", max_envp_len - strlen(tmp_str));
-						strncat(tmp_str, sandbox_lib, max_envp_len - strlen(tmp_str));
-					} else {
-						strncat(tmp_str, sandbox_lib, max_envp_len - strlen(tmp_str));
-					}
-
-					/* Valid string? */
-					tmp_str[max_envp_len] = '\0';
-
-					/* Ok, replace my_env[count] with our version that contains
-					 * sandbox_lib ... */
-					my_env[count] = tmp_str;
-
-					break;
+		("execve", canonic)
+	{
+		/* 4 cases:
+		 * (1) LD_PRELOAD already contains libsandbox.so,
+		 * (2) LD_PRELOAD does not contain libsandbox.so, but does have other "stuff"
+		 * (3) LD_PRELOAD ~ /^LD_PRELOAD= *$/, (sub-case of #2)
+		 * (4) LD_PRELOAD is not defined (case ignored by code below)
+		 */
+	    char **my_envp = (char**)envp;
+   	    char *new_ldp = NULL; /* our modified "LD_PRELOAD=/lib/libsandbox.so ..." */
+    	char *orig_ldp = NULL; /* original ptr containing "LD_PRELOAD=..." */
+#		ifdef ARCH_X86
+    	char **orig_ldp_ptr = NULL; /* ptr to ptr in envp for LD_PRELOAD */
+#       endif
+
+		while ((orig_ldp = envp[count]) && strncmp(orig_ldp, "LD_PRELOAD=", 11)) count++;
+		if (orig_ldp && !strstr(orig_ldp, sandbox_lib))
+		{
+            new_ldp = get_new_ldp(orig_ldp);
+            if (new_ldp == NULL) return result;
+#			ifdef ARCH_X86
+			    orig_ldp_ptr = (char**)&(envp[count]);
+				*orig_ldp_ptr = new_ldp;
+#			else /* Martin's fix for Bug #42290 */
+				/* Calculate envp size */
+				int env_len = 0, i = 0;
+				do
+					env_len += 1;
+				while (*my_envp++);
+
+				my_envp = (char **) malloc((env_len + 2) * sizeof (char *));
+				if (NULL == my_envp)
+				{
+					errno = ENOMEM;
+					return result;
 				}
-			}
-			count++;
+				/* Copy envp to my_env */
+				do
+					my_envp[i] = envp[i];
+				while (envp[i++]);
+
+				my_envp[count] = new_ldp;
+#			endif
 		}
 
+FILE *log=fopen("/tmp/jlog", "a"); // writing to stdout would mean that ebuild's use of `cmd` would capture this output as well
+fprintf(log,"INFO: pid=%d  true_execve(filename=%s; envp[%d] = '%s', new_ldp=%s: ",(int)getpid(),filename,count,my_envp[count],new_ldp);
+int i=0;
+while (argv[++i]) fprintf(log,"%s,",argv[i]);
+fputs("\n", log);
+fflush(log);
 		errno = old_errno;
 		check_dlsym(execve);
-		result = true_execve(filename, argv, my_env);
+		result = true_execve(filename, argv, my_envp);
 		old_errno = errno;
+fprintf(log,"ERROR: after true_execve()\n");
+fclose(log);
 
-		if (my_env) {
-			free(my_env);
-			my_env = NULL;
-		}
+		if (new_ldp)
+        {
+            free(new_ldp);
+#			ifdef ARCH_X86
+				*orig_ldp_ptr = orig_ldp;
+#			else
+				free(my_envp);
+			    my_envp = NULL;
+#			endif
+        }
 	}
 
 	errno = old_errno;
@@ -1270,6 +1321,8 @@
 is_sandbox_on()
 {
 	int old_errno = errno;
+    char *tmp_on = NULL;
+    char *tmp_active = NULL;
 
 	/* $SANDBOX_ACTIVE is an env variable that should ONLY
 	 * be used internal by sandbox.c and libsanbox.c.  External
@@ -1279,21 +1332,26 @@
 	 *
 	 * Azarah (3 Aug 2002)
 	 */
-	if ((NULL != getenv("SANDBOX_ON")) &&
-			(0 == strncmp(getenv("SANDBOX_ON"), "1", 1)) &&
-			(NULL != getenv("SANDBOX_ACTIVE")) &&
-			(0 == strncmp(getenv("SANDBOX_ACTIVE"), "armedandready", 13))
+	if ((NULL != (tmp_on = getenv("SANDBOX_ON"))) &&
+			(0 == strncmp(tmp_on, "1", 1)) &&
+			(NULL != (tmp_active = getenv("SANDBOX_ACTIVE"))) &&
+			(0 == strncmp(tmp_active, "armedandready", 13))
 			) {
-		errno = old_errno;
-
+	    errno = old_errno;
 		return 1;
-	} else {
-		errno = old_errno;
-
-		return 0;
-	}
+    }
+/* .. too noisy to notice any real violations of the sandbox,
+ * such as the 'env - script.sh' rule in a Makefile.
+    } else {
+        if (tmp_on == NULL) fputs(tmp_on, stderr);
+            fputs("INFO: SANDBOX_ON not defined", stderr);
+    }
+*/
+	errno = old_errno;
+    return 0;
 }
 
+
 static int
 before_syscall(const char *func, const char *file)
 {
@@ -1360,4 +1418,97 @@
 #include "getcwd.c"
 #include "canonicalize.c"
 
+#ifndef DEBUG
+#ifdef __GNUC__
+#if __GNUC_MINOR__ >= 3
+# define HIDDEN  __attribute__((visibility ("hidden")))
+# define USED    __attribute__((__used__))
+#else
+# define USED    __attribute__((__unused__))
+#endif
+#endif
+#endif
+
+#ifndef HIDDEN
+#define HIDDEN
+#endif
+
+#ifndef USED
+#define USED
+#endif
+
+static USED void __follow_alias_execve()
+{
+    asm("\t .global execve");
+    asm("\t .global _execve");
+    asm("\t .global __execve");
+    asm("\t execve = follow_execve");
+    asm("\t _execve = follow_execve");
+    asm("\t __execve = follow_execve");
+}
+
+#ifndef __linux
+	You are not using Linux.  I have only tested Athlon-XP with
+	Linux 2.6 kernel, gcc-3.3.3, and glibc-2.3.3_pre20040207.
+#else
+/*
+ * x86 platforms with Linux and glibc 2.? directly calls the kernel
+ * from execvp, instead of execve.  Thus, we need the following:
+ */
+
+
+extern char **__environ;
+
+static USED int follow_execvp(char *file, char *argv[])
+{
+	int old_errno = errno;
+    int result = -1;
+   	char *new_ldp = NULL; /* our modified "LD_PRELOAD=/lib/libsandbox.so ..." */
+   	char *orig_ldp = NULL; /* original ptr containing "LD_PRELOAD=..." */
+    orig_ldp = getenv("LD_PRELOAD");
+    if (orig_ldp == NULL || !strstr(orig_ldp, sandbox_lib))
+    {
+        new_ldp = get_new_ldp(orig_ldp);
+        if (new_ldp == NULL) return -1;
+        if (putenv(new_ldp))
+        {
+            perror("Unable to putenv new/fixed LD_PRELOAD");
+            return -1;
+        }
+    }
+
+FILE *log=fopen("/tmp/jlog", "a"); // writing to stdout would mean that ebuild's use of `cmd` would capture this output as well
+fprintf(log,"INFO: pid=%d  true_execVP(filename=%s; orig_ldp=%s, new_ldp=%s, getenv(LDP)=%s: ",(int)getpid(),file,orig_ldp,new_ldp,getenv("LD_PRELOAD"));
+int i=0;
+while (argv[++i]) fprintf(log,"%s, ",argv[i]);
+fputs("\n", log);
+fclose(log);
+	errno = old_errno;
+	check_dlsym(execvp);
+	result = true_execvp(file, argv);
+	old_errno = errno;
+fputs("ERROR:\e[31;01mafter real_execvp()\033[0m  \n", stderr);
+
+/* Since new_ldp has become part of the live environment, we should restore before free'ing it.
+ * Why write this code, since we exec'd above (if successful)?
+ * if (new_ldp)
+ *     free(new_ldp);
+ */
+	errno = old_errno;
+	return result;
+}
+
+static USED void __follow_alias_execvp()
+{
+    asm("\t .global execvp");
+    asm("\t .global _execvp");
+    asm("\t .global __execvp");
+    asm("\t execvp = follow_execvp");
+    asm("\t _execvp = follow_execvp");
+    asm("\t __execvp = follow_execvp");
+}
+
+
+#endif
+
 // vim:expandtab noai:cindent ai
diff -ruN portage-2.0.50-r3/src/sandbox-1.1/sandbox.c portage-2.0.50-r3.GB/src/sandbox-1.1/sandbox.c
--- portage-2.0.50-r3/src/sandbox-1.1/sandbox.c	2004-04-04 13:37:21.000000000 -0700
+++ portage-2.0.50-r3.GB/src/sandbox-1.1/sandbox.c	2004-04-07 14:54:41.130977352 -0700
@@ -35,7 +35,7 @@
 
 int preload_adaptable = 1;
 int cleaned_up = 0;
-int print_debug = 0;
+int print_debug = 1;
 
 /* Read pids file, and load active pids into an array.  Return number of pids in array */
 int
@@ -462,7 +462,7 @@
 		//printf("%s\n", argv_bash[i]);
 		i++;
 	}
-	printf("%s\n", sh);
+	printf("spawn_shell(): %s\n", sh);
 	ret = system(sh);
 	if (sh)
 		free(sh);
@@ -480,10 +480,18 @@
 
 	pid = fork();
 
+//FILE *log=fopen("/tmp/jlog2", "a");
+//fprintf(log,">>>pid=%d  sandbox BEFORE execv(",(int)getpid());
+//fflush(log);
 	/* Child's process */
 	if (0 == pid) {
 # endif
-		execv(argv_bash[0], argv_bash);
+//int i=-1;
+//while (argv_bash[++i]) fprintf(log,"%s,",argv_bash[i]);
+//fputs("\n", log);
+//fflush(log);
+    	execv(argv_bash[0], argv_bash);
+//fputs("*** ERROR: AFTER execv\n", log);
 # ifndef NO_FORK
 		return 0;
 	} else if (pid < 0) {
@@ -491,7 +499,13 @@
 	}
 	ret = waitpid(pid, &status, 0);
 	if ((-1 == ret) || (status > 0))
+    {
+        if (WIFEXITED(status))
+        fprintf(stderr, "sandbox ERROR: waitpid reported error: ret=%d; status=%d\n", ret, WEXITSTATUS(status));
 		return 0;
+    }
+//fprintf(log, "sandbox: After waitpid for execv(%s,..)\n", argv_bash[0]);
+//fclose(log);
 # endif
 	return 1;
 #endif
@@ -728,7 +742,7 @@
 
 		argv_bash = (char **) malloc(6 * sizeof (char *));
 		argv_bash[0] = strdup("/bin/bash");
-		argv_bash[1] = strdup("-rcfile");
+		argv_bash[1] = strdup("--rcfile");
 		argv_bash[2] = strdup(sandbox_rc);
 
 		if (argc < 2)
@@ -906,5 +920,3 @@
 			return 0;
 	}
 }
-
-// vim:expandtab noai:cindent ai

[-- Attachment #3: Type: text/plain, Size: 45 bytes --]

--
gentoo-portage-dev@gentoo.org mailing list

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2004-04-12 19:44 UTC | newest]

Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2004-04-12 19:43 [gentoo-portage-dev] [gavin@vess.com: Hardening the Gentoo Portage Sandbox [rev 2 - edits re: glibc's use of weak aliases]] Nick Jones

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox