diff options
-rw-r--r-- | guts/execve.c | 9 | ||||
-rw-r--r-- | guts/fork.c | 1 | ||||
-rw-r--r-- | pseudo.c | 41 | ||||
-rw-r--r-- | pseudo.h | 4 | ||||
-rw-r--r-- | pseudo_client.c | 9 | ||||
-rw-r--r-- | pseudo_util.c | 200 |
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(); @@ -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)); @@ -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 |