aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStéphane Lesimple <speed47_github@speed47.net>2022-01-27 03:05:57 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2022-01-27 03:05:57 +0000
commit5dafb41e6ee708bee76cdf3276fb845e8890d43d (patch)
tree8edf419ba5f5e2b590ad3bf6a40fb8388f0af27c
parent471bcd8b5af9953b88ef55cb1e8c37d69621686e (diff)
parent12835df0a507ff83be5a89d39d358f606c03a5aa (diff)
downloadminijail-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.c16
-rw-r--r--libminijail.h7
-rw-r--r--minijail0.110
-rw-r--r--minijail0.c17
-rw-r--r--minijail0_cli.c76
-rw-r--r--minijail0_cli.h5
-rw-r--r--minijail0_cli_unittest.cc45
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__)