diff --git a/bubblewrap.c b/bubblewrap.c
index de063058..fc68f952 100644
--- a/bubblewrap.c
+++ b/bubblewrap.c
@@ -59,6 +59,8 @@
* 2^64 - 2^12. */
#define MAX_TMPFS_BYTES ((size_t) (SIZE_MAX >> 1))
+#define LATEST_COMPAT_LEVEL 1
+
/* Globals to avoid having to use getuid(), since the uid/gid changes during runtime */
static uid_t real_uid;
static gid_t real_gid;
@@ -75,6 +77,7 @@ static bool opt_as_pid_1;
static const char *opt_chdir_path = NULL;
static bool opt_assert_userns_disabled = FALSE;
static bool opt_disable_userns = FALSE;
+static bool opt_disable_userns_set = FALSE;
static bool opt_unshare_user = FALSE;
static bool opt_unshare_user_try = FALSE;
static bool opt_unshare_pid = FALSE;
@@ -85,6 +88,7 @@ static bool opt_unshare_cgroup = FALSE;
static bool opt_unshare_cgroup_try = FALSE;
static bool opt_needs_devpts = FALSE;
static bool opt_new_session = FALSE;
+static bool opt_new_session_set = FALSE;
static bool opt_die_with_parent = FALSE;
static uid_t opt_sandbox_uid = -1;
static gid_t opt_sandbox_gid = -1;
@@ -99,6 +103,7 @@ static char *opt_args_data = NULL; /* owned */
static int opt_userns_fd = -1;
static int opt_userns2_fd = -1;
static int opt_pidns_fd = -1;
+static int opt_compat_level = 0;
static int next_perms = -1;
static size_t next_size_arg = 0;
@@ -308,6 +313,7 @@ usage (int ecode, FILE *out)
fprintf (out,
" --help Print this help\n"
" --version Print version\n"
+ " --compat Set compatability level (negative value means latest)\n"
" --args FD Parse NUL-separated args from FD\n"
" --unshare-all Unshare every namespace we support by default\n"
" --share-net Retain the network namespace (can only combine with --unshare-all)\n"
@@ -321,7 +327,7 @@ usage (int ecode, FILE *out)
" --unshare-cgroup-try Create new cgroup namespace if possible else continue by skipping it\n"
" --userns FD Use this user namespace (cannot combine with --unshare-user)\n"
" --userns2 FD After setup switch to this user namespace, only useful with --userns\n"
- " --disable-userns Disable further use of user namespaces inside sandbox\n"
+ "%s" /* --(disable/allow)-userns */
" --assert-userns-disabled Fail unless further use of user namespace inside sandbox is disabled\n"
" --pidns FD Use this pid namespace (as parent namespace if using --unshare-pid)\n"
" --uid UID Custom uid in the sandbox (requires --unshare-user or --userns)\n"
@@ -357,15 +363,20 @@ usage (int ecode, FILE *out)
" --userns-block-fd FD Block on FD until the user namespace is ready\n"
" --info-fd FD Write information about the running container to FD\n"
" --json-status-fd FD Write container status to FD as multiple JSON documents\n"
- " --new-session Create a new terminal session\n"
+ "%s" /* -(-no)-new-session */
" --die-with-parent Kills with SIGKILL child process (COMMAND) when bwrap or bwrap's parent dies.\n"
" --as-pid-1 Do not install a reaper process with PID=1\n"
" --cap-add CAP Add cap CAP when running as privileged user\n"
" --cap-drop CAP Drop cap CAP when running as privileged user\n"
" --perms OCTAL Set permissions of next argument (--bind-data, --file, etc.)\n"
" --size BYTES Set size of next argument (only for --tmpfs)\n"
- " --chmod OCTAL PATH Change permissions of PATH (must already exist)\n"
- );
+ " --chmod OCTAL PATH Change permissions of PATH (must already exist)\n",
+ opt_compat_level == 0 ?
+ " --disable-userns Disable further use of user namespaces inside sandbox\n" :
+ " --allow-userns Allow further use of user namespaces inside sandbox\n",
+ opt_compat_level == 0 ?
+ " --new-session Create a new terminal session\n" :
+ " --no-new-session Don't create a new terminal session\n");
exit (ecode);
}
@@ -1652,18 +1663,43 @@ parse_args_recurse (int *argcp,
if (*total_parsed_argc_p > MAX_ARGS)
die ("Exceeded maximum number of arguments %u", MAX_ARGS);
+ bool print_help = FALSE;
while (argc > 0)
{
const char *arg = argv[0];
if (strcmp (arg, "--help") == 0)
{
- usage (EXIT_SUCCESS, stdout);
+ /* Defer printing help as it now varies depending on the compat level. */
+ print_help = TRUE;
}
else if (strcmp (arg, "--version") == 0)
{
print_version_and_exit ();
}
+ else if (strcmp (arg, "--compat") == 0)
+ {
+ int the_compat_level;
+ char *endptr;
+
+ if (argc < 2)
+ die ("--compat takes an argument");
+
+ the_compat_level = strtol (argv[1], &endptr, 10);
+ if (argv[1][0] == 0 || endptr[0] != 0)
+ die ("Invalid compat level: %s", argv[1]);
+
+ if (the_compat_level > LATEST_COMPAT_LEVEL)
+ die ("Compat level %d is not suported by this version (latest supported is %d)", the_compat_level, LATEST_COMPAT_LEVEL);
+
+ if (the_compat_level < 0)
+ the_compat_level = LATEST_COMPAT_LEVEL;
+
+ opt_compat_level = the_compat_level;
+
+ argv += 1;
+ argc -= 1;
+ }
else if (strcmp (arg, "--args") == 0)
{
int the_fd;
@@ -1789,13 +1825,20 @@ parse_args_recurse (int *argcp,
argv++;
argc--;
}
- else if (strcmp (arg, "--disable-userns") == 0)
+ else if (opt_compat_level == 0 && strcmp (arg, "--disable-userns") == 0)
{
opt_disable_userns = TRUE;
+ opt_disable_userns_set = TRUE;
}
else if (strcmp (arg, "--assert-userns-disabled") == 0)
{
opt_assert_userns_disabled = TRUE;
+ opt_disable_userns_set = TRUE;
+ }
+ else if (opt_compat_level > 0 && strcmp (arg, "--allow-userns") == 0)
+ {
+ opt_disable_userns = FALSE;
+ opt_disable_userns_set = TRUE;
}
else if (strcmp (arg, "--remount-ro") == 0)
{
@@ -1980,7 +2023,10 @@ parse_args_recurse (int *argcp,
if (next_perms >= 0)
op->perms = next_perms;
else
- op->perms = 0666;
+ if (opt_compat_level == 0)
+ op->perms = 0666;
+ else
+ op->perms = 0644;
next_perms = -1;
argv += 2;
@@ -2176,7 +2222,12 @@ parse_args_recurse (int *argcp,
die ("--seccomp cannot be combined with --add-seccomp-fd");
if (opt_seccomp_fd != -1)
- warn_only_last_option ("--seccomp");
+ {
+ if (opt_compat_level == 0)
+ warn_only_last_option ("--seccomp");
+ else
+ die ("More than one --seccomp options specified, use --add-seccomp-fd instead.");
+ }
the_fd = strtol (argv[1], &endptr, 10);
if (argv[1][0] == 0 || endptr[0] != 0 || the_fd < 0)
@@ -2349,9 +2400,15 @@ parse_args_recurse (int *argcp,
argv += 1;
argc -= 1;
}
- else if (strcmp (arg, "--new-session") == 0)
+ else if (opt_compat_level == 0 && strcmp (arg, "--new-session") == 0)
{
opt_new_session = TRUE;
+ opt_new_session_set = TRUE;
+ }
+ else if (opt_compat_level > 0 && strcmp (arg, "--no-new-session") == 0)
+ {
+ opt_new_session = FALSE;
+ opt_new_session_set = TRUE;
}
else if (strcmp (arg, "--die-with-parent") == 0)
{
@@ -2526,6 +2583,15 @@ parse_args_recurse (int *argcp,
argc--;
}
+ if (print_help)
+ usage (EXIT_SUCCESS, stdout);
+
+ if (opt_compat_level > 0 && opt_unshare_user && !opt_disable_userns_set)
+ opt_disable_userns = TRUE;
+
+ if (opt_compat_level > 0 && !opt_new_session_set)
+ opt_new_session = TRUE;
+
*argcp = argc;
*argvp = argv;
}
diff --git a/bwrap.xml b/bwrap.xml
index 9d770ac0..e9b59076 100644
--- a/bwrap.xml
+++ b/bwrap.xml
@@ -84,6 +84,10 @@
Print version
+
+
+ Set compatability level (negative value means latest)
+
@@ -145,6 +149,15 @@
After setting up the new namespace, switch into the specified namespace. For this to work the specified namespace must be a descendant of the user namespace used for the setup, so this is only useful in combination with --userns.This is useful because sometimes bubblewrap itself creates nested user namespaces (to work around some kernel issues) and --userns2 can be used to enter these.
+
+
+
+ Allow the process in the sandbox to create further user namespaces,
+ so that it can rearrange the filesystem namespace or do other more
+ complex namespace modification.
+ This option is only available in compatability level 1 or later.
+
+
@@ -157,6 +170,7 @@
in the outer namespace.
This option requires , and doesn't work
in the setuid version of bubblewrap.
+ This option is not available in compatability level 1 or later.
@@ -455,12 +469,29 @@
ignore members and objects that they do not understand.
+
+
+
+ Don't create a new terminal session for the sandbox (don't call
+ setsid()). This doesn't disconnect the sandbox from the controlling
+ terminal which means the sandbox can for instance inject input into
+ the terminal. This option is only available in compatability level 1
+ or later.
+
+ Note: In a general sandbox, if you use --no-new-session, it is
+ recommended to use seccomp to disallow the TIOCSTI ioctl, otherwise
+ the application can feed keyboard input to the terminal
+ which can e.g. lead to out-of-sandbox command execution
+ (see CVE-2017-5226).
+
+
Create a new terminal session for the sandbox (calls setsid()). This
disconnects the sandbox from the controlling terminal which means
the sandbox can't for instance inject input into the terminal.
+ This option is not available in compatability level 1 or later.
Note: In a general sandbox, if you don't use --new-session, it is
recommended to use seccomp to disallow the TIOCSTI ioctl, otherwise
diff --git a/completions/bash/bwrap b/completions/bash/bwrap
index ca18d896..c9d44e20 100644
--- a/completions/bash/bwrap
+++ b/completions/bash/bwrap
@@ -9,12 +9,14 @@ _bwrap() {
# Please keep sorted in LC_ALL=C order
local boolean_options="
+ --allow-userns
--as-pid-1
--assert-userns-disabled
--clearenv
--disable-userns
--help
--new-session
+ --no-new-session
--unshare-all
--unshare-cgroup
--unshare-cgroup-try
@@ -39,6 +41,7 @@ _bwrap() {
--cap-drop
--chdir
--chmod
+ --compat
--dev
--dev-bind
--die-with-parent
diff --git a/completions/zsh/_bwrap b/completions/zsh/_bwrap
index a2e2caf3..5c079a11 100644
--- a/completions/zsh/_bwrap
+++ b/completions/zsh/_bwrap
@@ -27,6 +27,7 @@ _bwrap_args=(
# Please sort alphabetically (in LC_ALL=C order) by option name
'--add-seccomp-fd[Load and use seccomp rules from FD]: :_guard "[0-9]#" "file descriptor to read seccomp rules from"'
+ '--allow-userns[Allow further use of user namespaces inside sandbox]'
'--assert-userns-disabled[Fail unless further use of user namespace inside sandbox is disabled]'
'--args[Parse NUL-separated args from FD]: :_guard "[0-9]#" "file descriptor with NUL-separated arguments"'
'--as-pid-1[Do not install a reaper process with PID=1]'
@@ -38,6 +39,7 @@ _bwrap_args=(
'--chdir[Change directory to DIR]:working directory for sandbox: _files -/'
'--chmod[Set permissions]: :_guard "[0-7]#" "permissions in octal":path to set permissions:_files'
'--clearenv[Unset all environment variables]'
+ '--compat[Set compatability level (negative value means latest)]:compatability level:'
'--dev-bind-try[Equal to --dev-bind but ignores non-existent SRC]:source:_files:destination:_files'
'--dev-bind[Bind mount the host path SRC on DEST, allowing device access]:source:_files:destination:_files'
'--dev[Mount new dev on DEST]:mount point for /dev:_files -/'
@@ -53,6 +55,7 @@ _bwrap_args=(
'--lock-file[Take a lock on DEST while sandbox is running]:lock file:_files'
'--mqueue[Mount new mqueue on DEST]:mount point for mqueue:_files -/'
'--new-session[Create a new terminal session]'
+ "--no-new-session[Don't create a new terminal session]"
'--perms[Set permissions for next action argument]: :_guard "[0-7]#" "permissions in octal": :->after_perms'
'--pidns[Use this user namespace (as parent namespace if using --unshare-pid)]: :'
'--proc[Mount new procfs on DEST]:mount point for procfs:_files -/'