minijail0: implement --env-reset and --env-add
The default behavior of minijail0 is to transmit its own environment
to the program it launches.
These two options enable modification of this standard behavior, by
setting up a specific environment for the target program.
* --env-reset prevents minijail0 to pass its own environment to the
program. Note that if this option is used without any --env-add option,
the program starts with a completely empty environment.
* --env-add allows setting any number of environment variables to be
added to the environment set up for the program. Note that the special
environment variable LD_PRELOAD can also be set this way, without
impacting minijail0's own invocation.
Test: `make tests`
Test: `valgrind ./minijail0 --env-add A=X /usr/bin/env`
Test: `valgrind ./minijail0 --env-add A=X --env-add A=Y /usr/bin/env`
Test: `valgrind ./minijail0 --env-reset /usr/bin/env`
Test: `valgrind ./minijail0 --env-reset --env-reset /usr/bin/env`
Test: `valgrind ./minijail0 --env-reset --env-add A=X /usr/bin/env`
Test: `valgrind ./minijail0 --env-add A=X --env-reset --env-add A=Y /usr/bin/env`
Test: `valgrind ./minijail0 --env-add A=X --env-reset --env-add A=Y --env-reset /usr/bin/env`
Test: `minijail0 --env-reset --env-add LD_PRELOAD="a b" --env-add FOO=bar /usr/bin/env`
"""
ERROR: ld.so: object 'a' from LD_PRELOAD cannot be preloaded (cannot open shared object file): ignored.
ERROR: ld.so: object 'b' from LD_PRELOAD cannot be preloaded (cannot open shared object file): ignored.
LD_PRELOAD=a b
FOO=bar
"""
Change-Id: I2e106003824ff6707b80b6653141657e4ec40f47
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 @@
* 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 @@
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 @@
{"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 @@
" 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 @@
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 @@
}
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 @@
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.
*/