diff options
-rw-r--r-- | ChangeLog.txt | 3 | ||||
-rw-r--r-- | Makefile.in | 2 | ||||
-rw-r--r-- | guts/execv.c | 8 | ||||
-rw-r--r-- | guts/execve.c | 11 | ||||
-rw-r--r-- | guts/execvp.c | 8 | ||||
-rw-r--r-- | pseudo.c | 53 | ||||
-rw-r--r-- | pseudo.h | 12 | ||||
-rw-r--r-- | pseudo_client.c | 44 | ||||
-rw-r--r-- | pseudo_db.h | 1 | ||||
-rw-r--r-- | pseudo_util.c | 559 | ||||
-rw-r--r-- | pseudo_wrappers.c | 21 | ||||
-rw-r--r-- | pseudolog.c | 2 | ||||
-rwxr-xr-x | test/test-env_i.sh | 11 |
13 files changed, 455 insertions, 280 deletions
diff --git a/ChangeLog.txt b/ChangeLog.txt index 044afcb..dc6ba14 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -1,6 +1,9 @@ 2010-08-10: * (mhatle) add execl, execle, execlp, execv, and execvp wrappers * (seebs) handle ... for execl, etc. + * (mhatle) add a local cache of variables, instead of using environ + * (mhatle) rewrite pseudo_setupenv, pseudo_dropenv routines + we now support running "/usr/bin/env -i env" in pseudo! 2010-08-06: * (mhatle) Fix an exec program with an empty environment diff --git a/Makefile.in b/Makefile.in index ca282c0..b9163b3 100644 --- a/Makefile.in +++ b/Makefile.in @@ -59,7 +59,7 @@ LIBPSEUDO=$(LIB)/libpseudo$(SUFFIX).so all: $(LIBPSEUDO) $(PSEUDO) $(PSEUDODB) $(PSEUDOLOG) test: all $(BIN) $(LIB) $(LOCALSTATE) - @./run_tests.sh + @./run_tests.sh -v install-lib: $(LIBPSEUDO) mkdir -p $(DESTDIR)$(LIBDIR) diff --git a/guts/execv.c b/guts/execv.c index 3fec52a..59a3f96 100644 --- a/guts/execv.c +++ b/guts/execv.c @@ -6,7 +6,13 @@ * wrap_execv(const char *path, char *const *argv) { * int rc = -1; */ - environ = pseudo_setupenv(environ, getenv("PSEUDO_OPTS")); + + if (!pseudo_get_value("PSEUDO_RELOADED")) + pseudo_setupenv(); + else { + pseudo_setupenv(); + pseudo_dropenv(); + } rc = real_execv(path, argv); diff --git a/guts/execve.c b/guts/execve.c index 2bad0bf..7cb6632 100644 --- a/guts/execve.c +++ b/guts/execve.c @@ -14,10 +14,13 @@ * design will likely be revisited. */ pseudo_client_op(OP_EXEC, PSA_EXEC, -1, -1, filename, 0); - if (!getenv("PSEUDO_RELOADED")) - new_environ = pseudo_setupenv(envp, getenv("PSEUDO_OPTS")); - else - new_environ = envp; + if (!pseudo_get_value("PSEUDO_RELOADED")) + new_environ = pseudo_setupenvp(envp); + else { + new_environ = pseudo_setupenvp(envp); + new_environ = pseudo_dropenvp(new_environ); + } + rc = real_execve(filename, argv, new_environ); /* return rc; diff --git a/guts/execvp.c b/guts/execvp.c index 64e4b4b..1ab34d3 100644 --- a/guts/execvp.c +++ b/guts/execvp.c @@ -6,7 +6,13 @@ * wrap_execvp(const char *file, char *const *argv) { * int rc = -1; */ - environ = pseudo_setupenv(environ, getenv("PSEUDO_OPTS")); + + if (!pseudo_get_value("PSEUDO_RELOADED")) + pseudo_setupenv(); + else { + pseudo_setupenv(); + pseudo_dropenv(); + } rc = real_execvp(file, argv); @@ -73,30 +73,32 @@ main(int argc, char *argv[]) { opts[0] = '\0'; - s = getenv("PSEUDO_DEBUG"); + s = pseudo_get_value("PSEUDO_DEBUG"); if (s) { int level = atoi(s); for (o = 0; o < level; ++o) { pseudo_debug_verbose(); } } + free(s); 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")) { + s = pseudo_get_value("PSEUDO_RELOADED"); + if (s) { pseudo_diag("I can't seem to make LD_PRELOAD go away. Sorry.\n"); pseudo_diag("LD_PRELOAD: %s\n", ld_env); exit(EXIT_FAILURE); } - setenv("PSEUDO_RELOADED", "YES", 1); - new_environ = pseudo_dropenv(environ); - execve(argv[0], argv, new_environ); + free(s); + pseudo_set_value("PSEUDO_RELOADED", "YES"); + pseudo_setupenv(); + pseudo_dropenv(); /* Drop LD_PRELOAD */ + + execv(argv[0], argv); exit(EXIT_FAILURE); } - unsetenv("PSEUDO_RELOADED"); + pseudo_set_value("PSEUDO_RELOADED", NULL); /* we need cwd to canonicalize paths */ pseudo_client_getcwd(); @@ -131,20 +133,20 @@ main(int argc, char *argv[]) { s = PSEUDO_ROOT_PATH(AT_FDCWD, optarg, AT_SYMLINK_NOFOLLOW); if (!s) pseudo_diag("Can't resolve passwd path '%s'\n", optarg); - setenv("PSEUDO_PASSWD", s, 1); + pseudo_set_value("PSEUDO_PASSWD", s); break; case 'P': s = PSEUDO_ROOT_PATH(AT_FDCWD, optarg, AT_SYMLINK_NOFOLLOW); if (!s) pseudo_diag("Can't resolve prefix path '%s'\n", optarg); - setenv("PSEUDO_PREFIX", s, 1); + pseudo_set_value("PSEUDO_PREFIX", s); break; case 'r': /* FALLTHROUGH */ case 'R': s = PSEUDO_ROOT_PATH(AT_FDCWD, optarg, AT_SYMLINK_NOFOLLOW); if (!s) pseudo_diag("Can't resolve root path '%s'\n", optarg); - setenv("PSEUDO_CHROOT", s, 1); + pseudo_set_value("PSEUDO_CHROOT", s); if (o == 'r') opt_r = s; break; @@ -178,27 +180,14 @@ main(int argc, char *argv[]) { break; } } + /* Options are processed, preserve them... */ + pseudo_set_value("PSEUDO_OPTS", opts); if (!pseudo_get_prefix(argv[0])) { pseudo_diag("Can't figure out prefix. Set PSEUDO_PREFIX or invoke with full path.\n"); exit(EXIT_FAILURE); } - if (!pseudo_get_bindir()) { - pseudo_diag("Can't figure out bindir. Set PSEUDO_BINDIR.\n"); - exit(EXIT_FAILURE); - } - - if (!pseudo_get_libdir()) { - pseudo_diag("Can't figure out libdir. Set PSEUDO_LIBDIR.\n"); - exit(EXIT_FAILURE); - } - - if (!pseudo_get_localstatedir()) { - pseudo_diag("Can't figure out localstatedir. Set PSEUDO_LOCALSTATEDIR.\n"); - exit(EXIT_FAILURE); - } - if (opt_C) { return pseudo_db_check(); } @@ -220,8 +209,6 @@ main(int argc, char *argv[]) { } else { char fullpath[pseudo_path_max()]; char *path; - extern char **environ; - char **new_environ; if (opt_r) { if (chdir(opt_r) == -1) { @@ -244,9 +231,7 @@ main(int argc, char *argv[]) { argv[0] = "/bin/sh"; argv[1] = NULL; } - /* build an environment containing libpseudo */ - pseudo_debug(2, "setting up pseudo environment.\n"); - new_environ = pseudo_setupenv(environ, opts); + if (strchr(argv[0], '/')) { snprintf(fullpath, pseudo_path_max(), "%s", argv[0]); } else { @@ -274,7 +259,9 @@ main(int argc, char *argv[]) { exit(EXIT_FAILURE); } } - rc = execve(fullpath, argv, new_environ); + pseudo_setupenv(); + + rc = execv(fullpath, argv); if (rc == -1) { pseudo_diag("pseudo: can't run %s: %s\n", argv[0], strerror(errno)); @@ -20,6 +20,12 @@ #include <stdlib.h> #include <fcntl.h> +struct pseudo_variables { char * key; size_t key_len; char * value; }; + +void pseudo_dump_env(char **envp); +int pseudo_set_value(const char * key, const char * value); +char * pseudo_get_value(const char * key); + typedef enum { OP_UNKNOWN = -1, OP_NONE = 0, @@ -121,8 +127,10 @@ 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 char **pseudo_dropenv(char * const *); -extern char **pseudo_setupenv(char * const *, char *); +extern void pseudo_dropenv(void); +extern char **pseudo_dropenvp(char * const *); +extern void pseudo_setupenv(void); +extern char **pseudo_setupenvp(char * const *); extern char *pseudo_prefix_path(char *); extern char *pseudo_bindir_path(char *); extern char *pseudo_libdir_path(char *); diff --git a/pseudo_client.c b/pseudo_client.c index e6bfa76..c933329 100644 --- a/pseudo_client.c +++ b/pseudo_client.c @@ -173,7 +173,7 @@ pseudo_client_touchuid(void) { static char uidbuf[256]; snprintf(uidbuf, 256, "%d,%d,%d,%d", pseudo_ruid, pseudo_euid, pseudo_suid, pseudo_fuid); - setenv("PSEUDO_UIDS", uidbuf, 1); + pseudo_set_value("PSEUDO_UIDS", uidbuf); } void @@ -181,7 +181,7 @@ pseudo_client_touchgid(void) { static char gidbuf[256]; snprintf(gidbuf, 256, "%d,%d,%d,%d", pseudo_rgid, pseudo_egid, pseudo_sgid, pseudo_fgid); - setenv("PSEUDO_GIDS", gidbuf, 1); + pseudo_set_value("PSEUDO_GIDS", gidbuf); } int @@ -193,7 +193,7 @@ pseudo_client_chroot(const char *path) { if (!strcmp(path, "/")) { pseudo_chroot_len = 0; pseudo_chroot = 0; - unsetenv("PSEUDO_CHROOT"); + pseudo_set_value("PSEUDO_CHROOT", NULL); return 0; } /* allocate new value */ @@ -206,7 +206,7 @@ pseudo_client_chroot(const char *path) { return -1; } memcpy(pseudo_chroot, path, pseudo_chroot_len + 1); - setenv("PSEUDO_CHROOT", pseudo_chroot, 1); + pseudo_set_value("PSEUDO_CHROOT", pseudo_chroot); return 0; } @@ -320,19 +320,21 @@ pseudo_client_reset() { if (!pseudo_inited) { char *env; - env = getenv("PSEUDO_UIDS"); + env = pseudo_get_value("PSEUDO_UIDS"); if (env) sscanf(env, "%d,%d,%d,%d", &pseudo_ruid, &pseudo_euid, &pseudo_suid, &pseudo_fuid); + free(env); - env = getenv("PSEUDO_GIDS"); + env = pseudo_get_value("PSEUDO_GIDS"); if (env) sscanf(env, "%d,%d,%d,%d", &pseudo_rgid, &pseudo_egid, &pseudo_sgid, &pseudo_fuid); + free(env); - env = getenv("PSEUDO_CHROOT"); + env = pseudo_get_value("PSEUDO_CHROOT"); if (env) { pseudo_chroot = strdup(env); if (pseudo_chroot) { @@ -341,11 +343,13 @@ pseudo_client_reset() { pseudo_diag("can't store chroot path (%s)\n", env); } } + free(env); - env = getenv("PSEUDO_PASSWD"); + env = pseudo_get_value("PSEUDO_PASSWD"); if (env) { pseudo_passwd = strdup(env); } + free(env); pseudo_inited = 1; } @@ -358,7 +362,6 @@ static int client_spawn_server(void) { int status; FILE *fp; - extern char **environ; char * pseudo_pidfile; if ((server_pid = fork()) != 0) { @@ -390,15 +393,14 @@ client_spawn_server(void) { } else { char *base_args[] = { NULL, NULL, NULL }; char **argv; - char **new_environ; + char *option_string = pseudo_get_value("PSEUDO_OPTS"); int args; int fd; pseudo_new_pid(); base_args[0] = pseudo_bindir_path("pseudo"); base_args[1] = "-d"; - if (getenv("PSEUDO_OPTS")) { - char *option_string = strdup(getenv("PSEUDO_OPTS")); + if (option_string) { char *s; int arg; @@ -425,6 +427,7 @@ client_spawn_server(void) { } else { argv = base_args; } + /* close any higher-numbered fds which might be open, * such as sockets. We don't have to worry about 0 and 1; * the server closes them already, and more importantly, @@ -440,9 +443,14 @@ client_spawn_server(void) { close(fd); } /* 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_set_value("PSEUDO_RELOADED", "YES"); + pseudo_setupenv(); + pseudo_dropenv(); /* drop LD_PRELOAD */ + + pseudo_debug(4, "calling execv on %s\n", argv[0]); + + execv(argv[0], argv); pseudo_diag("critical failure: exec of pseudo daemon failed: %s\n", strerror(errno)); exit(1); } @@ -453,18 +461,19 @@ client_ping(void) { pseudo_msg_t ping; pseudo_msg_t *ack; char tagbuf[pseudo_path_max()]; - char *tag = getenv("PSEUDO_TAG"); + char *tag = pseudo_get_value("PSEUDO_TAG"); ping.type = PSEUDO_MSG_PING; ping.op = OP_NONE; if (!tag) - tag = ""; + tag = strdup(""); ping.pathlen = snprintf(tagbuf, sizeof(tagbuf), "%s%c%s", program_invocation_name ? program_invocation_name : "<unknown>", 0, tag); + free(tag); ping.client = getpid(); ping.result = 0; errno = 0; @@ -570,7 +579,6 @@ pseudo_client_setup(void) { char * pseudo_pidfile; FILE *fp; server_pid = 0; - int cwd_fd; /* avoid descriptor leak, I hope */ if (connect_fd >= 0) { diff --git a/pseudo_db.h b/pseudo_db.h index 77ff5b5..2e7dbb9 100644 --- a/pseudo_db.h +++ b/pseudo_db.h @@ -43,6 +43,7 @@ extern int pdb_unlink_file_dev(pseudo_msg_t *msg); extern int pdb_update_file(pseudo_msg_t *msg); extern int pdb_update_file_path(pseudo_msg_t *msg); extern int pdb_update_inode(pseudo_msg_t *msg); +extern int pdb_unlink_contents(pseudo_msg_t *msg); extern int pdb_rename_file(const char *oldpath, pseudo_msg_t *msg); extern int pdb_find_file_exact(pseudo_msg_t *msg); extern int pdb_find_file_path(pseudo_msg_t *msg); diff --git a/pseudo_util.c b/pseudo_util.c index 889630e..b44f000 100644 --- a/pseudo_util.c +++ b/pseudo_util.c @@ -33,6 +33,114 @@ #include "pseudo_ipc.h" #include "pseudo_db.h" +/* The order below is not arbitrary, but based on the assumption + * of how often things will be used. + */ +const struct pseudo_variables pseudo_env[] = { + { "PSEUDO_PREFIX", 13, NULL }, + { "PSEUDO_BINDIR", 13, NULL }, + { "PSEUDO_LIBDIR", 13, NULL }, + { "PSEUDO_LOCALSTATEDIR", 20, NULL }, + { "PSEUDO_PASSWD", 13, NULL }, + { "PSEUDO_CHROOT", 13, NULL }, + { "PSEUDO_UIDS", 11, NULL }, + { "PSEUDO_GIDS", 11, NULL }, + { "PSEUDO_OPTS", 11, NULL }, + { "PSEUDO_DEBUG", 12, NULL }, + { "PSEUDO_DEBUG_FILE", 17, NULL }, + { "PSEUDO_TAG", 10, NULL }, + { "PSEUDO_ENOSYS_ABORT", 19, NULL }, + { "PSEUDO_NOSYMLINKEXP", 19, NULL }, + { "PSEUDO_RELOADED", 15, NULL }, + { NULL, 0, NULL } /* Magic terminator */ +}; + +/* -1 - init hasn't been run yet + * 0 - init has been run + * 1 - init is running + * + * There are cases where the constructor is run AFTER the + * program starts playing with things, so we need to do our + * best to handle that case. + */ +int _in_init = -1; /* Not yet run */ + +static void _libpseudo_init(void) __attribute__ ((constructor)); + +void dump_env(char **envp) { + size_t i = 0; + for (i = 0; envp[i]; i++) { + pseudo_debug(0,"dump_envp: [%d]%s\n", i,envp[i]); + } + + for (i = 0; pseudo_env[i].key; i++) { + pseudo_debug(0,"dump_envp: {%d}%s=%s\n", i, pseudo_env[i].key, pseudo_env[i].value); + } + + pseudo_debug(0, "dump_envp: _in_init %d\n", _in_init); +} + +/* Caller must free memory! */ +char * pseudo_get_value(const char * key) { + size_t i = 0; + char * value; + + if (_in_init == -1) _libpseudo_init(); + + for (i = 0; pseudo_env[i].key && memcmp(pseudo_env[i].key, key, pseudo_env[i].key_len + 1); i++) ; + + /* Check if the environment has it and we don't ... + * if so, something went wrong... so we'll attempt to recover + */ + if (pseudo_env[i].key && !pseudo_env[i].value && getenv(pseudo_env[i].key)) + _libpseudo_init(); + + if (pseudo_env[i].value) value = strdup(pseudo_env[i].value); + else value = NULL; + + if (!pseudo_env[i].key) + pseudo_diag("Unknown variable %s.\n", key); + + return value; +} + +/* We make a copy, so the original values should be freed. */ +int pseudo_set_value(const char * key, const char * value) { + int rc = 0; + size_t i = 0; + + if (_in_init == -1) _libpseudo_init(); + + for (i = 0; pseudo_env[i].key && memcmp(pseudo_env[i].key, key, pseudo_env[i].key_len + 1); i++) ; + + if (pseudo_env[i].key) { + if (pseudo_env[i].value) free(pseudo_env[i].value); + if (value) + ((struct pseudo_variables *)pseudo_env)[i].value = strdup(value); + else + ((struct pseudo_variables *)pseudo_env)[i].value = NULL; + } else { + if (!_in_init) pseudo_diag("Unknown variable %s.\n", key); + rc = -EINVAL; + } + + return rc; +} + +static void _libpseudo_init(void) { + size_t i = 0; + + _in_init = 1; + + for (i = 0; pseudo_env[i].key; i++) { + if (pseudo_env[i].key) + if (getenv(pseudo_env[i].key)) + pseudo_set_value(pseudo_env[i].key, getenv(pseudo_env[i].key)); + } + + _in_init = 0; +} + /* 5 = ridiculous levels of duplication * 4 = exhaustive detail * 3 = detailed protocol analysis @@ -65,7 +173,7 @@ static ssize_t pseudo_sys_max_pathlen = -1; * the end of the string or a space after it. */ static char *libpseudo_name = "libpseudo.so"; -static char *libpseudo_pattern = "(=| )libpseudo[^ ]*\\.so($| )"; +static char *libpseudo_pattern = "(^|=| )libpseudo[^ ]*\\.so($| )"; static regex_t libpseudo_regex; static int libpseudo_regex_compiled = 0; @@ -88,9 +196,13 @@ static char * without_libpseudo(char *list) { regmatch_t pmatch[1]; int counter = 0; + int skip_start = 0; + if (libpseudo_regex_init()) return NULL; + if (list[0] == '=' || list[0] == ' ') + skip_start = 1; if (regexec(&libpseudo_regex, list, 1, pmatch, 0)) { return list; @@ -100,7 +212,7 @@ without_libpseudo(char *list) { char *start = list + pmatch[0].rm_so; char *end = list + pmatch[0].rm_eo; /* don't copy over the space or = */ - ++start; + start += skip_start; memmove(start, end, strlen(end) + 1); ++counter; if (counter > 5) { @@ -135,11 +247,19 @@ void pseudo_debug_terse(void) { if (max_debug_level > 0) --max_debug_level; + + char s[16]; + snprintf(s, 16, "%d", max_debug_level); + pseudo_set_value("PSEUDO_DEBUG", s); } void pseudo_debug_verbose(void) { ++max_debug_level; + + char s[16]; + snprintf(s, 16, "%d", max_debug_level); + pseudo_set_value("PSEUDO_DEBUG", s); } int @@ -437,195 +557,221 @@ 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 +/* remove the libpseudo 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() { + char * ld_preload = getenv("LD_PRELOAD"); + + if (ld_preload) { + ld_preload = without_libpseudo(ld_preload); + if (!ld_preload) { + pseudo_diag("fatal: can't allocate new LD_PRELOAD variable.\n"); + } + if (ld_preload && strlen(ld_preload)) + setenv("LD_PRELOAD", ld_preload, 1); + else + unsetenv("LD_PRELOAD"); + } +} + char ** -pseudo_dropenv(char * const *environ) { - char **new_environ; - int env_count = 0; +pseudo_dropenvp(char * const *envp) { + char **new_envp; int i, j; - for (i = 0; environ[i]; ++i) - ++env_count; - new_environ = malloc((env_count + 1) * sizeof(*new_environ)); - if (!new_environ) { + for (i = 0; envp[i]; ++i) ; + + new_envp = malloc((i + 1) * sizeof(*new_envp)); + if (!new_envp) { 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 *new_val = without_libpseudo(environ[i]); + for (i = 0; envp[i]; ++i) { + if (!memcmp(envp[i], "LD_PRELOAD=", 11)) { + char *new_val = without_libpseudo(envp[i]); if (!new_val) { pseudo_diag("fatal: can't allocate new environment variable.\n"); return 0; } else { /* don't keep an empty value */ - if (strcmp(new_val, "LD_PRELOAD=")) { - new_environ[j++] = new_val; + if (memcmp(new_val, "LD_PRELOAD=", 11)) { + new_envp[j++] = new_val; } } } else { - new_environ[j++] = environ[i]; + new_envp[j++] = envp[i]; } } - new_environ[j++] = NULL; - return new_environ; + new_envp[j++] = NULL; + return new_envp; +} + +/* add pseudo stuff to the environment. + */ +void +pseudo_setupenv() { + size_t i = 0; + + pseudo_debug(2, "setting up pseudo environment.\n"); + + /* Make sure everything has been evaluated */ + free(pseudo_get_prefix(NULL)); + free(pseudo_get_bindir()); + free(pseudo_get_libdir()); + free(pseudo_get_localstatedir()); + + while (pseudo_env[i].key) { + if (pseudo_env[i].value) + setenv(pseudo_env[i].key, strdup(pseudo_env[i].value), 0); + i++; + } + + char * ld_preload = getenv("LD_PRELOAD"); + if (ld_preload) { + ld_preload = with_libpseudo(ld_preload); + if (!ld_preload) { + pseudo_diag("fatal: can't allocate new LD_PRELOAD variable.\n"); + } + setenv("LD_PRELOAD", ld_preload, 1); + } else { + setenv("LD_PRELOAD", libpseudo_name, 1); + } + + const char * ld_library_path = getenv("LD_LIBRARY_PATH"); + char * libdir_path = pseudo_libdir_path(NULL); + if (ld_library_path && !strstr(ld_library_path, libdir_path)) { + size_t len = strlen(ld_library_path) + 1 + strlen(libdir_path) + 1 + (strlen(libdir_path) + 2) + 1; + char *newenv = malloc(len); + if (!newenv) { + pseudo_diag("fatal: can't allocate new LD_LIBRARY_PATH variable.\n"); + } + snprintf(newenv, len, "%s:%s:%s64", ld_preload, libdir_path, libdir_path); + setenv("LD_LIBRARY_PATH", newenv, 1); + } else { + size_t len = strlen(libdir_path) + 1 + (strlen(libdir_path) + 2) + 1; + char *newenv = malloc(len); + if (!newenv) { + pseudo_diag("fatal: can't allocate new LD_LIBRARY_PATH variable.\n"); + } + snprintf(newenv, len, "%s:%s64", libdir_path, libdir_path); + setenv("LD_LIBRARY_PATH", newenv, 1); + } + free(libdir_path); } /* 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(). + * execve(). */ 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 found_prefix = 0, found_bindir = 0, found_libdir = 0, found_localstatedir = 0; - int i, j; - size_t len; - 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; - if (!memcmp(environ[i], "PSEUDO_PREFIX=", 14)) - found_prefix = 1; - if (!memcmp(environ[i], "PSEUDO_BINDIR=", 14)) - found_bindir = 1; - if (!memcmp(environ[i], "PSEUDO_LIBDIR=", 14)) - found_libdir = 1; - if (!memcmp(environ[i], "PSEUDO_LOCALSTATEDIR=", 21)) - found_localstatedir = 1; +pseudo_setupenvp(char * const *envp) { + char **new_envp; + + size_t i, j, k; + size_t env_count = 0; + + size_t size_pseudoenv = 0; + + char * ld_preload=NULL, * ld_library_path=NULL; + + pseudo_debug(2, "setting up envp environment.\n"); + + /* Make sure everything has been evaluated */ + free(pseudo_get_prefix(NULL)); + free(pseudo_get_bindir()); + free(pseudo_get_libdir()); + free(pseudo_get_localstatedir()); + + for (i = 0; envp[i]; ++i) { + if (!memcmp(envp[i], "LD_PRELOAD=", 11)) { + ld_preload = envp[i]; + } + if (!memcmp(envp[i], "LD_LIBRARY_PATH=", 11)) { + ld_library_path = envp[i]; + } ++env_count; } - env_count += 8 - (found_preload + found_libpath + found_debug + found_opts + found_prefix + found_bindir + found_libdir + found_localstatedir); - new_environ = malloc((env_count + 1) * sizeof(*new_environ)); - if (!new_environ) { + for (i = 0; pseudo_env[i].key; i++) { + size_pseudoenv++; + } + + env_count += size_pseudoenv; /* We're going to over allocate */ + + j = 0; + new_envp = malloc((env_count + 1) * sizeof(*new_envp)); + if (!new_envp) { 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)) { - newenv = with_libpseudo(environ[i]); - if (!newenv) { - pseudo_diag("fatal: can't allocate new environment variable.\n"); - return NULL; - } - new_environ[j++] = newenv; - } else if (!memcmp(environ[i], "LD_LIBRARY_PATH=", 16)) { - if (!strstr(environ[i], pseudo_libdir_path(NULL))) { - char *e1; - e1 = pseudo_libdir_path(NULL); - len = strlen(environ[i]) + strlen(e1) + (strlen(e1) + 2) + 3; - newenv = malloc(len); - if (!newenv) { - pseudo_diag("fatal: can't allocate new environment variable.\n"); - } - snprintf(newenv, len, "%s:%s:%s64", environ[i], e1, e1); - free(e1); - new_environ[j++] = newenv; - } else { - new_environ[j++] = environ[i]; - } - } else { - new_environ[j++] = environ[i]; + if (ld_preload) { + ld_preload = with_libpseudo(ld_preload); + if (!ld_preload) { + pseudo_diag("fatal: can't allocate new LD_PRELOAD variable.\n"); } + new_envp[j++] = ld_preload; + } else { + size_t len = strlen("LD_PRELOAD=") + strlen(libpseudo_name) + 1; + char *newenv = malloc(len); + snprintf(newenv, len, "LD_PRELOAD=%s", libpseudo_name); + new_envp[j++] = newenv; } - if (!found_libpath) { - char *e1; - e1 = pseudo_libdir_path(NULL); - len = 16 + strlen(e1) + (strlen(e1) + 2) + 2; - newenv = malloc(len); - if (!newenv) { - pseudo_diag("fatal: can't allocate new environment variable.\n"); - } - snprintf(newenv, len, "LD_LIBRARY_PATH=%s:%s64", e1, e1); - new_environ[j++] = newenv; - } - if (!found_preload) { - new_environ[j++] = "LD_PRELOAD=libpseudo.so"; - } - 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); + + char * libdir_path = pseudo_libdir_path(NULL); + if (ld_library_path && !strstr(ld_library_path, libdir_path)) { + size_t len = strlen(ld_library_path) + 1 + strlen(libdir_path) + 1 + (strlen(libdir_path) + 2) + 1; + char *newenv = malloc(len); if (!newenv) { - pseudo_diag("fatal: can't allocate new environment variable.\n"); + pseudo_diag("fatal: can't allocate new LD_LIBRARY_PATH variable.\n"); } - snprintf(newenv, len, "PSEUDO_OPTS=%s", opts); - new_environ[j++] = newenv; - } - if (!found_prefix) { - char * prefix_path = pseudo_prefix_path(NULL); - len = 14 + strlen(prefix_path) + 1; - newenv = malloc(len); + snprintf(newenv, len, "%s:%s:%s64", ld_library_path, libdir_path, libdir_path); + new_envp[j++] = newenv; + } else { + size_t len = strlen("LD_LIBRARY_PATH=") + strlen(libdir_path) + 1 + (strlen(libdir_path) + 2) + 1; + char *newenv = malloc(len); if (!newenv) { - pseudo_diag("fatal: can't allocate new environment variable.\n"); + pseudo_diag("fatal: can't allocate new LD_LIBRARY_PATH variable.\n"); } - snprintf(newenv, len, "PSEUDO_PREFIX=%s", prefix_path); - new_environ[j++] = newenv; - free(prefix_path); + snprintf(newenv, len, "LD_LIBRARY_PATH=%s:%s64", libdir_path, libdir_path); + new_envp[j++] = newenv; } - if (!found_bindir) { - char * bindir_path = pseudo_bindir_path(NULL); - len = 14 + strlen(bindir_path) + 1; - newenv = malloc(len); - if (!newenv) { - pseudo_diag("fatal: can't allocate new environment variable.\n"); - } - snprintf(newenv, len, "PSEUDO_BINDIR=%s", bindir_path); - new_environ[j++] = newenv; - free(bindir_path); + free(libdir_path); + + for (i = 0; envp[i]; ++i) { + if (!memcmp(envp[i], "LD_PRELOAD=", 11)) continue; + if (!memcmp(envp[i], "LD_LIBRARY_PATH=", 16)) continue; + new_envp[j++] = envp[i]; } - if (!found_libdir) { - char * libdir_path = pseudo_libdir_path(NULL); - len = 14 + strlen(libdir_path) + 1; - newenv = malloc(len); - if (!newenv) { - pseudo_diag("fatal: can't allocate new environment variable.\n"); + + for (i = 0; pseudo_env[i].key; i++) { + int found = 0; + for (k = 0; k < j; k++) { + if (!strncmp(pseudo_env[i].key,new_envp[k],strlen(pseudo_env[i].key))) { + found = 1; + break; + } } - snprintf(newenv, len, "PSEUDO_LIBDIR=%s", libdir_path); - new_environ[j++] = newenv; - free(libdir_path); - } - if (!found_localstatedir) { - char * localstatedir_path = pseudo_localstatedir_path(NULL); - len = 21 + strlen(localstatedir_path) + 1; - newenv = malloc(len); - if (!newenv) { - pseudo_diag("fatal: can't allocate new environment variable.\n"); + if (!found && pseudo_env[i].key && pseudo_env[i].value) { + size_t len = strlen(pseudo_env[i].key) + 1 + strlen(pseudo_env[i].value) + 1; + char *newenv = malloc(len); + if (!newenv) { + pseudo_diag("fatal: can't allocate new variable.\n"); + } + snprintf(newenv, len, "%s=%s", pseudo_env[i].key, pseudo_env[i].value); + new_envp[j++] = newenv; } - snprintf(newenv, len, "PSEUDO_LOCALSTATEDIR=%s", localstatedir_path); - new_environ[j++] = newenv; - free(localstatedir_path); } - new_environ[j++] = NULL; - return new_environ; + new_envp[j++] = NULL; + return new_envp; } /* Append the file value to the prefix value. */ @@ -665,80 +811,77 @@ pseudo_append_path(const char * prefix, size_t prefix_len, char *file) { */ char * pseudo_prefix_path(char *file) { - static char *prefix = NULL; - static size_t prefix_len; + char * rc; + char * prefix = pseudo_get_prefix(NULL); if (!prefix) { - prefix = getenv("PSEUDO_PREFIX"); - if (!prefix) { - pseudo_diag("You must set the PSEUDO_PREFIX environment variable to run pseudo.\n"); - exit(1); - } - prefix_len = strlen(prefix); + pseudo_diag("You must set the PSEUDO_PREFIX environment variable to run pseudo.\n"); + exit(1); } - return pseudo_append_path(prefix, prefix_len, file); + rc = pseudo_append_path(prefix, strlen(prefix), file); + free(prefix); + + return rc; } /* get the full path to a file under $PSEUDO_BINDIR. */ char * pseudo_bindir_path(char *file) { - static char *bindir = NULL; - static size_t bindir_len; + char * rc; + char * bindir = pseudo_get_bindir(NULL); if (!bindir) { - bindir = pseudo_get_bindir(); - if (!bindir) { - pseudo_diag("You must set the PSEUDO_BINDIR environment variable to run pseudo.\n"); - exit(1); - } - bindir_len = strlen(bindir); + pseudo_diag("You must set the PSEUDO_BINDIR environment variable to run pseudo.\n"); + exit(1); } - return pseudo_append_path(bindir, bindir_len, file); + rc = pseudo_append_path(bindir, strlen(bindir), file); + free(bindir); + + return rc; } /* get the full path to a file under $PSEUDO_LIBDIR. */ char * pseudo_libdir_path(char *file) { - static char *libdir = NULL; - static size_t libdir_len; + char * rc; + char * libdir = pseudo_get_libdir(NULL); if (!libdir) { - libdir = pseudo_get_libdir(); - if (!libdir) { - pseudo_diag("You must set the PSEUDO_LIBDIR environment variable to run pseudo.\n"); - exit(1); - } - libdir_len = strlen(libdir); + pseudo_diag("You must set the PSEUDO_LIBDIR environment variable to run pseudo.\n"); + exit(1); } - return pseudo_append_path(libdir, libdir_len, file); + rc = pseudo_append_path(libdir, strlen(libdir), file); + free(libdir); + + return rc; } /* get the full path to a file under $PSEUDO_LOCALSTATEDIR. */ char * pseudo_localstatedir_path(char *file) { - static char *localstatedir = NULL; - static size_t localstatedir_len; + char * rc; + char * localstatedir = pseudo_get_localstatedir(NULL); if (!localstatedir) { - localstatedir = pseudo_get_localstatedir(); - if (!localstatedir) { - pseudo_diag("You must set the PSEUDO_LOCALSTATEDIR environment variable to run pseudo.\n"); - exit(1); - } - localstatedir_len = strlen(localstatedir); + pseudo_diag("You must set the PSEUDO_LOCALSTATEDIR environment variable to run pseudo.\n"); + exit(1); } - return pseudo_append_path(localstatedir, localstatedir_len, file); + rc = pseudo_append_path(localstatedir, strlen(localstatedir), file); + free(localstatedir); + + return rc; } char * pseudo_get_prefix(char *pathname) { - char *s; - s = getenv("PSEUDO_PREFIX"); - if (!s) { + char *s = pseudo_get_value("PSEUDO_PREFIX"); + + /* Generate the PSEUDO_PREFIX if necessary, and possible... */ + if (!s && pathname) { char mypath[pseudo_path_max()]; char *dir; char *tmp_path; @@ -783,22 +926,20 @@ pseudo_get_prefix(char *pathname) { pseudo_diag("Warning: PSEUDO_PREFIX unset, defaulting to %s.\n", mypath); - setenv("PSEUDO_PREFIX", mypath, 1); - s = getenv("PSEUDO_PREFIX"); + pseudo_set_value("PSEUDO_PREFIX", mypath); + s = pseudo_get_value("PSEUDO_PREFIX"); } return s; } char * pseudo_get_bindir() { - char *s; - s = getenv("PSEUDO_BINDIR"); + char *s = pseudo_get_value("PSEUDO_BINDIR"); if (!s) { - char *pseudo_bindir; - pseudo_bindir = pseudo_prefix_path(PSEUDO_BINDIR); + char *pseudo_bindir = pseudo_prefix_path(PSEUDO_BINDIR);; if (pseudo_bindir) { - setenv("PSEUDO_BINDIR", pseudo_bindir, 1); - s = getenv("PSEUDO_BINDIR"); + pseudo_set_value("PSEUDO_BINDIR", pseudo_bindir); + s = pseudo_bindir; } } return s; @@ -806,26 +947,20 @@ pseudo_get_bindir() { char * pseudo_get_libdir() { - char *s; - s = getenv("PSEUDO_LIBDIR"); + char *s = pseudo_get_value("PSEUDO_LIBDIR"); if (!s) { - char *pseudo_libdir; - pseudo_libdir = pseudo_prefix_path(PSEUDO_LIBDIR); + char *pseudo_libdir = pseudo_prefix_path(PSEUDO_LIBDIR); if (pseudo_libdir) { - setenv("PSEUDO_LIBDIR", pseudo_libdir, 1); - s = getenv("PSEUDO_LIBDIR"); + pseudo_set_value("PSEUDO_LIBDIR", pseudo_libdir); + s = pseudo_libdir; } } /* If we somehow got lib64 in there, clean it down to just lib... */ if (s) { size_t len = strlen(s); if (s[len-2] == '6' && s[len-1] == '4') { - char mypath[pseudo_path_max()]; - snprintf(mypath, pseudo_path_max(), "%s", s); - s = mypath + (strlen(mypath) - 2); - s[0] = '\0'; - setenv("PSEUDO_LIBDIR", mypath, 1); - s = getenv("PSEUDO_LIBDIR"); + s[len-2] = '\0'; + pseudo_set_value("PSEUDO_LIBDIR", s); } } @@ -834,14 +969,12 @@ pseudo_get_libdir() { char * pseudo_get_localstatedir() { - char *s; - s = getenv("PSEUDO_LOCALSTATEDIR"); + char *s = pseudo_get_value("PSEUDO_LOCALSTATEDIR"); if (!s) { - char *pseudo_localstatedir; - pseudo_localstatedir = pseudo_prefix_path(PSEUDO_LOCALSTATEDIR); + char *pseudo_localstatedir = pseudo_prefix_path(PSEUDO_LOCALSTATEDIR); if (pseudo_localstatedir) { - setenv("PSEUDO_LOCALSTATEDIR", pseudo_localstatedir, 1); - s = getenv("PSEUDO_LOCALSTATEDIR"); + pseudo_set_value("PSEUDO_LOCALSTATEDIR", pseudo_localstatedir); + s = pseudo_localstatedir; } } return s; @@ -1002,11 +1135,12 @@ pseudo_etc_file(const char *file, char *realname, int flags, char **search_dirs, int pseudo_logfile(char *defname) { char *pseudo_path; - char *filename, *s; + char *filename = pseudo_get_value("PSEUDO_DEBUG_FILE"); + char *s; extern char *program_invocation_short_name; /* glibcism */ int fd; - if ((filename = getenv("PSEUDO_DEBUG_FILE")) == NULL) { + if (!filename) { if (!defname) { pseudo_debug(3, "no special log file requested, using stderr.\n"); return -1; @@ -1077,7 +1211,8 @@ pseudo_logfile(char *defname) { } else { strcpy(pseudo_path, filename); } - } + free(filename); + } fd = open(pseudo_path, O_WRONLY | O_APPEND | O_CREAT, 0644); if (fd == -1) { pseudo_diag("help: can't open log file %s: %s\n", pseudo_path, strerror(errno)); diff --git a/pseudo_wrappers.c b/pseudo_wrappers.c index 4e98efa..d7ed9f6 100644 --- a/pseudo_wrappers.c +++ b/pseudo_wrappers.c @@ -1,6 +1,7 @@ /* wrapper code -- this is the shared code used around the pseduo * wrapper functions, which are in pseudo_wrapfuncs.c. */ +#include <assert.h> #include <stdlib.h> #include <limits.h> #include <stdarg.h> @@ -84,8 +85,10 @@ pseudo_magic() { static void pseudo_enosys(const char *func) { pseudo_diag("pseudo: ENOSYS for '%s'.\n", func ? func : "<nil>"); - if (getenv("PSEUDO_ENOSYS_ABORT")) + char * value = pseudo_get_value("PSEUDO_ENOSYS_ABORT"); + if (value) abort(); + free(value); } /* de-chroot a string. @@ -143,14 +146,15 @@ pseudo_populate_wrappers(void) { } } done = 1; - debug = getenv("PSEUDO_DEBUG"); + debug = pseudo_get_value("PSEUDO_DEBUG"); if (debug) { int level = atoi(debug); for (i = 0; i < level; ++i) { pseudo_debug_verbose(); } } - no_symlink_exp = getenv("PSEUDO_NOSYMLINKEXP"); + free(debug); + no_symlink_exp = pseudo_get_value("PSEUDO_NOSYMLINKEXP"); if (no_symlink_exp) { char *endptr; /* if the environment variable is not an empty string, @@ -171,6 +175,7 @@ pseudo_populate_wrappers(void) { } else { pseudo_nosymlinkexp = 0; } + free(no_symlink_exp); /* if PSEUDO_DEBUG_FILE is set up, redirect logging there. */ pseudo_logfile(NULL); @@ -185,33 +190,35 @@ pseudo_populate_wrappers(void) { if (pseudo_path) { pseudo_prefix_dir_fd = open(pseudo_path, O_RDONLY); pseudo_prefix_dir_fd = pseudo_fd(pseudo_prefix_dir_fd, MOVE_FD); - free(pseudo_path); } else { pseudo_diag("No prefix available to to find server.\n"); exit(1); } if (pseudo_prefix_dir_fd == -1) { - pseudo_diag("Can't open prefix path (%s) for server.\n", + pseudo_diag("Can't open prefix path (%s) for server: %s\n", + pseudo_path, strerror(errno)); exit(1); } } + free(pseudo_path); pseudo_path = pseudo_localstatedir_path(NULL); if (pseudo_localstate_dir_fd == -1) { if (pseudo_path) { pseudo_localstate_dir_fd = open(pseudo_path, O_RDONLY); pseudo_localstate_dir_fd = pseudo_fd(pseudo_localstate_dir_fd, MOVE_FD); - free(pseudo_path); } else { pseudo_diag("No prefix available to to find server.\n"); exit(1); } if (pseudo_localstate_dir_fd == -1) { - pseudo_diag("Can't open prefix path (%s) for server.\n", + pseudo_diag("Can't open prefix path (%s) for server: %s\n", + pseudo_path, strerror(errno)); exit(1); } } + free(pseudo_path); pseudo_debug(2, "(%s) set up wrappers\n", program_invocation_short_name); pseudo_magic(); pseudo_droplock(); diff --git a/pseudolog.c b/pseudolog.c index e2161c4..903c161 100644 --- a/pseudolog.c +++ b/pseudolog.c @@ -527,7 +527,7 @@ main(int argc, char **argv) { s = PSEUDO_ROOT_PATH(AT_FDCWD, optarg, AT_SYMLINK_NOFOLLOW); if (!s) pseudo_diag("Can't resolve prefix path '%s'\n", optarg); - setenv("PSEUDO_PREFIX", s, 1); + pseudo_set_value("PSEUDO_PREFIX", s); break; case 'v': pseudo_debug_verbose(); diff --git a/test/test-env_i.sh b/test/test-env_i.sh new file mode 100755 index 0000000..c38cf1d --- /dev/null +++ b/test/test-env_i.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +env -i A=A B=B C=C env | grep -q "PSEUDO_" + +if [ "$?" = "0" ] +then + #echo "Passed." + exit 0 +fi +#echo "Failed" +exit 1 |