aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPeter Seebach <peter.seebach@windriver.com>2010-04-26 17:03:10 -0700
committerPeter Seebach <peter.seebach@windriver.com>2010-04-26 17:13:45 -0700
commitb0dc7011bb3efb160223fd759544de9987129d99 (patch)
treedde56392a990d0a01627a305be5cbe7236983a67
parentcc00abcd92f3d1c491d262266115dcee71b9a7a1 (diff)
downloadpseudo-b0dc7011bb3efb160223fd759544de9987129d99.tar.gz
pseudo-b0dc7011bb3efb160223fd759544de9987129d99.tar.bz2
pseudo-b0dc7011bb3efb160223fd759544de9987129d99.zip
Handle execve() better
You can't use setenv() to modify the environment that will be passed to a child process through execve()... Also, fix the setupenv() to use PSEUDO_SUFFIX if defined. Use execve() to spawn child processes, so we can use setupenv() and dropenv().
-rw-r--r--guts/execve.c9
-rw-r--r--guts/fork.c1
-rw-r--r--pseudo.c41
-rw-r--r--pseudo.h4
-rw-r--r--pseudo_client.c9
-rw-r--r--pseudo_util.c200
6 files changed, 197 insertions, 67 deletions
diff --git a/guts/execve.c b/guts/execve.c
index 1dda551..ccbc9c7 100644
--- a/guts/execve.c
+++ b/guts/execve.c
@@ -6,14 +6,19 @@
* wrap_execve(const char *filename, char *const *argv, char *const *envp) {
* int rc = -1;
*/
+ char * const *new_environ;
/* note: we don't canonicalize this, because we are intentionally
* NOT redirecting execs into the chroot environment. If you try
* to execute /bin/sh, you get the actual /bin/sh, not
* <CHROOT>/bin/sh. This allows use of basic utilities. This
* design will likely be revisited.
*/
- pseudo_client_op(OP_EXEC, PSA_EXEC, 0, 0, filename, 0);
- rc = real_execve(filename, argv, envp);
+ pseudo_client_op(OP_EXEC, PSA_EXEC, -1, 0, filename, 0);
+ if (!getenv("PSEUDO_RELOADED"))
+ new_environ = pseudo_setupenv(envp, getenv("PSEUDO_OPTS"));
+ else
+ new_environ = envp;
+ rc = real_execve(filename, argv, new_environ);
/* return rc;
* }
diff --git a/guts/fork.c b/guts/fork.c
index f4eb7f6..4a91f7b 100644
--- a/guts/fork.c
+++ b/guts/fork.c
@@ -6,7 +6,6 @@
* wrap_fork(void) {
* int rc = -1;
*/
-
rc = real_fork();
if (rc == 0)
pseudo_client_reset();
diff --git a/pseudo.c b/pseudo.c
index ba0b6d0..1868cf2 100644
--- a/pseudo.c
+++ b/pseudo.c
@@ -83,6 +83,7 @@ main(int argc, char *argv[]) {
if (ld_env && strstr(ld_env, "libpseudo")) {
extern char **environ;
+ char **new_environ;
pseudo_debug(2, "can't run daemon with libpseudo in LD_PRELOAD\n");
if (getenv("PSEUDO_RELOADED")) {
@@ -90,8 +91,8 @@ main(int argc, char *argv[]) {
exit(EXIT_FAILURE);
}
setenv("PSEUDO_RELOADED", "YES", 1);
- pseudo_dropenv();
- execve(argv[0], argv, environ);
+ new_environ = pseudo_dropenv(environ);
+ execve(argv[0], argv, new_environ);
exit(EXIT_FAILURE);
}
unsetenv("PSEUDO_RELOADED");
@@ -202,6 +203,11 @@ main(int argc, char *argv[]) {
exit(EXIT_FAILURE);
}
} else {
+ char fullpath[pseudo_path_max()];
+ char *path;
+ extern char **environ;
+ char **new_environ;
+
if (opt_r) {
if (chdir(opt_r) == -1) {
pseudo_diag("failed to chdir to '%s': %s\n",
@@ -225,8 +231,35 @@ main(int argc, char *argv[]) {
}
/* build an environment containing libpseudo */
pseudo_debug(2, "setting up pseudo environment.\n");
- pseudo_setupenv(opts);
- rc = execvp(argv[0], argv);
+ new_environ = pseudo_setupenv(environ, opts);
+ if (strchr(argv[0], '/')) {
+ snprintf(fullpath, pseudo_path_max(), "%s", argv[0]);
+ } else {
+ int found = 0;
+ if ((path = getenv("PATH")) == NULL)
+ path = "/bin:/usr/bin";
+ while (*path) {
+ struct stat64 buf;
+ int len = strcspn(path, ":");
+ snprintf(fullpath, pseudo_path_max(), "%.*s/%s",
+ len, path, argv[0]);
+ path += len;
+ if (*path == ':')
+ ++path;
+ if (!stat64(fullpath, &buf)) {
+ if (buf.st_mode & 0111) {
+ found = 1;
+ break;
+ }
+ }
+ }
+ if (!found) {
+ pseudo_diag("Can't find '%s' in $PATH.\n",
+ argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ }
+ rc = execve(fullpath, argv, new_environ);
if (rc == -1) {
pseudo_diag("pseudo: can't run %s: %s\n",
argv[0], strerror(errno));
diff --git a/pseudo.h b/pseudo.h
index 1eed2ce..26460ac 100644
--- a/pseudo.h
+++ b/pseudo.h
@@ -121,8 +121,8 @@ void pseudo_new_pid(void);
/* pseudo_fix_path resolves symlinks up to this depth */
#define PSEUDO_MAX_LINK_RECURSION 16
extern char *pseudo_fix_path(const char *, const char *, size_t, size_t, size_t *, int);
-extern void pseudo_dropenv(void);
-extern void pseudo_setupenv(char *);
+extern char **pseudo_dropenv(char * const *);
+extern char **pseudo_setupenv(char * const *, char *);
extern char *pseudo_prefix_path(char *);
extern char *pseudo_get_prefix(char *);
extern ssize_t pseudo_sys_path_max(void);
diff --git a/pseudo_client.c b/pseudo_client.c
index 1638212..f1ec54d 100644
--- a/pseudo_client.c
+++ b/pseudo_client.c
@@ -403,6 +403,7 @@ client_spawn_server(void) {
} else {
char *base_args[] = { NULL, NULL, NULL };
char **argv;
+ char **new_environ;
int args;
int fd;
@@ -437,9 +438,6 @@ client_spawn_server(void) {
} else {
argv = base_args;
}
- pseudo_dropenv();
- pseudo_debug(4, "calling execve on %s\n", argv[0]);
- /* and now, execute the server */
if (fchdir(pseudo_dir_fd)) {
pseudo_diag("Couldn't change to server dir [%d]: %s\n",
pseudo_dir_fd, strerror(errno));
@@ -458,7 +456,10 @@ client_spawn_server(void) {
if (fd != pseudo_util_debug_fd)
close(fd);
}
- execve(argv[0], argv, environ);
+ /* and now, execute the server */
+ new_environ = pseudo_dropenv(environ);
+ pseudo_debug(4, "calling execve on %s\n", argv[0]);
+ execve(argv[0], argv, new_environ);
pseudo_diag("critical failure: exec of pseudo daemon failed: %s\n", strerror(errno));
exit(1);
}
diff --git a/pseudo_util.c b/pseudo_util.c
index 62ad848..388ff50 100644
--- a/pseudo_util.c
+++ b/pseudo_util.c
@@ -47,6 +47,7 @@ static int pseudo_append_elements(char **newpath, char **root, size_t *allocated
extern char **environ;
static ssize_t pseudo_max_pathlen = -1;
static ssize_t pseudo_sys_max_pathlen = -1;
+static char *libpseudo_name = "libpseudo" PSEUDO_SUFFIX ".so";
char *pseudo_version = PSEUDO_VERSION;
@@ -358,75 +359,166 @@ pseudo_fix_path(const char *base, const char *path, size_t rootlen, size_t basel
/* remove the pseudo stuff from the environment (leaving other preloads
* alone).
+ * There's an implicit memory leak here, but this is called only right
+ * before an exec(), or at most once in a given run.
+ *
+ * we don't try to fix the library path.
*/
-void
-pseudo_dropenv(void) {
- char *ld_env = getenv("LD_PRELOAD");
-
- /* why do this two ways? Because calling setenv(), then execing,
- * in bash seems to result in the variables still being set in the
- * new environment.
- * we remove the entire LD_PRELOAD, because our use case would have
- * fakechroot and fakepasswd in it too -- and we don't want that.
- * we don't touch LD_LIBRARY_PATH because it might be being used for
- * other system libraries...
- */
- if (ld_env) {
- unsetenv("LD_PRELOAD");
+char **
+pseudo_dropenv(char * const *environ) {
+ char **new_environ;
+ int env_count = 0, found_preload = 0;
+ int i, j;
+
+ for (i = 0; environ[i]; ++i) {
+ if (!memcmp(environ[i], "LD_PRELOAD=", 11))
+ found_preload = 1;
+ ++env_count;
+ }
+ new_environ = malloc((env_count + 1) * sizeof(*new_environ));
+ if (!new_environ) {
+ pseudo_diag("fatal: can't allocate new environment.\n");
+ return NULL;
+ }
+ j = 0;
+ for (i = 0; environ[i]; ++i) {
+ if (!memcmp(environ[i], "LD_PRELOAD=", 11)) {
+ char *s = environ[i] + 11;
+ char *p;
+ if (!strcmp(s, libpseudo_name)) {
+ /* drop it completely */
+ } else if ((p = strstr(s, libpseudo_name)) != NULL) {
+ char *without = strdup(environ[i]);
+ if (!without) {
+ pseudo_diag("fatal: can't allocate new environment variable.\n");
+ return 0;
+ }
+ p = strstr(without, libpseudo_name);
+ /* strip out that hunk */
+ memmove(p, p + strlen(libpseudo_name),
+ strlen(p) - strlen(libpseudo_name) + 1);
+ new_environ[j++] = without;
+ } else {
+ /* leave it alone */
+ new_environ[j++] = environ[i];
+ }
+ } else {
+ new_environ[j++] = environ[i];
+ }
}
+ new_environ[j++] = NULL;
+ return new_environ;
}
/* add pseudo stuff to the environment.
+ * We can't just use setenv(), because one use case is that we're trying
+ * to modify the environment of a process about to be forked through
+ * exec().
*/
-void
-pseudo_setupenv(char *opts) {
- char *ld_env;
- char *newenv;
+char **
+pseudo_setupenv(char * const *environ, char *opts) {
+ char **new_environ;
+ int env_count = 0;
+ int found_preload = 0, found_libpath = 0, found_debug = 0, found_opts = 0;
+ int i, j;
size_t len;
- char debugvalue[64];
-
- newenv = "libpseudo.so";
- setenv("LD_PRELOAD", newenv, 1);
-
- ld_env = getenv("LD_LIBRARY_PATH");
- if (ld_env) {
- char *prefix = pseudo_prefix_path(NULL);
- if (!strstr(ld_env, prefix)) {
- char *e1, *e2;
- e1 = pseudo_prefix_path("lib");
- e2 = pseudo_prefix_path("lib64");
- len = strlen(ld_env) + strlen(e1) + strlen(e2) + 3;
- newenv = malloc(len);
- snprintf(newenv, len, "%s:%s:%s", ld_env, e1, e2);
- free(e1);
- free(e2);
- setenv("LD_LIBRARY_PATH", newenv, 1);
- free(newenv);
+ char *newenv;
+
+ for (i = 0; environ[i]; ++i) {
+ if (!memcmp(environ[i], "LD_PRELOAD=", 11))
+ found_preload = 1;
+ if (!memcmp(environ[i], "PSEUDO_OPTS=", 12))
+ found_opts = 1;
+ if (!memcmp(environ[i], "PSEUDO_DEBUG=", 13))
+ found_debug = 1;
+ if (!memcmp(environ[i], "LD_LIBRARY_PATH=", 16))
+ found_libpath = 1;
+ ++env_count;
+ }
+ env_count += 4 - (found_preload + found_libpath + found_debug + found_opts);
+
+ new_environ = malloc((env_count + 1) * sizeof(*new_environ));
+ if (!new_environ) {
+ pseudo_diag("fatal: can't allocate new environment.\n");
+ return NULL;
+ }
+
+ j = 0;
+ for (i = 0; environ[i]; ++i) {
+ if (!memcmp(environ[i], "LD_PRELOAD=", 11)) {
+ if (!strstr(environ[i], libpseudo_name)) {
+ len = strlen(environ[i]) + strlen(libpseudo_name) + 2;
+ newenv = malloc(len);
+ if (!newenv) {
+ pseudo_diag("fatal: can't allocate new environment variable.\n");
+ }
+ snprintf(newenv, len, "%s %s", environ[i], libpseudo_name);
+ new_environ[j++] = newenv;
+ } else {
+ new_environ[j++] = environ[i];
+ }
+ } else if (!memcmp(environ[i], "LD_LIBRARY_PATH=", 16)) {
+ if (!strstr(environ[i], PSEUDO_PREFIX)) {
+ char *e1, *e2;
+ e1 = pseudo_prefix_path("lib");
+ e2 = pseudo_prefix_path("lib64");
+ len = strlen(environ[i]) + strlen(e1) + strlen(e2) + 3;
+ newenv = malloc(len);
+ if (!newenv) {
+ pseudo_diag("fatal: can't allocate new environment variable.\n");
+ }
+ snprintf(newenv, len, "%s:%s:%s", environ[i], e1, e2);
+ free(e1);
+ free(e2);
+ new_environ[j++] = newenv;
+ } else {
+ new_environ[j++] = environ[i];
+ }
+ } else {
+ new_environ[j++] = environ[i];
}
- free(prefix);
- } else {
+ }
+ if (!found_libpath) {
char *e1, *e2;
e1 = pseudo_prefix_path("lib");
e2 = pseudo_prefix_path("lib64");
- len = strlen(e1) + strlen(e2) + 2;
+ len = 16 + strlen(e1) + strlen(e2) + 2;
newenv = malloc(len);
- snprintf(newenv, len, "%s:%s", e1, e2);
- setenv("LD_LIBRARY_PATH", newenv, 1);
- free(newenv);
+ if (!newenv) {
+ pseudo_diag("fatal: can't allocate new environment variable.\n");
+ }
+ snprintf(newenv, len, "LD_LIBRARY_PATH=%s:%s", e1, e2);
+ new_environ[j++] = newenv;
}
-
- if (max_debug_level) {
- sprintf(debugvalue, "%d", max_debug_level);
- setenv("PSEUDO_DEBUG", debugvalue, 1);
- } else {
- unsetenv("PSEUDO_DEBUG");
+ if (!found_preload) {
+ len = 11 + strlen(libpseudo_name) + 1;
+ newenv = malloc(len);
+ if (!newenv) {
+ pseudo_diag("fatal: can't allocate new environment variable.\n");
+ }
+ snprintf(newenv, len, "LD_PRELOAD=%s", libpseudo_name);
+ new_environ[j++] = newenv;
}
-
- if (opts) {
- setenv("PSEUDO_OPTS", opts, 1);
- } else {
- unsetenv("PSEUDO_OPTS");
+ if (!found_debug && max_debug_level > 0) {
+ len = 16;
+ newenv = malloc(len);
+ if (!newenv) {
+ pseudo_diag("fatal: can't allocate new environment variable.\n");
+ }
+ snprintf(newenv, len, "PSEUDO_DEBUG=%d", max_debug_level);
+ new_environ[j++] = newenv;
+ }
+ if (!found_opts && opts) {
+ len = 12 + strlen(opts) + 1;
+ newenv = malloc(len);
+ if (!newenv) {
+ pseudo_diag("fatal: can't allocate new environment variable.\n");
+ }
+ snprintf(newenv, len, "PSEUDO_OPTS=%s", opts);
+ new_environ[j++] = newenv;
}
+ new_environ[j++] = NULL;
+ return new_environ;
}
/* get the full path to a file under $PSEUDO_PREFIX. Other ways of