diff options
author | Stéphane Lesimple <speed47_github@speed47.net> | 2022-01-27 03:05:57 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2022-01-27 03:05:57 +0000 |
commit | 5dafb41e6ee708bee76cdf3276fb845e8890d43d (patch) | |
tree | 8edf419ba5f5e2b590ad3bf6a40fb8388f0af27c | |
parent | 471bcd8b5af9953b88ef55cb1e8c37d69621686e (diff) | |
parent | 12835df0a507ff83be5a89d39d358f606c03a5aa (diff) | |
download | minijail-5dafb41e6ee708bee76cdf3276fb845e8890d43d.tar.gz |
minijail0: implement --env-reset and --env-add am: f65da3a91d am: 4f9480f394 am: 2257dd27b6 am: 12835df0a5
Original change: https://android-review.googlesource.com/c/platform/external/minijail/+/1217602
Change-Id: I6e0cb4bec4e484cd12b1a393ba909df761b7a752
-rw-r--r-- | libminijail.c | 16 | ||||
-rw-r--r-- | libminijail.h | 7 | ||||
-rw-r--r-- | minijail0.1 | 10 | ||||
-rw-r--r-- | minijail0.c | 17 | ||||
-rw-r--r-- | minijail0_cli.c | 76 | ||||
-rw-r--r-- | minijail0_cli.h | 5 | ||||
-rw-r--r-- | minijail0_cli_unittest.cc | 45 |
7 files changed, 164 insertions, 12 deletions
diff --git a/libminijail.c b/libminijail.c index 8ddea7e..ea59404 100644 --- a/libminijail.c +++ b/libminijail.c @@ -2590,7 +2590,7 @@ static int setup_preload(const struct minijail *j attribute_unused, const char *preload_path = j->preload_path ?: PRELOADPATH; char *newenv = NULL; int ret = 0; - const char *oldenv = getenv(kLdPreloadEnvVar); + const char *oldenv = minijail_getenv(*child_env, kLdPreloadEnvVar); if (!oldenv) oldenv = ""; @@ -2939,6 +2939,20 @@ int API minijail_run(struct minijail *j, const char *filename, return minijail_run_config_internal(j, &config); } +int API minijail_run_env(struct minijail *j, const char *filename, + char *const argv[], char *const envp[]) +{ + struct minijail_run_config config = { + .filename = filename, + .elf_fd = -1, + .argv = argv, + .envp = envp, + .use_preload = true, + .exec_in_child = true, + }; + return minijail_run_config_internal(j, &config); +} + int API minijail_run_pid(struct minijail *j, const char *filename, char *const argv[], pid_t *pchild_pid) { diff --git a/libminijail.h b/libminijail.h index bcfd995..d2dce7a 100644 --- a/libminijail.h +++ b/libminijail.h @@ -344,6 +344,13 @@ void minijail_enter(const struct minijail *j); /* * Run the specified command in the given minijail, execve(2)-style. + * Pass |envp| as the full environment for the child. + */ +int minijail_run_env(struct minijail *j, const char *filename, + char *const argv[], char *const envp[]); + +/* + * Run the specified command in the given minijail, execve(2)-style. * If minijail_namespace_pids() or minijail_namespace_user() are used, * this or minijail_fork() is required instead of minijail_enter(). */ diff --git a/minijail0.1 b/minijail0.1 index 47aa803..9258e3f 100644 --- a/minijail0.1 +++ b/minijail0.1 @@ -289,6 +289,16 @@ capability-dumb binaries. See \fBcapabilities\fR(7). Create a new UTS/hostname namespace, and optionally set the hostname in the new namespace to \fIhostname\fR. .TP +\fB--env-reset\fR +Clear the current environment instead of having the program inherit the active +environment. This is often used to start the program with a minimal +sanitized environment. +.TP +\fB--env-add <NAME=value>\fR +Adds or replace the specified environment variable \fINAME\fR in the program's +environment before starting it, and set it to the specified \fIvalue\fR. +This option can be used several times to set any number of environment variables. +.TP \fB--logging=<system>\fR Use \fIsystem\fR as the logging system. \fIsystem\fR must be one of \fBauto\fR (the default), \fBsyslog\fR, or \fBstderr\fR. diff --git a/minijail0.c b/minijail0.c index 86dff01..9b1fcf3 100644 --- a/minijail0.c +++ b/minijail0.c @@ -16,15 +16,17 @@ #include "minijail0_cli.h" #include "util.h" -int main(int argc, char *argv[]) +int main(int argc, char *argv[], char *environ[]) { struct minijail *j = minijail_new(); const char *dl_mesg = NULL; const char *preload_path = PRELOADPATH; int exit_immediately = 0; ElfType elftype = ELFERROR; - int consumed = parse_args(j, argc, argv, &exit_immediately, &elftype, - &preload_path); + char **envp = NULL; + int consumed = parse_args(j, argc, argv, environ, + &exit_immediately, &elftype, + &preload_path, &envp); argc -= consumed; argv += consumed; @@ -61,9 +63,14 @@ int main(int argc, char *argv[]) return 1; } minijail_set_preload_path(j, preload_path); - minijail_run(j, argv[0], argv); - } else + if (envp) { + minijail_run_env(j, argv[0], argv, envp); + } else { + minijail_run(j, argv[0], argv); + } + } else { errx(1, "Target program '%s' is not a valid ELF file", argv[0]); + } if (exit_immediately) return 0; diff --git a/minijail0_cli.c b/minijail0_cli.c index 54cfdb7..3b9b708 100644 --- a/minijail0_cli.c +++ b/minijail0_cli.c @@ -456,7 +456,7 @@ static void read_seccomp_filter(const char *filter_path, * bit confusing, and honestly there's no reason to "optimize" here. * * The long enum values are internal to this file and can freely change at any - * time without breaking anything. Don't worry about ordering. + * time without breaking anything. Please keep alphabetically ordered. */ enum { /* Everything after this point only have long options. */ @@ -465,6 +465,8 @@ enum { OPT_ALLOW_SPECULATIVE_EXECUTION, OPT_AMBIENT, OPT_CONFIG, + OPT_ENV_ADD, + OPT_ENV_RESET, OPT_LOGGING, OPT_PRELOAD_LIBRARY, OPT_PROFILE, @@ -494,6 +496,8 @@ static const struct option long_options[] = { {"allow-speculative-execution", no_argument, 0, OPT_ALLOW_SPECULATIVE_EXECUTION}, {"config", required_argument, 0, OPT_CONFIG}, + {"env-add", required_argument, 0, OPT_ENV_ADD}, + {"env-reset", no_argument, 0, OPT_ENV_RESET}, {"mount", required_argument, 0, 'k'}, {"bind-mount", required_argument, 0, 'b'}, {0, 0, 0, 0}, @@ -599,6 +603,12 @@ static const char help_text[] = " This will avoid accessing <program> binary before execve(2).\n" " Type 'static' will avoid preload hooking.\n" " -w Create and join a new anonymous session keyring.\n" +" --env-reset Clear the current environment instead of having <program>\n" +" inherit the active environment. Often used to start <program>\n" +" with a minimal sanitized environment.\n" +" --env-add <NAME=value>\n" +" Sets the specified environment variable <NAME>\n" +" in the <program>'s environment before starting it.\n" "\n" "Uncommon options:\n" " --allow-speculative-execution\n" @@ -693,9 +703,33 @@ static int getopt_conf_or_cli(int argc, char *const argv[], return opt; } +static void set_child_env(char ***envp, char *arg, char *const environ[]) +{ + /* We expect VAR=value format for arg. */ + char *delim = strchr(arg, '='); + if (!delim) { + errx(1, "Expected an argument of the " + "form VAR=value (got '%s')", arg); + } + *delim = '\0'; + const char *env_value = delim + 1; + if (!*envp) { + /* + * We got our first --env-add. Initialize *envp by + * copying our current env to the future child env. + */ + *envp = minijail_copy_env(environ); + if (!*envp) + err(1, "Failed to allocate memory."); + } + if (minijail_setenv(envp, arg, env_value, 1)) + err(1, "minijail_setenv() failed."); +} + int parse_args(struct minijail *j, int argc, char *const argv[], - int *exit_immediately, ElfType *elftype, - const char **preload_path) + char *const environ[], int *exit_immediately, + ElfType *elftype, const char **preload_path, + char ***envp) { enum seccomp_type { None, Strict, Filter, BpfBinaryFilter }; enum seccomp_type seccomp = None; @@ -1033,6 +1067,32 @@ int parse_args(struct minijail *j, int argc, char *const argv[], } break; } + case OPT_ENV_ADD: + /* + * We either copy our current env to the child env + * then add the requested envvar to it, or just + * add the requested envvar to the already existing + * envp. + */ + set_child_env(envp, optarg, environ); + break; + case OPT_ENV_RESET: + if (*envp && *envp != environ) { + /* + * We already started to initialize the future + * child env, because we got some --env-add + * earlier on the command-line, so first, + * free the memory we allocated. + * If |*envp| happens to point to |environ|, + * don't attempt to free it. + */ + minijail_free_env(*envp); + } + /* Allocate an empty environment for the child. */ + *envp = calloc(1, sizeof(char *)); + if (!*envp) + err(1, "Failed to allocate memory."); + break; default: usage(argv[0]); exit(opt == 'h' ? 0 : 1); @@ -1127,6 +1187,16 @@ int parse_args(struct minijail *j, int argc, char *const argv[], minijail_mount_tmp_size(j, tmp_size); /* + * Copy our current env to the child if its |*envp| has not + * already been initialized from --env-(reset|add) usage. + */ + if (!*envp) { + *envp = minijail_copy_env(environ); + if (!*envp) + err(1, "Failed to allocate memory."); + } + + /* * There should be at least one additional unparsed argument: the * executable name. */ diff --git a/minijail0_cli.h b/minijail0_cli.h index 583c763..cd504b3 100644 --- a/minijail0_cli.h +++ b/minijail0_cli.h @@ -17,8 +17,9 @@ extern "C" { struct minijail; int parse_args(struct minijail *j, int argc, char *const argv[], - int *exit_immediately, ElfType *elftype, - const char **preload_path); + char *const environ[], int *exit_immediately, + ElfType *elftype, const char **preload_path, + char ***envp); #ifdef __cplusplus }; /* extern "C" */ diff --git a/minijail0_cli_unittest.cc b/minijail0_cli_unittest.cc index 151f789..f280a8a 100644 --- a/minijail0_cli_unittest.cc +++ b/minijail0_cli_unittest.cc @@ -60,9 +60,10 @@ class CliTest : public ::testing::Test { testing::internal::CaptureStdout(); const char* preload_path = PRELOADPATH; + char **envp = NULL; int ret = parse_args(j, pargv.size(), const_cast<char* const*>(pargv.data()), - exit_immediately, elftype, &preload_path); + NULL, exit_immediately, elftype, &preload_path, &envp); testing::internal::GetCapturedStdout(); minijail_destroy(j); @@ -544,6 +545,48 @@ TEST_F(CliTest, invalid_L_combo) { ASSERT_EXIT(parse_args_(argv), testing::ExitedWithCode(1), ""); } +// Valid calls to the clear env option. +TEST_F(CliTest, valid_clear_env) { + std::vector<std::string> argv = {"--env-reset", "/bin/sh"}; + + ASSERT_TRUE(parse_args_(argv)); +} + +// Valid calls to the set env option. +TEST_F(CliTest, valid_set_env) { + std::vector<std::string> argv1 = {"--env-add", "NAME=value", "/bin/sh"}; + ASSERT_TRUE(parse_args_(argv1)); + + // multiple occurences are allowed. + std::vector<std::string> argv2 = {"--env-add", "A=b", + "--env-add", "b=C=D", "/bin/sh"}; + ASSERT_TRUE(parse_args_(argv2)); + + // --env-reset before any --env-add to not pass our own env. + std::vector<std::string> argv3 = {"--env-reset", "--env-add", "A=b", "/bin/sh"}; + ASSERT_TRUE(parse_args_(argv3)); + + // --env-add before an --env-reset doesn't have any effect, but is allowed. + std::vector<std::string> argv4 = {"--env-add", "A=b", "--env-reset", "/bin/sh"}; + ASSERT_TRUE(parse_args_(argv4)); +} + +// Invalid calls to the set env options. +TEST_F(CliTest, invalid_set_env) { + + // invalid env=value arguments. + std::vector<std::string> argv2 = {"--env-add", "", "/bin/sh"}; + + argv2[1] = "INVALID"; + ASSERT_EXIT(parse_args_(argv2), testing::ExitedWithCode(1), ""); + + argv2[1] = "="; + ASSERT_EXIT(parse_args_(argv2), testing::ExitedWithCode(1), ""); + + argv2[1] = "=foo"; + ASSERT_EXIT(parse_args_(argv2), testing::ExitedWithCode(1), ""); +} + // Android unit tests do not support data file yet. #if !defined(__ANDROID__) |