diff -Nru bubblewrap-0.2.1/aclocal.m4 bubblewrap-0.4.0/aclocal.m4 --- bubblewrap-0.2.1/aclocal.m4 2018-04-06 14:55:06.000000000 +0000 +++ bubblewrap-0.4.0/aclocal.m4 2019-11-27 12:53:16.000000000 +0000 @@ -1,6 +1,6 @@ -# generated automatically by aclocal 1.15 -*- Autoconf -*- +# generated automatically by aclocal 1.16.1 -*- Autoconf -*- -# Copyright (C) 1996-2014 Free Software Foundation, Inc. +# Copyright (C) 1996-2018 Free Software Foundation, Inc. # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -364,7 +364,7 @@ [AC_DEFINE([HAVE_][$1], 1, [Enable ]m4_tolower([$1])[ support])]) ])dnl PKG_HAVE_DEFINE_WITH_MODULES -# Copyright (C) 2002-2014 Free Software Foundation, Inc. +# Copyright (C) 2002-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -376,10 +376,10 @@ # generated from the m4 files accompanying Automake X.Y. # (This private macro should not be called outside this file.) AC_DEFUN([AM_AUTOMAKE_VERSION], -[am__api_version='1.15' +[am__api_version='1.16' dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to dnl require some minimum version. Point them to the right macro. -m4_if([$1], [1.15], [], +m4_if([$1], [1.16.1], [], [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl ]) @@ -395,14 +395,14 @@ # Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. # This function is AC_REQUIREd by AM_INIT_AUTOMAKE. AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], -[AM_AUTOMAKE_VERSION([1.15])dnl +[AM_AUTOMAKE_VERSION([1.16.1])dnl m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) # AM_AUX_DIR_EXPAND -*- Autoconf -*- -# Copyright (C) 2001-2014 Free Software Foundation, Inc. +# Copyright (C) 2001-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -454,7 +454,7 @@ # AM_CONDITIONAL -*- Autoconf -*- -# Copyright (C) 1997-2014 Free Software Foundation, Inc. +# Copyright (C) 1997-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -485,7 +485,7 @@ Usually this means the macro was only invoked conditionally.]]) fi])]) -# Copyright (C) 1999-2014 Free Software Foundation, Inc. +# Copyright (C) 1999-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -676,13 +676,12 @@ # Generate code to set up dependency tracking. -*- Autoconf -*- -# Copyright (C) 1999-2014 Free Software Foundation, Inc. +# Copyright (C) 1999-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. - # _AM_OUTPUT_DEPENDENCY_COMMANDS # ------------------------------ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], @@ -690,49 +689,41 @@ # Older Autoconf quotes --file arguments for eval, but not when files # are listed without --file. Let's play safe and only enable the eval # if we detect the quoting. - case $CONFIG_FILES in - *\'*) eval set x "$CONFIG_FILES" ;; - *) set x $CONFIG_FILES ;; - esac + # TODO: see whether this extra hack can be removed once we start + # requiring Autoconf 2.70 or later. + AS_CASE([$CONFIG_FILES], + [*\'*], [eval set x "$CONFIG_FILES"], + [*], [set x $CONFIG_FILES]) shift - for mf + # Used to flag and report bootstrapping failures. + am_rc=0 + for am_mf do # Strip MF so we end up with the name of the file. - mf=`echo "$mf" | sed -e 's/:.*$//'` - # Check whether this is an Automake generated Makefile or not. - # We used to match only the files named 'Makefile.in', but - # some people rename them; so instead we look at the file content. - # Grep'ing the first line is not enough: some people post-process - # each Makefile.in and add a new line on top of each file to say so. - # Grep'ing the whole file is not good either: AIX grep has a line + am_mf=`AS_ECHO(["$am_mf"]) | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile which includes + # dependency-tracking related rules and includes. + # Grep'ing the whole file directly is not great: AIX grep has a line # limit of 2048, but all sed's we know have understand at least 4000. - if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then - dirpart=`AS_DIRNAME("$mf")` - else - continue - fi - # Extract the definition of DEPDIR, am__include, and am__quote - # from the Makefile without running 'make'. - DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` - test -z "$DEPDIR" && continue - am__include=`sed -n 's/^am__include = //p' < "$mf"` - test -z "$am__include" && continue - am__quote=`sed -n 's/^am__quote = //p' < "$mf"` - # Find all dependency output files, they are included files with - # $(DEPDIR) in their names. We invoke sed twice because it is the - # simplest approach to changing $(DEPDIR) to its actual value in the - # expansion. - for file in `sed -n " - s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ - sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do - # Make sure the directory exists. - test -f "$dirpart/$file" && continue - fdir=`AS_DIRNAME(["$file"])` - AS_MKDIR_P([$dirpart/$fdir]) - # echo "creating $dirpart/$file" - echo '# dummy' > "$dirpart/$file" - done + sed -n 's,^am--depfiles:.*,X,p' "$am_mf" | grep X >/dev/null 2>&1 \ + || continue + am_dirpart=`AS_DIRNAME(["$am_mf"])` + am_filepart=`AS_BASENAME(["$am_mf"])` + AM_RUN_LOG([cd "$am_dirpart" \ + && sed -e '/# am--include-marker/d' "$am_filepart" \ + | $MAKE -f - am--depfiles]) || am_rc=$? done + if test $am_rc -ne 0; then + AC_MSG_FAILURE([Something went wrong bootstrapping makefile fragments + for automatic dependency tracking. Try re-running configure with the + '--disable-dependency-tracking' option to at least be able to build + the package (albeit without support for automatic dependency tracking).]) + fi + AS_UNSET([am_dirpart]) + AS_UNSET([am_filepart]) + AS_UNSET([am_mf]) + AS_UNSET([am_rc]) + rm -f conftest-deps.mk } ])# _AM_OUTPUT_DEPENDENCY_COMMANDS @@ -741,18 +732,17 @@ # ----------------------------- # This macro should only be invoked once -- use via AC_REQUIRE. # -# This code is only required when automatic dependency tracking -# is enabled. FIXME. This creates each '.P' file that we will -# need in order to bootstrap the dependency handling code. +# This code is only required when automatic dependency tracking is enabled. +# This creates each '.Po' and '.Plo' makefile fragment that we'll need in +# order to bootstrap the dependency handling code. AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], [AC_CONFIG_COMMANDS([depfiles], [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS], - [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"]) -]) + [AMDEP_TRUE="$AMDEP_TRUE" MAKE="${MAKE-make}"])]) # Do all the work for Automake. -*- Autoconf -*- -# Copyright (C) 1996-2014 Free Software Foundation, Inc. +# Copyright (C) 1996-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -839,8 +829,8 @@ AC_REQUIRE([AC_PROG_MKDIR_P])dnl # For better backward compatibility. To be removed once Automake 1.9.x # dies out for good. For more background, see: -# -# +# +# AC_SUBST([mkdir_p], ['$(MKDIR_P)']) # We need awk for the "check" target (and possibly the TAP driver). The # system "awk" is bad on some platforms. @@ -907,7 +897,7 @@ Aborting the configuration process, to ensure you take notice of the issue. You can download and install GNU coreutils to get an 'rm' implementation -that behaves properly: . +that behaves properly: . If you want to complete the configuration process using your problematic 'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM @@ -949,7 +939,7 @@ done echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) -# Copyright (C) 2001-2014 Free Software Foundation, Inc. +# Copyright (C) 2001-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -970,7 +960,7 @@ fi AC_SUBST([install_sh])]) -# Copyright (C) 2003-2014 Free Software Foundation, Inc. +# Copyright (C) 2003-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -992,7 +982,7 @@ # Add --enable-maintainer-mode option to configure. -*- Autoconf -*- # From Jim Meyering -# Copyright (C) 1996-2014 Free Software Foundation, Inc. +# Copyright (C) 1996-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -1027,7 +1017,7 @@ # Check to see how 'make' treats includes. -*- Autoconf -*- -# Copyright (C) 2001-2014 Free Software Foundation, Inc. +# Copyright (C) 2001-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -1035,49 +1025,42 @@ # AM_MAKE_INCLUDE() # ----------------- -# Check to see how make treats includes. +# Check whether make has an 'include' directive that can support all +# the idioms we need for our automatic dependency tracking code. AC_DEFUN([AM_MAKE_INCLUDE], -[am_make=${MAKE-make} -cat > confinc << 'END' +[AC_MSG_CHECKING([whether ${MAKE-make} supports the include directive]) +cat > confinc.mk << 'END' am__doit: - @echo this is the am__doit target + @echo this is the am__doit target >confinc.out .PHONY: am__doit END -# If we don't find an include directive, just comment out the code. -AC_MSG_CHECKING([for style of include used by $am_make]) am__include="#" am__quote= -_am_result=none -# First try GNU make style include. -echo "include confinc" > confmf -# Ignore all kinds of additional output from 'make'. -case `$am_make -s -f confmf 2> /dev/null` in #( -*the\ am__doit\ target*) - am__include=include - am__quote= - _am_result=GNU - ;; -esac -# Now try BSD make style include. -if test "$am__include" = "#"; then - echo '.include "confinc"' > confmf - case `$am_make -s -f confmf 2> /dev/null` in #( - *the\ am__doit\ target*) - am__include=.include - am__quote="\"" - _am_result=BSD - ;; - esac -fi -AC_SUBST([am__include]) -AC_SUBST([am__quote]) -AC_MSG_RESULT([$_am_result]) -rm -f confinc confmf -]) +# BSD make does it like this. +echo '.include "confinc.mk" # ignored' > confmf.BSD +# Other make implementations (GNU, Solaris 10, AIX) do it like this. +echo 'include confinc.mk # ignored' > confmf.GNU +_am_result=no +for s in GNU BSD; do + AM_RUN_LOG([${MAKE-make} -f confmf.$s && cat confinc.out]) + AS_CASE([$?:`cat confinc.out 2>/dev/null`], + ['0:this is the am__doit target'], + [AS_CASE([$s], + [BSD], [am__include='.include' am__quote='"'], + [am__include='include' am__quote=''])]) + if test "$am__include" != "#"; then + _am_result="yes ($s style)" + break + fi +done +rm -f confinc.* confmf.* +AC_MSG_RESULT([${_am_result}]) +AC_SUBST([am__include])]) +AC_SUBST([am__quote])]) # Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- -# Copyright (C) 1997-2014 Free Software Foundation, Inc. +# Copyright (C) 1997-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -1116,7 +1099,7 @@ # Helper functions for option handling. -*- Autoconf -*- -# Copyright (C) 2001-2014 Free Software Foundation, Inc. +# Copyright (C) 2001-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -1145,7 +1128,7 @@ AC_DEFUN([_AM_IF_OPTION], [m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) -# Copyright (C) 1999-2014 Free Software Foundation, Inc. +# Copyright (C) 1999-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -1192,7 +1175,7 @@ # For backward compatibility. AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])]) -# Copyright (C) 2001-2014 Free Software Foundation, Inc. +# Copyright (C) 2001-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -1211,7 +1194,7 @@ # Check to make sure that the build environment is sane. -*- Autoconf -*- -# Copyright (C) 1996-2014 Free Software Foundation, Inc. +# Copyright (C) 1996-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -1292,7 +1275,7 @@ rm -f conftest.file ]) -# Copyright (C) 2009-2014 Free Software Foundation, Inc. +# Copyright (C) 2009-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -1352,7 +1335,7 @@ _AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl ]) -# Copyright (C) 2001-2014 Free Software Foundation, Inc. +# Copyright (C) 2001-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -1380,7 +1363,7 @@ INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" AC_SUBST([INSTALL_STRIP_PROGRAM])]) -# Copyright (C) 2006-2014 Free Software Foundation, Inc. +# Copyright (C) 2006-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -1399,7 +1382,7 @@ # Check how to create a tarball. -*- Autoconf -*- -# Copyright (C) 2004-2014 Free Software Foundation, Inc. +# Copyright (C) 2004-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, diff -Nru bubblewrap-0.2.1/bubblewrap.c bubblewrap-0.4.0/bubblewrap.c --- bubblewrap-0.2.1/bubblewrap.c 2018-04-06 14:52:40.000000000 +0000 +++ bubblewrap-0.4.0/bubblewrap.c 2019-11-27 12:34:31.000000000 +0000 @@ -42,6 +42,15 @@ #define CLONE_NEWCGROUP 0x02000000 /* New cgroup namespace */ #endif +#ifndef TEMP_FAILURE_RETRY +#define TEMP_FAILURE_RETRY(expression) \ + (__extension__ \ + ({ long int __result; \ + do __result = (long int) (expression); \ + while (__result == -1L && errno == EINTR); \ + __result; })) +#endif + /* Globals to avoid having to use getuid(), since the uid/gid changes during runtime */ static uid_t real_uid; static gid_t real_gid; @@ -73,13 +82,38 @@ int opt_block_fd = -1; int opt_userns_block_fd = -1; int opt_info_fd = -1; +int opt_json_status_fd = -1; int opt_seccomp_fd = -1; const char *opt_sandbox_hostname = NULL; char *opt_args_data = NULL; /* owned */ +int opt_userns_fd = -1; +int opt_userns2_fd = -1; +int opt_pidns_fd = -1; #define CAP_TO_MASK_0(x) (1L << ((x) & 31)) #define CAP_TO_MASK_1(x) CAP_TO_MASK_0(x - 32) +typedef struct _NsInfo NsInfo; + +struct _NsInfo { + const char *name; + bool *do_unshare; + ino_t id; +}; + +static NsInfo ns_infos[] = { + {"cgroup", &opt_unshare_cgroup, 0}, + {"ipc", &opt_unshare_ipc, 0}, + {"mnt", NULL, 0}, + {"net", &opt_unshare_net, 0}, + {"pid", &opt_unshare_pid, 0}, + /* user namespace info omitted because it + * is not (yet) valid when we obtain the + * namespace info (get un-shared later) */ + {"uts", &opt_unshare_uts, 0}, + {NULL, NULL, 0} +}; + typedef enum { SETUP_BIND_MOUNT, SETUP_RO_BIND_MOUNT, @@ -99,6 +133,7 @@ typedef enum { NO_CREATE_DEST = (1 << 0), + ALLOW_NOTEXIST = (2 << 0), } SetupOpFlag; typedef struct _SetupOp SetupOp; @@ -182,7 +217,7 @@ static void usage (int ecode, FILE *out) { - fprintf (out, "usage: %s [OPTIONS...] COMMAND [ARGS...]\n\n", argv0); + fprintf (out, "usage: %s [OPTIONS...] [--] COMMAND [ARGS...]\n\n", argv0); fprintf (out, " --help Print this help\n" @@ -198,8 +233,11 @@ " --unshare-uts Create new uts namespace\n" " --unshare-cgroup Create new cgroup namespace\n" " --unshare-cgroup-try Create new cgroup namespace if possible else continue by skipping it\n" - " --uid UID Custom uid in the sandbox (requires --unshare-user)\n" - " --gid GID Custom gid in the sandbox (requires --unshare-user)\n" + " --userns FD Use this user namespace (cannot combine with --unshare-user)\n" + " --userns2 FD After setup switch to this user namspace, only useful with --userns\n" + " --pidns FD Use this user namespace (as parent namespace if using --unshare-pid)\n" + " --uid UID Custom uid in the sandbox (requires --unshare-user or --userns)\n" + " --gid GID Custom gid in the sandbox (requires --unshare-user or --userns)\n" " --hostname NAME Custom hostname in the sandbox (requires --unshare-uts)\n" " --chdir DIR Change directory to DIR\n" " --setenv VAR VALUE Set an environment variable\n" @@ -207,8 +245,11 @@ " --lock-file DEST Take a lock on DEST while sandbox is running\n" " --sync-fd FD Keep this fd open while sandbox is running\n" " --bind SRC DEST Bind mount the host path SRC on DEST\n" + " --bind-try SRC DEST Equal to --bind but ignores non-existent SRC\n" " --dev-bind SRC DEST Bind mount the host path SRC on DEST, allowing device access\n" + " --dev-bind-try SRC DEST Equal to --dev-bind but ignores non-existent SRC\n" " --ro-bind SRC DEST Bind mount the host path SRC readonly on DEST\n" + " --ro-bind-try SRC DEST Equal to --ro-bind but ignores non-existent SRC\n" " --remount-ro DEST Remount DEST as readonly; does not recursively remount\n" " --exec-label LABEL Exec label for the sandbox\n" " --file-label LABEL File label for temporary sandbox content\n" @@ -225,6 +266,7 @@ " --block-fd FD Block on FD until some data to read is available\n" " --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" " --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" @@ -310,14 +352,47 @@ return 255; } +static void +dump_info (int fd, const char *output, bool exit_on_error) +{ + size_t len = strlen (output); + if (write_to_fd (fd, output, len)) + { + if (exit_on_error) + die_with_error ("Write to info_fd"); + } +} + +static void +report_child_exit_status (int exitc, int setup_finished_fd) +{ + ssize_t s; + char data[2]; + cleanup_free char *output = NULL; + if (opt_json_status_fd == -1 || setup_finished_fd == -1) + return; + + s = TEMP_FAILURE_RETRY (read (setup_finished_fd, data, sizeof data)); + if (s == -1 && errno != EAGAIN) + die_with_error ("read eventfd"); + if (s != 1) // Is 0 if pipe closed before exec, is 2 if closed after exec. + return; + + output = xasprintf ("{ \"exit-code\": %i }\n", exitc); + dump_info (opt_json_status_fd, output, FALSE); + close (opt_json_status_fd); + opt_json_status_fd = -1; + close (setup_finished_fd); +} + /* This stays around for as long as the initial process in the app does * and when that exits it exits, propagating the exit status. We do this * by having pid 1 in the sandbox detect this exit and tell the monitor * the exit status via a eventfd. We also track the exit of the sandbox * pid 1 via a signalfd for SIGCHLD, and exit with an error in this case. * This is to catch e.g. problems during setup. */ -static void -monitor_child (int event_fd, pid_t child_pid) +static int +monitor_child (int event_fd, pid_t child_pid, int setup_finished_fd) { int res; uint64_t val; @@ -327,12 +402,21 @@ struct pollfd fds[2]; int num_fds; struct signalfd_siginfo fdsi; - int dont_close[] = { event_fd, -1 }; + int dont_close[] = {-1, -1, -1, -1}; + int j = 0; + int exitc; pid_t died_pid; int died_status; /* Close all extra fds in the monitoring process. Any passed in fds have been passed on to the child anyway. */ + if (event_fd != -1) + dont_close[j++] = event_fd; + if (opt_json_status_fd != -1) + dont_close[j++] = opt_json_status_fd; + if (setup_finished_fd != -1) + dont_close[j++] = setup_finished_fd; + assert (j < sizeof(dont_close)/sizeof(*dont_close)); fdwalk (proc_fd, close_extra_fds, dont_close); sigemptyset (&mask); @@ -368,12 +452,16 @@ if (s == -1 && errno != EINTR && errno != EAGAIN) die_with_error ("read eventfd"); else if (s == 8) - exit ((int) val - 1); + { + exitc = (int) val - 1; + report_child_exit_status (exitc, setup_finished_fd); + return exitc; + } } /* We need to read the signal_fd, or it will keep polling as read, * however we ignore the details as we get them from waitpid - * below anway */ + * below anyway */ s = read (signal_fd, &fdsi, sizeof (struct signalfd_siginfo)); if (s == -1 && errno != EINTR && errno != EAGAIN) die_with_error ("read signalfd"); @@ -385,9 +473,17 @@ /* We may be getting sigchild from other children too. For instance if someone created a child process, and then exec:ed bubblewrap. Ignore them */ if (died_pid == child_pid) - exit (propagate_exit_status (died_status)); + { + exitc = propagate_exit_status (died_status); + report_child_exit_status (exitc, setup_finished_fd); + return exitc; + } } } + + die ("Should not be reached"); + + return 0; } /* This is pid 1 in the app sandbox. It is needed because we're using @@ -479,7 +575,8 @@ static uint32_t requested_caps[2] = {0, 0}; /* low 32bit caps needed */ -#define REQUIRED_CAPS_0 (CAP_TO_MASK_0 (CAP_SYS_ADMIN) | CAP_TO_MASK_0 (CAP_SYS_CHROOT) | CAP_TO_MASK_0 (CAP_NET_ADMIN) | CAP_TO_MASK_0 (CAP_SETUID) | CAP_TO_MASK_0 (CAP_SETGID)) +/* CAP_SYS_PTRACE is needed to dereference the symlinks in /proc//ns/, see namespaces(7) */ +#define REQUIRED_CAPS_0 (CAP_TO_MASK_0 (CAP_SYS_ADMIN) | CAP_TO_MASK_0 (CAP_SYS_CHROOT) | CAP_TO_MASK_0 (CAP_NET_ADMIN) | CAP_TO_MASK_0 (CAP_SETUID) | CAP_TO_MASK_0 (CAP_SETGID) | CAP_TO_MASK_0 (CAP_SYS_PTRACE)) /* high 32bit caps needed */ #define REQUIRED_CAPS_1 0 @@ -630,7 +727,7 @@ * "is_privileged = FALSE". * * If bwrap is setuid, then we do things in phases. - * The first part is run as euid 0, but with with fsuid as the real user. + * The first part is run as euid 0, but with fsuid as the real user. * The second part, inside the child, is run as the real user but with * capabilities. * And finally we drop all capabilities. @@ -708,9 +805,19 @@ switch_to_user_with_privs (void) { /* If we're in a new user namespace, we got back the bounding set, clear it again */ - if (opt_unshare_user) + if (opt_unshare_user || opt_userns_fd != -1) drop_cap_bounding_set (FALSE); + /* If we switched to a new user namespace it may allow other uids/gids, so switch to the target one */ + if (opt_userns_fd != -1) + { + if (opt_sandbox_uid != real_uid && setuid (opt_sandbox_uid) < 0) + die_with_error ("unable to switch to uid %d", opt_sandbox_uid); + + if (opt_sandbox_gid != real_gid && setgid (opt_sandbox_gid) < 0) + die_with_error ("unable to switch to gid %d", opt_sandbox_gid); + } + if (!is_privileged) return; @@ -731,10 +838,14 @@ { assert (!keep_requested_caps || !is_privileged); /* Drop root uid */ - if (getuid () == 0 && setuid (opt_sandbox_uid) < 0) + if (geteuid () == 0 && setuid (opt_sandbox_uid) < 0) die_with_error ("unable to drop root uid"); drop_all_caps (keep_requested_caps); + + /* We don't have any privs now, so mark us dumpable which makes /proc/self be owned by the user instead of root */ + if (prctl (PR_SET_DUMPABLE, 1, 0, 0, 0) != 0) + die_with_error ("can't set dumpable"); } static char * @@ -962,7 +1073,11 @@ source = get_oldroot_path (op->source); source_mode = get_file_mode (source); if (source_mode < 0) - die_with_error ("Can't get type of source %s", op->source); + { + if (op->flags & ALLOW_NOTEXIST && errno == ENOENT) + continue; /* Ignore and move on */ + die_with_error("Can't get type of source %s", op->source); + } } if (op->dest && @@ -1002,7 +1117,7 @@ if (ensure_dir (dest, 0755) != 0) die_with_error ("Can't mkdir %s", op->dest); - if (unshare_pid) + if (unshare_pid || opt_pidns_fd != -1) { /* Our own procfs */ privileged_op (privileged_op_socket, @@ -1025,9 +1140,15 @@ for (i = 0; i < N_ELEMENTS (cover_proc_dirs); i++) { cleanup_free char *subdir = strconcat3 (dest, "/", cover_proc_dirs[i]); - /* Some of these may not exist */ - if (get_file_mode (subdir) == -1) - continue; + if (access (subdir, W_OK) < 0) + { + /* The file is already read-only or doesn't exist. */ + if (errno == EACCES || errno == ENOENT) + continue; + + die_with_error ("Can't access %s", subdir); + } + privileged_op (privileged_op_socket, PRIV_SEP_OP_BIND_MOUNT, BIND_READONLY, subdir, subdir); @@ -1242,7 +1363,12 @@ old_source = op->source; op->source = realpath (old_source, NULL); if (op->source == NULL) - die_with_error ("Can't find source path %s", old_source); + { + if (op->flags & ALLOW_NOTEXIST && errno == ENOENT) + op->source = old_source; + else + die_with_error("Can't find source path %s", old_source); + } break; default: break; @@ -1475,38 +1601,47 @@ argv++; argc--; } - else if (strcmp (arg, "--bind") == 0) + else if (strcmp(arg, "--bind") == 0 || + strcmp(arg, "--bind-try") == 0) { if (argc < 3) - die ("--bind takes two arguments"); + die ("%s takes two arguments", arg); op = setup_op_new (SETUP_BIND_MOUNT); op->source = argv[1]; op->dest = argv[2]; + if (strcmp(arg, "--bind-try") == 0) + op->flags = ALLOW_NOTEXIST; argv += 2; argc -= 2; } - else if (strcmp (arg, "--ro-bind") == 0) + else if (strcmp(arg, "--ro-bind") == 0 || + strcmp(arg, "--ro-bind-try") == 0) { if (argc < 3) - die ("--ro-bind takes two arguments"); + die ("%s takes two arguments", arg); op = setup_op_new (SETUP_RO_BIND_MOUNT); op->source = argv[1]; op->dest = argv[2]; + if (strcmp(arg, "--ro-bind-try") == 0) + op->flags = ALLOW_NOTEXIST; argv += 2; argc -= 2; } - else if (strcmp (arg, "--dev-bind") == 0) + else if (strcmp (arg, "--dev-bind") == 0 || + strcmp (arg, "--dev-bind-try") == 0) { if (argc < 3) - die ("--dev-bind takes two arguments"); + die ("%s takes two arguments", arg); op = setup_op_new (SETUP_DEV_BIND_MOUNT); op->source = argv[1]; op->dest = argv[2]; + if (strcmp(arg, "--dev-bind-try") == 0) + op->flags = ALLOW_NOTEXIST; argv += 2; argc -= 2; @@ -1736,6 +1871,23 @@ argv += 1; argc -= 1; } + else if (strcmp (arg, "--json-status-fd") == 0) + { + int the_fd; + char *endptr; + + if (argc < 2) + die ("--json-status-fd takes an argument"); + + the_fd = strtol (argv[1], &endptr, 10); + if (argv[1][0] == 0 || endptr[0] != 0 || the_fd < 0) + die ("Invalid fd: %s", argv[1]); + + opt_json_status_fd = the_fd; + + argv += 1; + argc -= 1; + } else if (strcmp (arg, "--seccomp") == 0) { int the_fd; @@ -1753,6 +1905,57 @@ argv += 1; argc -= 1; } + else if (strcmp (arg, "--userns") == 0) + { + int the_fd; + char *endptr; + + if (argc < 2) + die ("--userns takes an argument"); + + the_fd = strtol (argv[1], &endptr, 10); + if (argv[1][0] == 0 || endptr[0] != 0 || the_fd < 0) + die ("Invalid fd: %s", argv[1]); + + opt_userns_fd = the_fd; + + argv += 1; + argc -= 1; + } + else if (strcmp (arg, "--userns2") == 0) + { + int the_fd; + char *endptr; + + if (argc < 2) + die ("--userns2 takes an argument"); + + the_fd = strtol (argv[1], &endptr, 10); + if (argv[1][0] == 0 || endptr[0] != 0 || the_fd < 0) + die ("Invalid fd: %s", argv[1]); + + opt_userns2_fd = the_fd; + + argv += 1; + argc -= 1; + } + else if (strcmp (arg, "--pidns") == 0) + { + int the_fd; + char *endptr; + + if (argc < 2) + die ("--pidns takes an argument"); + + the_fd = strtol (argv[1], &endptr, 10); + if (argv[1][0] == 0 || endptr[0] != 0 || the_fd < 0) + die ("Invalid fd: %s", argv[1]); + + opt_pidns_fd = the_fd; + + argv += 1; + argc -= 1; + } else if (strcmp (arg, "--setenv") == 0) { if (argc < 3) @@ -1885,6 +2088,12 @@ argv += 1; argc -= 1; } + else if (strcmp (arg, "--") == 0) + { + argv += 1; + argc -= 1; + break; + } else if (*arg == '-') { die ("Unknown option %s", arg); @@ -1934,17 +2143,77 @@ die ("Can't parse /proc/sys/kernel/overflowgid"); } +static void +namespace_ids_read (pid_t pid) +{ + cleanup_free char *dir = NULL; + cleanup_fd int ns_fd = -1; + NsInfo *info; + + dir = xasprintf ("%d/ns", pid); + ns_fd = openat (proc_fd, dir, O_PATH); + + if (ns_fd < 0) + die_with_error ("open /proc/%s/ns failed", dir); + + for (info = ns_infos; info->name; info++) + { + bool *do_unshare = info->do_unshare; + struct stat st; + int r; + + /* if we don't unshare this ns, ignore it */ + if (do_unshare && *do_unshare == FALSE) + continue; + + r = fstatat (ns_fd, info->name, &st, 0); + + /* if we can't get the information, ignore it */ + if (r != 0) + continue; + + info->id = st.st_ino; + } +} + +static void +namespace_ids_write (int fd, + bool in_json) +{ + NsInfo *info; + + for (info = ns_infos; info->name; info++) + { + cleanup_free char *output = NULL; + const char *indent; + uintmax_t nsid; + + nsid = (uintmax_t) info->id; + + /* if we don't have the information, we don't write it */ + if (nsid == 0) + continue; + + indent = in_json ? " " : "\n "; + output = xasprintf (",%s\"%s-namespace\": %ju", + indent, info->name, nsid); + + dump_info (fd, output, TRUE); + } +} + int main (int argc, char **argv) { mode_t old_umask; - cleanup_free char *base_path = NULL; + const char *base_path = NULL; int clone_flags; char *old_cwd = NULL; pid_t pid; int event_fd = -1; int child_wait_fd = -1; + int setup_finished_pipe[] = {-1, -1}; const char *new_cwd; uid_t ns_uid; gid_t ns_gid; @@ -1955,6 +2224,7 @@ size_t seccomp_len; struct sock_fprog seccomp_prog; cleanup_free char *args_data = NULL; + int intermediate_pids_sockets[2] = {-1, -1}; /* Handle --version early on before we try to acquire/drop * any capabilities so it works in a build environment; @@ -2005,14 +2275,35 @@ if (opt_userns_block_fd != -1 && opt_info_fd == -1) die ("--userns-block-fd requires --info-fd"); + if (opt_userns_fd != -1 && opt_unshare_user) + die ("--userns not compatible --unshare-user"); + + if (opt_userns_fd != -1 && opt_unshare_user_try) + die ("--userns not compatible --unshare-user-try"); + + /* Technically using setns() is probably safe even in the privileged + * case, because we got passed in a file descriptor to the + * namespace, and that can only be gotten if you have ptrace + * permissions against the target, and then you could do whatever to + * the namespace anyway. + * + * However, for practical reasons this isn't possible to use, + * because (as described in acquire_privs()) setuid bwrap causes + * root to own the namespaces that it creates, so you will not be + * able to access these namespaces anyway. So, best just not support + * it anway. + */ + if (opt_userns_fd != -1 && is_privileged) + die ("--userns doesn't work in setuid mode"); + /* We have to do this if we weren't installed setuid (and we're not * root), so let's just DWIM */ - if (!is_privileged && getuid () != 0) + if (!is_privileged && getuid () != 0 && opt_userns_fd == -1) opt_unshare_user = TRUE; #ifdef ENABLE_REQUIRE_USERNS /* In this build option, we require userns. */ - if (is_privileged && getuid () != 0) + if (is_privileged && getuid () != 0 && opt_userns_fd == -1) opt_unshare_user = TRUE; #endif @@ -2057,11 +2348,11 @@ if (opt_sandbox_gid == -1) opt_sandbox_gid = real_gid; - if (!opt_unshare_user && opt_sandbox_uid != real_uid) - die ("Specifying --uid requires --unshare-user"); + if (!opt_unshare_user && opt_userns_fd == -1 && opt_sandbox_uid != real_uid) + die ("Specifying --uid requires --unshare-user or --userns"); - if (!opt_unshare_user && opt_sandbox_gid != real_gid) - die ("Specifying --gid requires --unshare-user"); + if (!opt_unshare_user && opt_userns_fd == -1 && opt_sandbox_gid != real_gid) + die ("Specifying --gid requires --unshare-user or --userns"); if (!opt_unshare_uts && opt_sandbox_hostname != NULL) die ("Specifying --hostname requires --unshare-uts"); @@ -2079,15 +2370,12 @@ die_with_error ("Can't open /proc"); /* We need *some* mountpoint where we can mount the root tmpfs. - We first try in /run, and if that fails, try in /tmp. */ - base_path = xasprintf ("/run/user/%d/.bubblewrap", real_uid); - if (ensure_dir (base_path, 0755)) - { - free (base_path); - base_path = xasprintf ("/tmp/.bubblewrap-%d", real_uid); - if (ensure_dir (base_path, 0755)) - die_with_error ("Creating root mountpoint failed"); - } + * Because we use pivot_root, it won't appear to be mounted from + * the perspective of the sandboxed process, so we can use anywhere + * that is sure to exist, that is sure to not be a symlink controlled + * by someone malicious, and that we won't immediately need to + * access ourselves. */ + base_path = "/tmp"; __debug__ (("creating new namespace\n")); @@ -2104,7 +2392,7 @@ clone_flags = SIGCHLD | CLONE_NEWNS; if (opt_unshare_user) clone_flags |= CLONE_NEWUSER; - if (opt_unshare_pid) + if (opt_unshare_pid && opt_pidns_fd == -1) clone_flags |= CLONE_NEWPID; if (opt_unshare_net) clone_flags |= CLONE_NEWNET; @@ -2124,13 +2412,41 @@ clone_flags |= CLONE_NEWCGROUP; } if (opt_unshare_cgroup_try) - if (!stat ("/proc/self/ns/cgroup", &sbuf)) - clone_flags |= CLONE_NEWCGROUP; + { + opt_unshare_cgroup = !stat ("/proc/self/ns/cgroup", &sbuf); + if (opt_unshare_cgroup) + clone_flags |= CLONE_NEWCGROUP; + } child_wait_fd = eventfd (0, EFD_CLOEXEC); if (child_wait_fd == -1) die_with_error ("eventfd()"); + /* Track whether pre-exec setup finished if we're reporting process exit */ + if (opt_json_status_fd != -1) + { + int ret; + ret = pipe2 (setup_finished_pipe, O_CLOEXEC); + if (ret == -1) + die_with_error ("pipe2()"); + } + + /* Switch to the custom user ns before the clone, gets us privs in that ns (assuming its a child of the current and thus allowed) */ + if (opt_userns_fd > 0 && setns (opt_userns_fd, CLONE_NEWUSER) != 0) + { + if (errno == EINVAL) + die ("Joining the specified user namespace failed, it might not be a descendant of the current user namespace."); + die_with_error ("Joining specified user namespace failed"); + } + + /* Sometimes we have uninteresting intermediate pids during the setup, set up code to pass the real pid down */ + if (opt_pidns_fd != -1) + { + /* Mark us as a subreaper, this way we can get exit status from grandchildren */ + prctl (PR_SET_CHILD_SUBREAPER, 1, 0, 0, 0); + create_pid_socketpair (intermediate_pids_sockets); + } + pid = raw_clone (clone_flags, NULL); if (pid == -1) { @@ -2152,6 +2468,16 @@ { /* Parent, outside sandbox, privileged (initially) */ + if (intermediate_pids_sockets[0] != -1) + { + close (intermediate_pids_sockets[1]); + pid = read_pid_from_socket (intermediate_pids_sockets[0]); + close (intermediate_pids_sockets[0]); + } + + /* Discover namespace ids before we drop privileges */ + namespace_ids_read (pid); + if (is_privileged && opt_unshare_user && opt_userns_block_fd == -1) { /* We're running as euid 0, but the uid we want to map is @@ -2167,7 +2493,10 @@ pid, TRUE, opt_needs_devpts); } - /* Initial launched process, wait for exec:ed command to exit */ + /* Initial launched process, wait for pid 1 or exec:ed command to exit */ + + if (opt_userns2_fd > 0 && setns (opt_userns2_fd, CLONE_NEWUSER) != 0) + die_with_error ("Setting userns2 failed"); /* We don't need any privileges in the launcher, drop them immediately. */ drop_privs (FALSE); @@ -2177,12 +2506,19 @@ if (opt_info_fd != -1) { - cleanup_free char *output = xasprintf ("{\n \"child-pid\": %i\n}\n", pid); - size_t len = strlen (output); - if (write (opt_info_fd, output, len) != len) - die_with_error ("Write to info_fd"); + cleanup_free char *output = xasprintf ("{\n \"child-pid\": %i", pid); + dump_info (opt_info_fd, output, TRUE); + namespace_ids_write (opt_info_fd, FALSE); + dump_info (opt_info_fd, "\n}\n", TRUE); close (opt_info_fd); } + if (opt_json_status_fd != -1) + { + cleanup_free char *output = xasprintf ("{ \"child-pid\": %i", pid); + dump_info (opt_json_status_fd, output, TRUE); + namespace_ids_write (opt_json_status_fd, TRUE); + dump_info (opt_json_status_fd, " }\n", TRUE); + } if (opt_userns_block_fd != -1) { @@ -2197,8 +2533,32 @@ /* Ignore res, if e.g. the child died and closed child_wait_fd we don't want to error out here */ close (child_wait_fd); - monitor_child (event_fd, pid); - exit (0); /* Should not be reached, but better safe... */ + return monitor_child (event_fd, pid, setup_finished_pipe[0]); + } + + if (opt_pidns_fd > 0) + { + if (setns (opt_pidns_fd, CLONE_NEWPID) != 0) + die_with_error ("Setting pidns failed"); + + /* fork to get the passed in pid ns */ + fork_intermediate_child (); + + /* We might both have specified an --pidns *and* --unshare-pid, so set up a new child pid namespace under the specified one */ + if (opt_unshare_pid) + { + if (unshare (CLONE_NEWPID)) + die_with_error ("unshare pid ns"); + + /* fork to get the new pid ns */ + fork_intermediate_child (); + } + + /* We're back, either in a child or grandchild, so message the actual pid to the monitor */ + + close (intermediate_pids_sockets[0]); + send_pid_on_socket (intermediate_pids_sockets[1]); + close (intermediate_pids_sockets[1]); } /* Child, in sandbox, privileged in the parent or in the user namespace (if --unshare-user). @@ -2218,6 +2578,9 @@ if (opt_info_fd != -1) close (opt_info_fd); + if (opt_json_status_fd != -1) + close (opt_json_status_fd); + /* Wait for the parent to init uid/gid maps and drop caps */ res = read (child_wait_fd, &val, 8); close (child_wait_fd); @@ -2265,7 +2628,7 @@ die_with_error ("Failed to make / slave"); /* Create a tmpfs which we will use as / in the namespace */ - if (mount ("", base_path, "tmpfs", MS_NODEV | MS_NOSUID, NULL) != 0) + if (mount ("tmpfs", base_path, "tmpfs", MS_NODEV | MS_NOSUID, NULL) != 0) die_with_error ("Failed to mount tmpfs"); old_cwd = get_current_dir_name (); @@ -2278,11 +2641,15 @@ /* We create a subdir "$base_path/newroot" for the new root, that * way we can pivot_root to base_path, and put the old root at * "$base_path/oldroot". This avoids problems accessing the oldroot - * dir if the user requested to bind mount something over / */ + * dir if the user requested to bind mount something over / (or + * over /tmp, now that we use that for base_path). */ if (mkdir ("newroot", 0755)) die_with_error ("Creating newroot failed"); + if (mount ("newroot", "newroot", NULL, MS_MGC_VAL | MS_BIND | MS_REC, NULL) < 0) + die_with_error ("setting up newroot bind"); + if (mkdir ("oldroot", 0755)) die_with_error ("Creating oldroot failed"); @@ -2351,6 +2718,40 @@ if (umount2 ("oldroot", MNT_DETACH)) die_with_error ("unmount old root"); + /* This is our second pivot. It's like we're a Silicon Valley startup flush + * with cash but short on ideas! + * + * We're aiming to make /newroot the real root, and get rid of /oldroot. To do + * that we need a temporary place to store it before we can unmount it. + */ + { cleanup_fd int oldrootfd = open ("/", O_DIRECTORY | O_RDONLY); + if (oldrootfd < 0) + die_with_error ("can't open /"); + if (chdir ("/newroot") != 0) + die_with_error ("chdir /newroot"); + /* While the documentation claims that put_old must be underneath + * new_root, it is perfectly fine to use the same directory as the + * kernel checks only if old_root is accessible from new_root. + * + * Both runc and LXC are using this "alternative" method for + * setting up the root of the container: + * + * https://github.com/opencontainers/runc/blob/master/libcontainer/rootfs_linux.go#L671 + * https://github.com/lxc/lxc/blob/master/src/lxc/conf.c#L1121 + */ + if (pivot_root (".", ".") != 0) + die_with_error ("pivot_root(/newroot)"); + if (fchdir (oldrootfd) < 0) + die_with_error ("fchdir to oldroot"); + if (umount2 (".", MNT_DETACH) < 0) + die_with_error ("umount old root"); + if (chdir ("/") != 0) + die_with_error ("chdir /"); + } + + if (opt_userns2_fd > 0 && setns (opt_userns2_fd, CLONE_NEWUSER) != 0) + die_with_error ("Setting userns2 failed"); + if (opt_unshare_user && (ns_uid != opt_sandbox_uid || ns_gid != opt_sandbox_gid) && opt_userns_block_fd == -1) @@ -2367,14 +2768,6 @@ -1, FALSE, FALSE); } - /* Now make /newroot the real root */ - if (chdir ("/newroot") != 0) - die_with_error ("chdir newroot"); - if (chroot ("/newroot") != 0) - die_with_error ("chroot /newroot"); - if (chdir ("/") != 0) - die_with_error ("chdir /"); - /* All privileged ops are done now, so drop caps we don't need */ drop_privs (!is_privileged); @@ -2498,8 +2891,27 @@ prctl (PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &seccomp_prog) != 0) die_with_error ("prctl(PR_SET_SECCOMP)"); + if (setup_finished_pipe[1] != -1) + { + char data = 0; + res = write_to_fd (setup_finished_pipe[1], &data, 1); + /* Ignore res, if e.g. the parent died and closed setup_finished_pipe[0] + we don't want to error out here */ + } + if (execvp (argv[0], argv) == -1) - die_with_error ("execvp %s", argv[0]); + { + if (setup_finished_pipe[1] != -1) + { + int saved_errno = errno; + char data = 0; + res = write_to_fd (setup_finished_pipe[1], &data, 1); + errno = saved_errno; + /* Ignore res, if e.g. the parent died and closed setup_finished_pipe[0] + we don't want to error out here */ + } + die_with_error ("execvp %s", argv[0]); + } return 0; } diff -Nru bubblewrap-0.2.1/build-aux/compile bubblewrap-0.4.0/build-aux/compile --- bubblewrap-0.2.1/build-aux/compile 2018-03-14 14:27:20.000000000 +0000 +++ bubblewrap-0.4.0/build-aux/compile 2019-11-27 08:47:09.000000000 +0000 @@ -1,9 +1,9 @@ #! /bin/sh # Wrapper for compilers which do not understand '-c -o'. -scriptversion=2012-10-14.11; # UTC +scriptversion=2018-03-07.03; # UTC -# Copyright (C) 1999-2014 Free Software Foundation, Inc. +# Copyright (C) 1999-2018 Free Software Foundation, Inc. # Written by Tom Tromey . # # This program is free software; you can redistribute it and/or modify @@ -17,7 +17,7 @@ # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a @@ -255,7 +255,8 @@ echo "compile $scriptversion" exit $? ;; - cl | *[/\\]cl | cl.exe | *[/\\]cl.exe ) + cl | *[/\\]cl | cl.exe | *[/\\]cl.exe | \ + icl | *[/\\]icl | icl.exe | *[/\\]icl.exe ) func_cl_wrapper "$@" # Doesn't return... ;; esac @@ -339,9 +340,9 @@ # Local Variables: # mode: shell-script # sh-indentation: 2 -# eval: (add-hook 'write-file-hooks 'time-stamp) +# eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" -# time-stamp-time-zone: "UTC" +# time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: diff -Nru bubblewrap-0.2.1/build-aux/depcomp bubblewrap-0.4.0/build-aux/depcomp --- bubblewrap-0.2.1/build-aux/depcomp 2018-03-14 14:27:20.000000000 +0000 +++ bubblewrap-0.4.0/build-aux/depcomp 2019-11-27 08:47:09.000000000 +0000 @@ -1,9 +1,9 @@ #! /bin/sh # depcomp - compile a program generating dependencies as side-effects -scriptversion=2013-05-30.07; # UTC +scriptversion=2018-03-07.03; # UTC -# Copyright (C) 1999-2014 Free Software Foundation, Inc. +# Copyright (C) 1999-2018 Free Software Foundation, Inc. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -16,7 +16,7 @@ # GNU General Public License for more details. # You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a @@ -783,9 +783,9 @@ # Local Variables: # mode: shell-script # sh-indentation: 2 -# eval: (add-hook 'write-file-hooks 'time-stamp) +# eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" -# time-stamp-time-zone: "UTC" +# time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: diff -Nru bubblewrap-0.2.1/build-aux/install-sh bubblewrap-0.4.0/build-aux/install-sh --- bubblewrap-0.2.1/build-aux/install-sh 2018-03-14 14:27:20.000000000 +0000 +++ bubblewrap-0.4.0/build-aux/install-sh 2019-11-27 08:47:09.000000000 +0000 @@ -1,7 +1,7 @@ #!/bin/sh # install - install a program, script, or datafile -scriptversion=2013-12-25.23; # UTC +scriptversion=2018-03-11.20; # UTC # This originates from X11R5 (mit/util/scripts/install.sh), which was # later released in X11R6 (xc/config/util/install.sh) with the @@ -271,15 +271,18 @@ fi dst=$dst_arg - # If destination is a directory, append the input filename; won't work - # if double slashes aren't ignored. + # If destination is a directory, append the input filename. if test -d "$dst"; then if test "$is_target_a_directory" = never; then echo "$0: $dst_arg: Is a directory" >&2 exit 1 fi dstdir=$dst - dst=$dstdir/`basename "$src"` + dstbase=`basename "$src"` + case $dst in + */) dst=$dst$dstbase;; + *) dst=$dst/$dstbase;; + esac dstdir_status=0 else dstdir=`dirname "$dst"` @@ -288,6 +291,11 @@ fi fi + case $dstdir in + */) dstdirslash=$dstdir;; + *) dstdirslash=$dstdir/;; + esac + obsolete_mkdir_used=false if test $dstdir_status != 0; then @@ -324,34 +332,43 @@ # is incompatible with FreeBSD 'install' when (umask & 300) != 0. ;; *) + # Note that $RANDOM variable is not portable (e.g. dash); Use it + # here however when possible just to lower collision chance. tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ - trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 + trap 'ret=$?; rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null; exit $ret' 0 + + # Because "mkdir -p" follows existing symlinks and we likely work + # directly in world-writeable /tmp, make sure that the '$tmpdir' + # directory is successfully created first before we actually test + # 'mkdir -p' feature. if (umask $mkdir_umask && - exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 + $mkdirprog $mkdir_mode "$tmpdir" && + exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1 then if test -z "$dir_arg" || { # Check for POSIX incompatibilities with -m. # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or # other-writable bit of parent directory when it shouldn't. # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. - ls_ld_tmpdir=`ls -ld "$tmpdir"` + test_tmpdir="$tmpdir/a" + ls_ld_tmpdir=`ls -ld "$test_tmpdir"` case $ls_ld_tmpdir in d????-?r-*) different_mode=700;; d????-?--*) different_mode=755;; *) false;; esac && - $mkdirprog -m$different_mode -p -- "$tmpdir" && { - ls_ld_tmpdir_1=`ls -ld "$tmpdir"` + $mkdirprog -m$different_mode -p -- "$test_tmpdir" && { + ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"` test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" } } then posix_mkdir=: fi - rmdir "$tmpdir/d" "$tmpdir" + rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" else # Remove any dirs left behind by ancient mkdir implementations. - rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null + rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null fi trap '' 0;; esac;; @@ -427,8 +444,8 @@ else # Make a couple of temp file names in the proper directory. - dsttmp=$dstdir/_inst.$$_ - rmtmp=$dstdir/_rm.$$_ + dsttmp=${dstdirslash}_inst.$$_ + rmtmp=${dstdirslash}_rm.$$_ # Trap to clean up those temp files at exit. trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 @@ -493,9 +510,9 @@ done # Local variables: -# eval: (add-hook 'write-file-hooks 'time-stamp) +# eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" -# time-stamp-time-zone: "UTC" +# time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: diff -Nru bubblewrap-0.2.1/build-aux/missing bubblewrap-0.4.0/build-aux/missing --- bubblewrap-0.2.1/build-aux/missing 2018-03-14 14:27:20.000000000 +0000 +++ bubblewrap-0.4.0/build-aux/missing 2019-11-27 08:47:09.000000000 +0000 @@ -1,9 +1,9 @@ #! /bin/sh # Common wrapper for a few potentially missing GNU programs. -scriptversion=2013-10-28.13; # UTC +scriptversion=2018-03-07.03; # UTC -# Copyright (C) 1996-2014 Free Software Foundation, Inc. +# Copyright (C) 1996-2018 Free Software Foundation, Inc. # Originally written by Fran,cois Pinard , 1996. # This program is free software; you can redistribute it and/or modify @@ -17,7 +17,7 @@ # GNU General Public License for more details. # You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a @@ -101,9 +101,9 @@ exit $st fi -perl_URL=http://www.perl.org/ -flex_URL=http://flex.sourceforge.net/ -gnu_software_URL=http://www.gnu.org/software +perl_URL=https://www.perl.org/ +flex_URL=https://github.com/westes/flex +gnu_software_URL=https://www.gnu.org/software program_details () { @@ -207,9 +207,9 @@ exit $st # Local variables: -# eval: (add-hook 'write-file-hooks 'time-stamp) +# eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" -# time-stamp-time-zone: "UTC" +# time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: diff -Nru bubblewrap-0.2.1/build-aux/tap-driver.sh bubblewrap-0.4.0/build-aux/tap-driver.sh --- bubblewrap-0.2.1/build-aux/tap-driver.sh 2018-03-14 14:27:20.000000000 +0000 +++ bubblewrap-0.4.0/build-aux/tap-driver.sh 2019-11-27 08:47:09.000000000 +0000 @@ -1,5 +1,5 @@ #! /bin/sh -# Copyright (C) 2011-2014 Free Software Foundation, Inc. +# Copyright (C) 2011-2018 Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -12,7 +12,7 @@ # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a @@ -132,7 +132,7 @@ # last `echo $?' statement), and would thus die reporting an internal # error. # For more information, see the Autoconf manual and the threads: - # + # # trap : 1 3 2 13 15 if test $merge -gt 0; then @@ -643,9 +643,9 @@ # Local Variables: # mode: shell-script # sh-indentation: 2 -# eval: (add-hook 'write-file-hooks 'time-stamp) +# eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" -# time-stamp-time-zone: "UTC" +# time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: diff -Nru bubblewrap-0.2.1/build-aux/test-driver bubblewrap-0.4.0/build-aux/test-driver --- bubblewrap-0.2.1/build-aux/test-driver 2018-03-14 14:27:20.000000000 +0000 +++ bubblewrap-0.4.0/build-aux/test-driver 2019-11-27 08:47:09.000000000 +0000 @@ -1,9 +1,9 @@ #! /bin/sh # test-driver - basic testsuite driver script. -scriptversion=2013-07-13.22; # UTC +scriptversion=2018-03-07.03; # UTC -# Copyright (C) 2011-2014 Free Software Foundation, Inc. +# Copyright (C) 2011-2018 Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -16,7 +16,7 @@ # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a @@ -140,9 +140,9 @@ # Local Variables: # mode: shell-script # sh-indentation: 2 -# eval: (add-hook 'write-file-hooks 'time-stamp) +# eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" -# time-stamp-time-zone: "UTC" +# time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: diff -Nru bubblewrap-0.2.1/bwrap.xml bubblewrap-0.4.0/bwrap.xml --- bubblewrap-0.2.1/bwrap.xml 2017-09-29 08:47:01.000000000 +0000 +++ bubblewrap-0.4.0/bwrap.xml 2019-11-27 12:34:31.000000000 +0000 @@ -48,7 +48,7 @@ It works by creating a new, completely empty, filesystem namespace where the root is on a tmpfs that is invisible from the host, and which will be automatically - cleaned up when the last process exists. You can then use commandline options to + cleaned up when the last process exits. You can then use commandline options to construct the root filesystem and process environment for the command to run in the namespace. @@ -131,6 +131,21 @@ Unshare all possible namespaces. Currently equivalent with: + + Use an existing user namespace instead of creating a new one. The namespace must fulfil the permission requirements for setns(), which generally means that it must be a decendant of the currently active user namespace, owned by the same user. + This is incompatible with --unshare-user, and doesn't work in the setuid version of bubblewrap. + + + + After setting up the new namespace, switch into the specified namespace. For this to work the specified namespace must be a decendant 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. + + + + Use an existing pid namespace instead of creating one. This is often used with --userns, because the pid namespace must be owned by the same user namespace that bwrap uses. + Note that this can be combined with --unshare-pid, and in that case it means that the sandbox will be in its own pid namespace, which is a child of the passed in one. + + Use a custom user id in the sandbox (requires ) @@ -184,14 +199,26 @@ Bind mount the host path SRC on DEST + + Equal to but ignores non-existent SRC + + Bind mount the host path SRC on DEST, allowing device access + + Equal to but ignores non-existent SRC + + Bind mount the host path SRC readonly on DEST + + Equal to but ignores non-existent SRC + + Remount the path DEST as readonly. It works only on the specified mount point, without changing any other mount point under the specified path @@ -332,7 +359,7 @@ HOME - Used as the cwd in the sandbox if has not been + Used as the cwd in the sandbox if has not been explicitly specified and the current cwd is not present inside the sandbox. The option can be used to override the value that is used here. diff -Nru bubblewrap-0.2.1/configure bubblewrap-0.4.0/configure --- bubblewrap-0.2.1/configure 2018-04-06 14:55:06.000000000 +0000 +++ bubblewrap-0.4.0/configure 2019-11-27 12:53:16.000000000 +0000 @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for bubblewrap 0.2.1. +# Generated by GNU Autoconf 2.69 for bubblewrap 0.4.0. # # Report bugs to . # @@ -580,8 +580,8 @@ # Identity of this package. PACKAGE_NAME='bubblewrap' PACKAGE_TARNAME='bubblewrap' -PACKAGE_VERSION='0.2.1' -PACKAGE_STRING='bubblewrap 0.2.1' +PACKAGE_VERSION='0.4.0' +PACKAGE_STRING='bubblewrap 0.4.0' PACKAGE_BUGREPORT='atomic-devel@projectatomic.io' PACKAGE_URL='' @@ -658,7 +658,6 @@ AMDEPBACKSLASH AMDEP_FALSE AMDEP_TRUE -am__quote am__include DEPDIR am__untar @@ -731,7 +730,8 @@ PACKAGE_TARNAME PACKAGE_NAME PATH_SEPARATOR -SHELL' +SHELL +am__quote' ac_subst_files='' ac_user_opts=' enable_option_checking @@ -1302,7 +1302,7 @@ # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures bubblewrap 0.2.1 to adapt to many kinds of systems. +\`configure' configures bubblewrap 0.4.0 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1368,7 +1368,7 @@ if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of bubblewrap 0.2.1:";; + short | recursive ) echo "Configuration of bubblewrap 0.4.0:";; esac cat <<\_ACEOF @@ -1492,7 +1492,7 @@ test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -bubblewrap configure 0.2.1 +bubblewrap configure 0.4.0 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -1794,7 +1794,7 @@ This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by bubblewrap $as_me 0.2.1, which was +It was created by bubblewrap $as_me 0.4.0, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -3486,7 +3486,7 @@ -am__api_version='1.15' +am__api_version='1.16' # Find a good install program. We prefer a C program (faster), # so one script is as good as another. But avoid the broken or @@ -3912,45 +3912,45 @@ ac_config_commands="$ac_config_commands depfiles" - -am_make=${MAKE-make} -cat > confinc << 'END' +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} supports the include directive" >&5 +$as_echo_n "checking whether ${MAKE-make} supports the include directive... " >&6; } +cat > confinc.mk << 'END' am__doit: - @echo this is the am__doit target + @echo this is the am__doit target >confinc.out .PHONY: am__doit END -# If we don't find an include directive, just comment out the code. -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for style of include used by $am_make" >&5 -$as_echo_n "checking for style of include used by $am_make... " >&6; } am__include="#" am__quote= -_am_result=none -# First try GNU make style include. -echo "include confinc" > confmf -# Ignore all kinds of additional output from 'make'. -case `$am_make -s -f confmf 2> /dev/null` in #( -*the\ am__doit\ target*) - am__include=include - am__quote= - _am_result=GNU - ;; -esac -# Now try BSD make style include. -if test "$am__include" = "#"; then - echo '.include "confinc"' > confmf - case `$am_make -s -f confmf 2> /dev/null` in #( - *the\ am__doit\ target*) - am__include=.include - am__quote="\"" - _am_result=BSD +# BSD make does it like this. +echo '.include "confinc.mk" # ignored' > confmf.BSD +# Other make implementations (GNU, Solaris 10, AIX) do it like this. +echo 'include confinc.mk # ignored' > confmf.GNU +_am_result=no +for s in GNU BSD; do + { echo "$as_me:$LINENO: ${MAKE-make} -f confmf.$s && cat confinc.out" >&5 + (${MAKE-make} -f confmf.$s && cat confinc.out) >&5 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + case $?:`cat confinc.out 2>/dev/null` in #( + '0:this is the am__doit target') : + case $s in #( + BSD) : + am__include='.include' am__quote='"' ;; #( + *) : + am__include='include' am__quote='' ;; +esac ;; #( + *) : ;; - esac -fi - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $_am_result" >&5 -$as_echo "$_am_result" >&6; } -rm -f confinc confmf +esac + if test "$am__include" != "#"; then + _am_result="yes ($s style)" + break + fi +done +rm -f confinc.* confmf.* +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ${_am_result}" >&5 +$as_echo "${_am_result}" >&6; } # Check whether --enable-dependency-tracking was given. if test "${enable_dependency_tracking+set}" = set; then : @@ -4032,7 +4032,7 @@ # Define the identity of the package. PACKAGE='bubblewrap' - VERSION='0.2.1' + VERSION='0.4.0' # Some tools Automake needs. @@ -4053,8 +4053,8 @@ # For better backward compatibility. To be removed once Automake 1.9.x # dies out for good. For more background, see: -# -# +# +# mkdir_p='$(MKDIR_P)' # We need awk for the "check" target (and possibly the TAP driver). The @@ -4349,7 +4349,7 @@ Aborting the configuration process, to ensure you take notice of the issue. You can download and install GNU coreutils to get an 'rm' implementation -that behaves properly: . +that behaves properly: . If you want to complete the configuration process using your problematic 'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM @@ -6365,7 +6365,7 @@ # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by bubblewrap $as_me 0.2.1, which was +This file was extended by bubblewrap $as_me 0.4.0, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -6431,7 +6431,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -bubblewrap config.status 0.2.1 +bubblewrap config.status 0.4.0 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" @@ -6550,7 +6550,7 @@ # # INIT-COMMANDS # -AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir" +AMDEP_TRUE="$AMDEP_TRUE" MAKE="${MAKE-make}" _ACEOF @@ -7162,29 +7162,35 @@ # Older Autoconf quotes --file arguments for eval, but not when files # are listed without --file. Let's play safe and only enable the eval # if we detect the quoting. - case $CONFIG_FILES in - *\'*) eval set x "$CONFIG_FILES" ;; - *) set x $CONFIG_FILES ;; - esac + # TODO: see whether this extra hack can be removed once we start + # requiring Autoconf 2.70 or later. + case $CONFIG_FILES in #( + *\'*) : + eval set x "$CONFIG_FILES" ;; #( + *) : + set x $CONFIG_FILES ;; #( + *) : + ;; +esac shift - for mf + # Used to flag and report bootstrapping failures. + am_rc=0 + for am_mf do # Strip MF so we end up with the name of the file. - mf=`echo "$mf" | sed -e 's/:.*$//'` - # Check whether this is an Automake generated Makefile or not. - # We used to match only the files named 'Makefile.in', but - # some people rename them; so instead we look at the file content. - # Grep'ing the first line is not enough: some people post-process - # each Makefile.in and add a new line on top of each file to say so. - # Grep'ing the whole file is not good either: AIX grep has a line + am_mf=`$as_echo "$am_mf" | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile which includes + # dependency-tracking related rules and includes. + # Grep'ing the whole file directly is not great: AIX grep has a line # limit of 2048, but all sed's we know have understand at least 4000. - if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then - dirpart=`$as_dirname -- "$mf" || -$as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$mf" : 'X\(//\)[^/]' \| \ - X"$mf" : 'X\(//\)$' \| \ - X"$mf" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X"$mf" | + sed -n 's,^am--depfiles:.*,X,p' "$am_mf" | grep X >/dev/null 2>&1 \ + || continue + am_dirpart=`$as_dirname -- "$am_mf" || +$as_expr X"$am_mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$am_mf" : 'X\(//\)[^/]' \| \ + X"$am_mf" : 'X\(//\)$' \| \ + X"$am_mf" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$am_mf" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q @@ -7202,53 +7208,48 @@ q } s/.*/./; q'` - else - continue - fi - # Extract the definition of DEPDIR, am__include, and am__quote - # from the Makefile without running 'make'. - DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` - test -z "$DEPDIR" && continue - am__include=`sed -n 's/^am__include = //p' < "$mf"` - test -z "$am__include" && continue - am__quote=`sed -n 's/^am__quote = //p' < "$mf"` - # Find all dependency output files, they are included files with - # $(DEPDIR) in their names. We invoke sed twice because it is the - # simplest approach to changing $(DEPDIR) to its actual value in the - # expansion. - for file in `sed -n " - s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ - sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do - # Make sure the directory exists. - test -f "$dirpart/$file" && continue - fdir=`$as_dirname -- "$file" || -$as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$file" : 'X\(//\)[^/]' \| \ - X"$file" : 'X\(//\)$' \| \ - X"$file" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X"$file" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ + am_filepart=`$as_basename -- "$am_mf" || +$as_expr X/"$am_mf" : '.*/\([^/][^/]*\)/*$' \| \ + X"$am_mf" : 'X\(//\)$' \| \ + X"$am_mf" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$am_mf" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } - /^X\(\/\/\)$/{ + /^X\/\(\/\/\)$/{ s//\1/ q } - /^X\(\/\).*/{ + /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` - as_dir=$dirpart/$fdir; as_fn_mkdir_p - # echo "creating $dirpart/$file" - echo '# dummy' > "$dirpart/$file" - done + { echo "$as_me:$LINENO: cd "$am_dirpart" \ + && sed -e '/# am--include-marker/d' "$am_filepart" \ + | $MAKE -f - am--depfiles" >&5 + (cd "$am_dirpart" \ + && sed -e '/# am--include-marker/d' "$am_filepart" \ + | $MAKE -f - am--depfiles) >&5 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } || am_rc=$? done + if test $am_rc -ne 0; then + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "Something went wrong bootstrapping makefile fragments + for automatic dependency tracking. Try re-running configure with the + '--disable-dependency-tracking' option to at least be able to build + the package (albeit without support for automatic dependency tracking). +See \`config.log' for more details" "$LINENO" 5; } + fi + { am_dirpart=; unset am_dirpart;} + { am_filepart=; unset am_filepart;} + { am_mf=; unset am_mf;} + { am_rc=; unset am_rc;} + rm -f conftest-deps.mk } ;; diff -Nru bubblewrap-0.2.1/configure.ac bubblewrap-0.4.0/configure.ac --- bubblewrap-0.2.1/configure.ac 2018-04-06 14:54:57.000000000 +0000 +++ bubblewrap-0.4.0/configure.ac 2019-11-27 12:40:58.000000000 +0000 @@ -1,5 +1,5 @@ AC_PREREQ([2.63]) -AC_INIT([bubblewrap], [0.2.1], [atomic-devel@projectatomic.io]) +AC_INIT([bubblewrap], [0.4.0], [atomic-devel@projectatomic.io]) AC_CONFIG_HEADER([config.h]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_AUX_DIR([build-aux]) diff -Nru bubblewrap-0.2.1/debian/changelog bubblewrap-0.4.0/debian/changelog --- bubblewrap-0.2.1/debian/changelog 2018-04-08 14:42:03.000000000 +0000 +++ bubblewrap-0.4.0/debian/changelog 2021-03-02 05:29:38.000000000 +0000 @@ -1,3 +1,137 @@ +bubblewrap (0.4.0-1ubuntu4~18.04.sav0) bionic; urgency=medium + + * Backport to Bionic + * debian/control: Set debhelper-compat (= 11) BD + + -- Rob Savoury Mon, 01 Mar 2021 21:29:38 -0800 + +bubblewrap (0.4.0-1ubuntu4) focal; urgency=medium + + * SECURITY UPDATE: privilege escalation when used in setuid mode + - debian/patches/CVE-2020-5291.patch: don't rely on geteuid() to know + when to switch back from setuid root in bubblewrap.c. + - CVE-2020-5291 + + -- Marc Deslauriers Wed, 01 Apr 2020 08:25:00 -0400 + +bubblewrap (0.4.0-1ubuntu3) focal; urgency=medium + + * d/p/update-output-patterns-libcap-2.29.patch: cherry-pick fix proposed + fix to capability drop-related tests, which broke with newer libcap2. + + -- Łukasz 'sil2100' Zemczak Wed, 26 Feb 2020 21:39:11 +0100 + +bubblewrap (0.4.0-1ubuntu2) focal; urgency=medium + + * No-change rebuild with fixed binutils on arm64. + + -- Matthias Klose Sat, 08 Feb 2020 10:56:27 +0000 + +bubblewrap (0.4.0-1ubuntu1) focal; urgency=medium + + * Make autopkgtests cross-test-friendly. + + -- Steve Langasek Fri, 10 Jan 2020 10:31:19 -0800 + +bubblewrap (0.4.0-1) unstable; urgency=medium + + * New upstream release + * Use debhelper-compat 12 + * Standards-Version: 4.4.1 (no changes required) + + -- Simon McVittie Thu, 28 Nov 2019 11:14:41 +0000 + +bubblewrap (0.3.3-2) unstable; urgency=medium + + * Release to unstable + * d/salsa-ci.yml: Request standard CI on salsa.debian.org + * d/rules: Disable any active LD_PRELOAD hacks while running tests. + These will typically assume a fully-featured OS (for example faketime + assumes sem_open() will work), but bubblewrap is a low-level tool + that temporarily operates in a container that is only partially + functional (for example /dev/shm isn't always mounted). + * Standards-Version: 4.4.0 (no changes required) + + -- Simon McVittie Tue, 09 Jul 2019 09:34:53 +0100 + +bubblewrap (0.3.3-1) experimental; urgency=medium + + * New upstream release + - Drop all patches except + d/p/debian/Use-Python-3-for-test-demo-code.patch, merged upstream + + -- Simon McVittie Sun, 05 May 2019 10:36:48 +0100 + +bubblewrap (0.3.1-4) unstable; urgency=medium + + * d/p/Don-t-create-our-own-temporary-mount-point-for-pivot_root.patch: + Replace with the version that was applied upstream + * d/p/tests-Ensure-that-tmpfs-with-oldroot-newroot-doesn-t-appe.patch: + Add a test to check that the above patch works as intended + + -- Simon McVittie Wed, 06 Mar 2019 14:43:44 +0000 + +bubblewrap (0.3.1-3) unstable; urgency=medium + + * d/p/Don-t-create-our-own-temporary-mount-point-for-pivot_root.patch: + Avoid denial of service and potential symlink attacks on systems not + using systemd-logind (Closes: #923557) + * Standards-Version: 4.3.0 (no changes required) + * d/upstream/metadata: Add DEP-12 metadata + + -- Simon McVittie Sat, 02 Mar 2019 13:03:29 +0000 + +bubblewrap (0.3.1-2) unstable; urgency=medium + + [ Iain Lane ] + * d/tests/basic: Don't assume `id` will be the same inside the sandbox, + making this test pass on (Ubuntu) systems where bubblewrap is not + setuid (Closes: #910006) + * d/tests/upstream-usrmerge: Add a test to ensure that bubblewrap + works on a /usr-merged system + + [ Simon McVittie ] + * d/p/tests-Handle-systems-without-merged-usr.patch: + Add patch from upstream git to make tests pass on non-merged-/usr + systems where bubblewrap is not setuid. Thanks to Iain Lane. + * d/p/man-page-Describe-chdir-not-nonexistent-cwd.patch: + Add patch from upstream git to fix documentation of --chdir option + * d/p/Make-lockdata-long-enough-on-32-bit-with-64-bit-file-poin.patch: + Add patch from upstream git to fix lock handling in tests on 32-bit + platforms with 64-bit off_t. Thanks to Timothy E Baldwin. + + -- Simon McVittie Wed, 03 Oct 2018 15:23:27 +0100 + +bubblewrap (0.3.1-1) unstable; urgency=medium + + [ Simon McVittie ] + * Standards-Version: 4.2.1 (no changes required) + * New upstream release + + [ Iain Lane ] + * Don't install setuid on Ubuntu and derivatives. + Ubuntu's kernel enables unprivileged user namespaces, so we don't + need to install bwrap setuid there. + + -- Simon McVittie Thu, 27 Sep 2018 20:30:53 +0100 + +bubblewrap (0.3.0-1) unstable; urgency=medium + + * New upstream release + * Upload to unstable + - d/gbp.conf: Switch back to debian/master + * Standards-Version: 4.1.5 (no changes required) + + -- Simon McVittie Thu, 12 Jul 2018 10:03:38 +0100 + +bubblewrap (0.2.1+5+g5991dab-1) experimental; urgency=medium + + * d/watch: Strip +N+gHHHHHHH snapshot markers from version + * d/gbp.conf: Use debian/experimental branch + * New upstream git snapshot + + -- Simon McVittie Thu, 07 Jun 2018 13:04:18 +0100 + bubblewrap (0.2.1-1) unstable; urgency=medium * New upstream release diff -Nru bubblewrap-0.2.1/debian/compat bubblewrap-0.4.0/debian/compat --- bubblewrap-0.2.1/debian/compat 2018-04-08 14:42:03.000000000 +0000 +++ bubblewrap-0.4.0/debian/compat 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -10 diff -Nru bubblewrap-0.2.1/debian/control bubblewrap-0.4.0/debian/control --- bubblewrap-0.2.1/debian/control 2018-04-08 14:42:03.000000000 +0000 +++ bubblewrap-0.4.0/debian/control 2021-03-02 05:29:38.000000000 +0000 @@ -1,14 +1,15 @@ Source: bubblewrap Section: admin Priority: optional -Maintainer: Utopia Maintenance Team +Maintainer: Ubuntu Developers +XSBC-Original-Maintainer: Utopia Maintenance Team Uploaders: Laszlo Boszormenyi (GCS) , Simon McVittie , Build-Depends: automake (>= 1.14.1), bash-completion, - debhelper (>= 10), + debhelper-compat (= 11), docbook-xml, docbook-xsl, libcap-dev, @@ -16,7 +17,7 @@ pkg-config, python3 , xsltproc, -Standards-Version: 4.1.4 +Standards-Version: 4.4.1 Homepage: https://github.com/projectatomic/bubblewrap Vcs-Git: https://salsa.debian.org/debian/bubblewrap.git Vcs-Browser: https://salsa.debian.org/debian/bubblewrap diff -Nru bubblewrap-0.2.1/debian/patches/CVE-2020-5291.patch bubblewrap-0.4.0/debian/patches/CVE-2020-5291.patch --- bubblewrap-0.2.1/debian/patches/CVE-2020-5291.patch 1970-01-01 00:00:00.000000000 +0000 +++ bubblewrap-0.4.0/debian/patches/CVE-2020-5291.patch 2020-04-01 12:24:59.000000000 +0000 @@ -0,0 +1,84 @@ +From 5404a15d34301a5a0dd5930203e03c76b80ebf21 Mon Sep 17 00:00:00 2001 +From: Alexander Larsson +Date: Thu, 26 Mar 2020 15:36:44 +0100 +Subject: [PATCH 1/3] Don't rely on geteuid() to know when to switch back from + setuid root +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +As pointed out by Stephen Röttger , in +drop_privs() we only drop root in the setuid case if geteuid() is +0. Typically geteuid() == 0 means we were setuid root and have not yet +switched away from it. + +However, it is possible to make the geteuid call fail by passing a +--userns2 namespace which doesn't have 0 mapped (i.e. where geteuid() +will return the owerflow uid instead). + +If you do this, the pid 1 process in the sandbox will continue running +as host uid 0, while dropping the dumpable flag, and at this point the +user can ptrace attach the process and have root permissions. + +We fix this by not relying on the geteuid() call to know when we need +to drop root uid, but rather keep track of whether we already switched +from it. +--- + bubblewrap.c | 12 +++++++----- + 1 file changed, 7 insertions(+), 5 deletions(-) + +--- a/bubblewrap.c ++++ b/bubblewrap.c +@@ -834,11 +834,13 @@ switch_to_user_with_privs (void) + + /* Call setuid() and use capset() to adjust capabilities */ + static void +-drop_privs (bool keep_requested_caps) ++drop_privs (bool keep_requested_caps, ++ bool already_changed_uid) + { + assert (!keep_requested_caps || !is_privileged); + /* Drop root uid */ +- if (geteuid () == 0 && setuid (opt_sandbox_uid) < 0) ++ if (is_privileged && !already_changed_uid && ++ setuid (opt_sandbox_uid) < 0) + die_with_error ("unable to drop root uid"); + + drop_all_caps (keep_requested_caps); +@@ -2296,6 +2298,9 @@ main (int argc, + if (opt_userns_fd != -1 && is_privileged) + die ("--userns doesn't work in setuid mode"); + ++ if (opt_userns2_fd != -1 && is_privileged) ++ die ("--userns2 doesn't work in setuid mode"); ++ + /* We have to do this if we weren't installed setuid (and we're not + * root), so let's just DWIM */ + if (!is_privileged && getuid () != 0 && opt_userns_fd == -1) +@@ -2499,7 +2504,7 @@ main (int argc, + die_with_error ("Setting userns2 failed"); + + /* We don't need any privileges in the launcher, drop them immediately. */ +- drop_privs (FALSE); ++ drop_privs (FALSE, FALSE); + + /* Optionally bind our lifecycle to that of the parent */ + handle_die_with_parent (); +@@ -2674,7 +2679,7 @@ main (int argc, + if (child == 0) + { + /* Unprivileged setup process */ +- drop_privs (FALSE); ++ drop_privs (FALSE, TRUE); + close (privsep_sockets[0]); + setup_newroot (opt_unshare_pid, privsep_sockets[1]); + exit (0); +@@ -2769,7 +2774,7 @@ main (int argc, + } + + /* All privileged ops are done now, so drop caps we don't need */ +- drop_privs (!is_privileged); ++ drop_privs (!is_privileged, TRUE); + + if (opt_block_fd != -1) + { diff -Nru bubblewrap-0.2.1/debian/patches/debian/Use-Python-3-for-test-demo-code.patch bubblewrap-0.4.0/debian/patches/debian/Use-Python-3-for-test-demo-code.patch --- bubblewrap-0.2.1/debian/patches/debian/Use-Python-3-for-test-demo-code.patch 2018-04-08 14:42:03.000000000 +0000 +++ bubblewrap-0.4.0/debian/patches/debian/Use-Python-3-for-test-demo-code.patch 2019-11-28 11:14:41.000000000 +0000 @@ -19,10 +19,10 @@ import os, select, subprocess, sys, json diff --git a/tests/test-run.sh b/tests/test-run.sh -index 0dae450..637776e 100755 +index a404c4e..1d2ffbc 100755 --- a/tests/test-run.sh +++ b/tests/test-run.sh -@@ -151,7 +151,7 @@ fi +@@ -215,7 +215,7 @@ fi # Test --die-with-parent cat >lockf-n.py < +Date: Wed, 19 Feb 2020 10:03:05 +0100 +Subject: [PATCH] tests: Update output patterns for libcap >= 2.29 + +--- + tests/test-run.sh | 15 +++++++++++---- + 1 file changed, 11 insertions(+), 4 deletions(-) + +diff --git a/tests/test-run.sh b/tests/test-run.sh +index a01f41c..702c480 100755 +--- a/tests/test-run.sh ++++ b/tests/test-run.sh +@@ -215,11 +215,18 @@ else + $RUN $OPT --cap-drop ALL --unshare-pid capsh --print >caps.test + assert_file_has_content caps.test 'Current: =$' + # Check for dropping kill/fowner (we assume all uid 0 callers have this) +- $RUN $OPT --cap-drop CAP_KILL --cap-drop CAP_FOWNER --unshare-pid capsh --print >caps.test +- assert_not_file_has_content caps.test '^Current: =.*cap_kill' +- assert_not_file_has_content caps.test '^Current: =.*cap_fowner' + # But we should still have net_bind_service for example +- assert_file_has_content caps.test '^Current: =.*cap_net_bind_service' ++ $RUN $OPT --cap-drop CAP_KILL --cap-drop CAP_FOWNER --unshare-pid capsh --print >caps.test ++ # capsh's output format changed from v2.29 -> drops are now indicated with -eip ++ if grep 'Current: =.*+eip$' caps.test; then ++ assert_not_file_has_content caps.test '^Current: =.*cap_kill.*+eip$' ++ assert_not_file_has_content caps.test '^Current: =.*cap_fowner.*+eip$' ++ assert_file_has_content caps.test '^Current: =.*cap_net_bind_service.*+eip$' ++ else ++ assert_file_has_content caps.test '^Current: =eip.*cap_kill.*-eip$' ++ assert_file_has_content caps.test '^Current: =eip.*cap_fowner.*-eip$' ++ assert_not_file_has_content caps.test '^Current: =.*cap_net_bind_service.*-eip$' ++ fi + echo "ok - we have the expected caps as uid 0" + fi + diff -Nru bubblewrap-0.2.1/debian/rules bubblewrap-0.4.0/debian/rules --- bubblewrap-0.2.1/debian/rules 2018-04-08 14:42:03.000000000 +0000 +++ bubblewrap-0.4.0/debian/rules 2019-11-28 11:14:41.000000000 +0000 @@ -13,7 +13,23 @@ override_dh_fixperms: chmod a+x $(PKGDIR)/usr/share/bash-completion/completions/bwrap +# Ubuntu enables unprivileged user namespaces; no need for bwrap to be suid +# there. +ifneq (yes,$(shell dpkg-vendor --derives-from Ubuntu && echo yes)) chmod 04755 $(PKGDIR)/usr/bin/bwrap dh_fixperms -Xbin/bwrap +else + dh_fixperms +endif .PHONY: override_dh_fixperms + +override_dh_auto_test: +ifeq (,$(filter nocheck,$(DEB_BUILD_OPTIONS))) + # Remove LD_PRELOAD so we don't run with faketime. It uses + # sem_open(), but bubblewrap runs in an environment where that + # can't work. + env -u LD_PRELOAD dh_auto_test +endif + +.PHONY: override_dh_auto_test diff -Nru bubblewrap-0.2.1/debian/salsa-ci.yml bubblewrap-0.4.0/debian/salsa-ci.yml --- bubblewrap-0.2.1/debian/salsa-ci.yml 1970-01-01 00:00:00.000000000 +0000 +++ bubblewrap-0.4.0/debian/salsa-ci.yml 2019-11-28 11:14:41.000000000 +0000 @@ -0,0 +1,3 @@ +include: + - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/salsa-ci.yml + - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/pipeline-jobs.yml diff -Nru bubblewrap-0.2.1/debian/tests/basic bubblewrap-0.4.0/debian/tests/basic --- bubblewrap-0.2.1/debian/tests/basic 2018-04-08 14:42:03.000000000 +0000 +++ bubblewrap-0.4.0/debian/tests/basic 2019-11-28 11:14:41.000000000 +0000 @@ -13,7 +13,9 @@ } my $out; -run_ok([qw(bwrap --ro-bind / / /usr/bin/id)], '<', \undef, '>', \$out); -is($out, `id`); +run_ok([qw(bwrap --ro-bind / / /usr/bin/id -u)], '<', \undef, '>', \$out); +is($out, `id -u`); +run_ok([qw(bwrap --ro-bind / / /usr/bin/id -g)], '<', \undef, '>', \$out); +is($out, `id -g`); done_testing; diff -Nru bubblewrap-0.2.1/debian/tests/control bubblewrap-0.4.0/debian/tests/control --- bubblewrap-0.2.1/debian/tests/control 2018-04-08 14:42:03.000000000 +0000 +++ bubblewrap-0.4.0/debian/tests/control 2020-01-10 18:31:02.000000000 +0000 @@ -7,19 +7,30 @@ Restrictions: allow-stderr, isolation-machine Depends: bubblewrap, - iproute2, - libcap2-bin, - libipc-run-perl, - perl, - python3, + iproute2:native, + libcap2-bin:native, + libipc-run-perl:native, + perl:native, + python3:native, + +Tests: upstream-usrmerge +Restrictions: allow-stderr, isolation-machine, breaks-testbed +Depends: + bubblewrap, + iproute2:native, + libcap2-bin:native, + libipc-run-perl:native, + perl:native, + python3:native, + usrmerge Tests: upstream-as-root Restrictions: allow-stderr, isolation-machine, needs-root Depends: bubblewrap, - iproute2, - libcap2-bin, - libipc-run-perl, - perl, - python3, + iproute2:native, + libcap2-bin:native, + libipc-run-perl:native, + perl:native, + python3:native, diff -Nru bubblewrap-0.2.1/debian/tests/upstream-usrmerge bubblewrap-0.4.0/debian/tests/upstream-usrmerge --- bubblewrap-0.2.1/debian/tests/upstream-usrmerge 1970-01-01 00:00:00.000000000 +0000 +++ bubblewrap-0.4.0/debian/tests/upstream-usrmerge 2019-11-28 11:14:41.000000000 +0000 @@ -0,0 +1,5 @@ +#!/bin/sh + +set -e + +exec tests/test-run.sh diff -Nru bubblewrap-0.2.1/debian/upstream/metadata bubblewrap-0.4.0/debian/upstream/metadata --- bubblewrap-0.2.1/debian/upstream/metadata 1970-01-01 00:00:00.000000000 +0000 +++ bubblewrap-0.4.0/debian/upstream/metadata 2019-11-28 11:14:41.000000000 +0000 @@ -0,0 +1,8 @@ +--- +Name: Bubblewrap +Repository: https://github.com/projectatomic/bubblewrap +Repository-Browse: https://github.com/projectatomic/bubblewrap +Bug-Database: https://github.com/projectatomic/bubblewrap/issues +Bug-Submit: https://github.com/projectatomic/bubblewrap/issues/new +... +# vim:set ft=yaml: diff -Nru bubblewrap-0.2.1/debian/watch bubblewrap-0.4.0/debian/watch --- bubblewrap-0.2.1/debian/watch 2018-04-08 14:42:03.000000000 +0000 +++ bubblewrap-0.4.0/debian/watch 2019-11-28 11:14:41.000000000 +0000 @@ -1,3 +1,3 @@ version=4 -opts="compression=xz,dversionmangle=s/\+git[0-9]*\+g[0-9a-f]*//" \ +opts="compression=xz,dversionmangle=s/\+(?:git)?[0-9]*\+g[0-9a-f]*//" \ https://github.com/projectatomic/@PACKAGE@/releases .*/@PACKAGE@-@ANY_VERSION@@ARCHIVE_EXT@ diff -Nru bubblewrap-0.2.1/Makefile.in bubblewrap-0.4.0/Makefile.in --- bubblewrap-0.2.1/Makefile.in 2018-04-06 14:55:06.000000000 +0000 +++ bubblewrap-0.4.0/Makefile.in 2019-11-27 12:53:16.000000000 +0000 @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.15 from Makefile.am. +# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2014 Free Software Foundation, Inc. +# Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -133,7 +133,10 @@ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp -am__depfiles_maybe = depfiles +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/bwrap-bind-mount.Po \ + ./$(DEPDIR)/bwrap-bubblewrap.Po ./$(DEPDIR)/bwrap-network.Po \ + ./$(DEPDIR)/bwrap-utils.Po am__mv = mv -f AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) @@ -580,8 +583,8 @@ echo ' $(SHELL) ./config.status'; \ $(SHELL) ./config.status;; \ *) \ - echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \ - cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__maybe_remake_depfiles);; \ esac; $(srcdir)/Makefile-bwrap.am $(srcdir)/Makefile-docs.am $(am__empty): @@ -664,10 +667,16 @@ distclean-compile: -rm -f *.tab.c -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bwrap-bind-mount.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bwrap-bubblewrap.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bwrap-network.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bwrap-utils.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bwrap-bind-mount.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bwrap-bubblewrap.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bwrap-network.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bwrap-utils.Po@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @@ -982,7 +991,7 @@ fi; \ $$success || exit 1 -check-TESTS: +check-TESTS: $(check_PROGRAMS) @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) @@ -1025,7 +1034,10 @@ @am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ @am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT) -distdir: $(DISTFILES) +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) $(am__remove_distdir) test -d "$(distdir)" || mkdir "$(distdir)" @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ @@ -1237,7 +1249,10 @@ distclean: distclean-am -rm -f $(am__CONFIG_DISTCLEAN_FILES) - -rm -rf ./$(DEPDIR) + -rm -f ./$(DEPDIR)/bwrap-bind-mount.Po + -rm -f ./$(DEPDIR)/bwrap-bubblewrap.Po + -rm -f ./$(DEPDIR)/bwrap-network.Po + -rm -f ./$(DEPDIR)/bwrap-utils.Po -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-hdr distclean-tags @@ -1286,7 +1301,10 @@ maintainer-clean: maintainer-clean-am -rm -f $(am__CONFIG_DISTCLEAN_FILES) -rm -rf $(top_srcdir)/autom4te.cache - -rm -rf ./$(DEPDIR) + -rm -f ./$(DEPDIR)/bwrap-bind-mount.Po + -rm -f ./$(DEPDIR)/bwrap-bubblewrap.Po + -rm -f ./$(DEPDIR)/bwrap-network.Po + -rm -f ./$(DEPDIR)/bwrap-utils.Po -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic @@ -1309,19 +1327,19 @@ .MAKE: all check-am install-am install-exec-am install-strip -.PHONY: CTAGS GTAGS TAGS all all-am am--refresh check check-TESTS \ - check-am clean clean-binPROGRAMS clean-checkPROGRAMS \ - clean-cscope clean-generic cscope cscopelist-am ctags ctags-am \ - dist dist-all dist-bzip2 dist-gzip dist-lzip dist-shar \ - dist-tarZ dist-xz dist-zip distcheck distclean \ - distclean-compile distclean-generic distclean-hdr \ - distclean-tags distcleancheck distdir distuninstallcheck dvi \ - dvi-am html html-am info info-am install install-am \ - install-binPROGRAMS install-data install-data-am \ - install-dist_bashcompletionDATA install-dvi install-dvi-am \ - install-exec install-exec-am install-exec-hook install-html \ - install-html-am install-info install-info-am install-man \ - install-man1 install-pdf install-pdf-am install-ps \ +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles am--refresh check \ + check-TESTS check-am clean clean-binPROGRAMS \ + clean-checkPROGRAMS clean-cscope clean-generic cscope \ + cscopelist-am ctags ctags-am dist dist-all dist-bzip2 \ + dist-gzip dist-lzip dist-shar dist-tarZ dist-xz dist-zip \ + distcheck distclean distclean-compile distclean-generic \ + distclean-hdr distclean-tags distcleancheck distdir \ + distuninstallcheck dvi dvi-am html html-am info info-am \ + install install-am install-binPROGRAMS install-data \ + install-data-am install-dist_bashcompletionDATA install-dvi \ + install-dvi-am install-exec install-exec-am install-exec-hook \ + install-html install-html-am install-info install-info-am \ + install-man install-man1 install-pdf install-pdf-am install-ps \ install-ps-am install-strip installcheck installcheck-am \ installdirs maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-compile mostlyclean-generic pdf pdf-am \ diff -Nru bubblewrap-0.2.1/README.md bubblewrap-0.4.0/README.md --- bubblewrap-0.2.1/README.md 2018-03-14 14:20:49.000000000 +0000 +++ bubblewrap-0.4.0/README.md 2019-11-19 13:38:57.000000000 +0000 @@ -139,7 +139,7 @@ Related project comparison: Sandstorm.io ---------------------------------------- -[Sandstorm.io](https://sandstorm.io/) requries unprivileged user +[Sandstorm.io](https://sandstorm.io/) requires unprivileged user namespaces to set up its sandbox, though it could easily be adapted to operate in a setuid mode as well. @cgwalters believes their code is fairly good, but it could still make sense to unify on bubblewrap. @@ -168,9 +168,13 @@ [binctr](https://github.com/jfrazelle/binctr) is just a wrapper for runC, so inherits all of its design tradeoffs. -Whats with the name ?! +What's with the name?! ---------------------- The name bubblewrap was chosen to convey that this tool runs as the parent of the application (so wraps it in some sense) and creates a protective layer (the sandbox) around it. + +![](bubblewrap.jpg) + +(Bubblewrap cat by [dancing_stupidity](https://www.flickr.com/photos/27549668@N03/)) diff -Nru bubblewrap-0.2.1/tests/libtest-core.sh bubblewrap-0.4.0/tests/libtest-core.sh --- bubblewrap-0.2.1/tests/libtest-core.sh 2017-09-29 08:47:01.000000000 +0000 +++ bubblewrap-0.4.0/tests/libtest-core.sh 2019-11-27 12:34:31.000000000 +0000 @@ -75,6 +75,18 @@ fatal "$@" } +_fatal_print_files() { + file1="$1" + shift + file2="$1" + shift + ls -al "$file1" >&2 + sed -e 's/^/# /' < "$file1" >&2 + ls -al "$file2" >&2 + sed -e 's/^/# /' < "$file2" >&2 + fatal "$@" +} + assert_not_has_file () { if test -f "$1"; then _fatal_print_file "$1" "File '$1' exists" @@ -135,8 +147,18 @@ fi } +assert_files_equal() { + if ! cmp "$1" "$2"; then + _fatal_print_files "$1" "$2" "File '$1' and '$2' is not equal" + fi +} + # Use to skip all of these tests skip() { echo "1..0 # SKIP" "$@" exit 0 } + +extract_child_pid() { + grep child-pid "$1" | sed "s/^.*: \([0-9]*\).*/\1/" +} diff -Nru bubblewrap-0.2.1/tests/test-run.sh bubblewrap-0.4.0/tests/test-run.sh --- bubblewrap-0.2.1/tests/test-run.sh 2018-03-14 14:20:49.000000000 +0000 +++ bubblewrap-0.4.0/tests/test-run.sh 2019-11-27 12:34:31.000000000 +0000 @@ -24,6 +24,9 @@ cd ${tempdir} : "${BWRAP:=bwrap}" +if test -u "$(type -p ${BWRAP})"; then + bwrap_is_suid=true +fi FUSE_DIR= for mp in $(cat /proc/self/mounts | grep " fuse[. ]" | grep user_id=$(id -u) | awk '{print $2}'); do @@ -46,6 +49,30 @@ UNREADABLE= fi +# https://github.com/projectatomic/bubblewrap/issues/217 +# are we on a merged-/usr system? +if [ /lib -ef /usr/lib ]; then + BWRAP_RO_HOST_ARGS="--ro-bind /usr /usr + --ro-bind /etc /etc + --dir /var/tmp + --symlink usr/lib /lib + --symlink usr/lib64 /lib64 + --symlink usr/bin /bin + --symlink usr/sbin /sbin + --proc /proc + --dev /dev" +else + BWRAP_RO_HOST_ARGS="--ro-bind /usr /usr + --ro-bind /etc /etc + --ro-bind /bin /bin + --ro-bind /lib /lib + --ro-bind-try /lib64 /lib64 + --ro-bind /sbin /sbin + --dir /var/tmp + --proc /proc + --dev /dev" +fi + # Default arg, bind whole host fs to /, tmpfs on /tmp RUN="${BWRAP} --bind / / --tmpfs /tmp" @@ -53,7 +80,7 @@ skip Seems like bwrap is not working at all. Maybe setuid is not working fi -echo "1..33" +echo "1..49" # Test help ${BWRAP} --help > help.txt @@ -78,7 +105,7 @@ echo -n "expect EPERM: " >&2 # Test caps when bwrap is not setuid - if ! test -u ${BWRAP}; then + if test -n "${bwrap_is_suid:-}"; then CAP="--cap-add ALL" else CAP="" @@ -113,6 +140,54 @@ assert_file_has_content as_pid_1.txt "1" echo "ok - can run as pid 1" +# Test --info-fd and --json-status-fd +if $RUN --unshare-all --info-fd 42 --json-status-fd 43 -- bash -c 'exit 42' 42>info.json 43>json-status.json 2>err.txt; then + fatal "should have been exit 42" +fi +assert_file_has_content info.json '"child-pid": [0-9]' +assert_file_has_content json-status.json '"child-pid": [0-9]' +assert_file_has_content_literal json-status.json '"exit-code": 42' +echo "ok info and json-status fd" + +DATA=$($RUN --proc /proc --unshare-all --info-fd 42 --json-status-fd 43 -- bash -c 'stat -L --format "%n %i" /proc/self/ns/*' 42>info.json 43>json-status.json 2>err.txt) + +for NS in "ipc" "mnt" "net" "pid" "uts"; do + + want=$(echo "$DATA" | grep "/proc/self/ns/$NS" | awk '{print $2}') + assert_file_has_content info.json "$want" + assert_file_has_content json-status.json "$want" +done + +echo "ok namespace id info in info and json-status fd" + +if ! which strace 2>/dev/null || ! strace -h | grep -v -e default | grep -e fault; then + echo "ok - # SKIP no strace fault injection" +else + ! strace -o /dev/null -f -e trace=prctl -e fault=prctl:when=39 $RUN --die-with-parent --json-status-fd 42 true 42>json-status.json + assert_not_file_has_content json-status.json '"exit-code": [0-9]' + echo "ok pre-exec failure doesn't include exit-code in json-status" +fi + +notanexecutable=/ +$RUN --json-status-fd 42 $notanexecutable 42>json-status.json || true +assert_not_file_has_content json-status.json '"exit-code": [0-9]' +echo "ok exec failure doesn't include exit-code in json-status" + +# These tests require --unshare-user +if test -n "${bwrap_is_suid:-}"; then + echo "ok - # SKIP no --cap-add support" + echo "ok - # SKIP no --cap-add support" +else + BWRAP_RECURSE="$BWRAP --unshare-all --uid 0 --gid 0 --cap-add ALL --bind / / --bind /proc /proc" + $BWRAP_RECURSE -- $BWRAP --unshare-all --bind / / --bind /proc /proc echo hello > recursive_proc.txt + assert_file_has_content recursive_proc.txt "hello" + echo "ok - can mount /proc recursively" + + $BWRAP_RECURSE -- $BWRAP --unshare-all ${BWRAP_RO_HOST_ARGS} findmnt > recursive-newroot.txt + assert_file_has_content recursive-newroot.txt "/usr" + echo "ok - can pivot to new rootfs recursively" +fi + # Test error prefixing if $RUN --unshare-pid --bind /source-enoent /dest true 2>err.txt; then assert_not_reached "bound nonexistent source" @@ -158,7 +233,7 @@ locktype = fcntl.F_SETLKW else: locktype = fcntl.F_SETLK -lockdata = struct.pack("hhllhh", fcntl.F_WRLCK, 0, 0, 0, 0, 0) +lockdata = struct.pack("hhqqhh", fcntl.F_WRLCK, 0, 0, 0, 0, 0) fd=open(sys.argv[1], 'a') try: fcntl.fcntl(fd.fileno(), locktype, lockdata) @@ -199,4 +274,106 @@ $RUN --args 3 test -d /tmp/hello/world 3 bin/--inadvisable-executable-name-- +echo "echo hello" >> bin/--inadvisable-executable-name-- +chmod +x bin/--inadvisable-executable-name-- +PATH="${srcd}:$PATH" $RUN -- sh -c "echo hello" > stdout +assert_file_has_content stdout hello +echo "ok - we can run with --" +PATH="$(pwd)/bin:$PATH" $RUN -- --inadvisable-executable-name-- > stdout +assert_file_has_content stdout hello +echo "ok - we can run an inadvisable executable name with --" +if $RUN -- --dev-bind /dev /dev sh -c 'echo should not have run'; then + assert_not_reached "'--dev-bind' should have been interpreted as a (silly) executable name" +fi +echo "ok - options like --dev-bind are defanged by --" + +if command -v mktemp > /dev/null; then + tempfile="$(mktemp /tmp/bwrap-test-XXXXXXXX)" + echo "hello" > "$tempfile" + $BWRAP --bind / / cat "$tempfile" > stdout + assert_file_has_content stdout hello + echo "ok - bind-mount of / exposes real /tmp" + $BWRAP --bind / / --bind /tmp /tmp cat "$tempfile" > stdout + assert_file_has_content stdout hello + echo "ok - bind-mount of /tmp exposes real /tmp" + if [ -d /mnt ]; then + $BWRAP --bind / / --bind /tmp /mnt cat "/mnt/${tempfile#/tmp/}" > stdout + assert_file_has_content stdout hello + echo "ok - bind-mount of /tmp onto /mnt exposes real /tmp" + else + echo "ok - # SKIP /mnt does not exist" + fi +else + echo "ok - # SKIP mktemp not found" + echo "ok - # SKIP mktemp not found" + echo "ok - # SKIP mktemp not found" +fi + +if $RUN test -d /tmp/oldroot; then + assert_not_reached "/tmp/oldroot should not be visible" +fi +if $RUN test -d /tmp/newroot; then + assert_not_reached "/tmp/newroot should not be visible" +fi + +echo "hello" > input.$$ +$BWRAP --bind / / --bind "$(pwd)" /tmp cat /tmp/input.$$ > stdout +assert_file_has_content stdout hello +if $BWRAP --bind / / --bind "$(pwd)" /tmp test -d /tmp/oldroot; then + assert_not_reached "/tmp/oldroot should not be visible" +fi +if $BWRAP --bind / / --bind "$(pwd)" /tmp test -d /tmp/newroot; then + assert_not_reached "/tmp/newroot should not be visible" +fi +echo "ok - we can mount another directory onto /tmp" + +echo "hello" > input.$$ +$RUN --bind "$(pwd)" /tmp/here cat /tmp/here/input.$$ > stdout +assert_file_has_content stdout hello +if $RUN --bind "$(pwd)" /tmp/here test -d /tmp/oldroot; then + assert_not_reached "/tmp/oldroot should not be visible" +fi +if $RUN --bind "$(pwd)" /tmp/here test -d /tmp/newroot; then + assert_not_reached "/tmp/newroot should not be visible" +fi +echo "ok - we can mount another directory inside /tmp" + +# These tests need user namespaces +if test -n "${bwrap_is_suid:-}"; then + echo "ok - # SKIP no setuid support for --unshare-user" + echo "ok - # SKIP no setuid support for --unshare-user" +else + mkfifo donepipe + + $RUN --info-fd 42 --unshare-user sh -c 'readlink /proc/self/ns/user > sandbox-userns; cat < donepipe' 42>info.json & + while ! test -f sandbox-userns; do sleep 1; done + SANDBOX1PID=$(extract_child_pid info.json) + + $RUN --userns 11 readlink /proc/self/ns/user > sandbox2-userns 11< /proc/$SANDBOX1PID/ns/user + echo foo > donepipe + + assert_files_equal sandbox-userns sandbox2-userns + + rm donepipe info.json sandbox-userns + + echo "ok - Test --userns" + + mkfifo donepipe + $RUN --info-fd 42 --unshare-user --unshare-pid sh -c 'readlink /proc/self/ns/pid > sandbox-pidns; cat < donepipe' 42>info.json & + while ! test -f sandbox-pidns; do sleep 1; done + SANDBOX1PID=$(extract_child_pid info.json) + + $RUN --userns 11 --pidns 12 readlink /proc/self/ns/pid > sandbox2-pidns 11< /proc/$SANDBOX1PID/ns/user 12< /proc/$SANDBOX1PID/ns/pid + echo foo > donepipe + + assert_files_equal sandbox-pidns sandbox2-pidns + + rm donepipe info.json sandbox-pidns + + echo "ok - Test --pidns" +fi + + echo "ok - End of test" diff -Nru bubblewrap-0.2.1/utils.c bubblewrap-0.4.0/utils.c --- bubblewrap-0.2.1/utils.c 2018-04-06 14:52:40.000000000 +0000 +++ bubblewrap-0.4.0/utils.c 2019-11-27 12:34:31.000000000 +0000 @@ -19,6 +19,7 @@ #include "utils.h" #include +#include #ifdef HAVE_SELINUX #include #endif @@ -75,10 +76,23 @@ void die_oom (void) { - puts ("Out of memory"); + fputs ("Out of memory\n", stderr); exit (1); } +/* Fork, return in child, exiting the previous parent */ +void +fork_intermediate_child (void) +{ + int pid = fork (); + if (pid == -1) + die_with_error ("Can't fork for --pidns"); + + /* Parent is an process not needed */ + if (pid != 0) + exit (0); +} + void * xmalloc (size_t size) { @@ -670,6 +684,86 @@ return 0; } +/* Send an ucred with current pid/uid/gid over a socket, it can be + read back with read_pid_from_socket(), and then the kernel has + translated it between namespaces as needed. */ +void +send_pid_on_socket (int socket) +{ + char buf[1] = { 0 }; + struct msghdr msg = {}; + struct iovec iov = { buf, sizeof (buf) }; + const ssize_t control_len_snd = CMSG_SPACE(sizeof(struct ucred)); + char control_buf_snd[control_len_snd]; + struct cmsghdr *cmsg; + struct ucred *cred; + + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = control_buf_snd; + msg.msg_controllen = control_len_snd; + + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_CREDENTIALS; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred)); + cred = (struct ucred *)CMSG_DATA(cmsg); + + cred->pid = getpid (); + cred->uid = geteuid (); + cred->gid = getegid (); + + if (sendmsg (socket, &msg, 0) < 0) + die_with_error ("Can't send pid"); +} + +void +create_pid_socketpair (int sockets[2]) +{ + int enable = 1; + + if (socketpair (AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sockets) != 0) + die_with_error ("Can't create intermediate pids socket"); + + if (setsockopt (sockets[0], SOL_SOCKET, SO_PASSCRED, &enable, sizeof (enable)) < 0) + die_with_error ("Can't set SO_PASSCRED"); +} + +int +read_pid_from_socket (int socket) +{ + char recv_buf[1] = { 0 }; + struct msghdr msg = {}; + struct iovec iov = { recv_buf, sizeof (recv_buf) }; + const ssize_t control_len_rcv = CMSG_SPACE(sizeof(struct ucred)); + char control_buf_rcv[control_len_rcv]; + struct cmsghdr* cmsg; + + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = control_buf_rcv; + msg.msg_controllen = control_len_rcv; + + if (recvmsg (socket, &msg, 0) < 0) + die_with_error ("Cant read pid from socket"); + + if (msg.msg_controllen <= 0) + die ("Unexpected short read from pid socket"); + + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) + { + const unsigned payload_len = cmsg->cmsg_len - CMSG_LEN(0); + if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_CREDENTIALS && + payload_len == sizeof(struct ucred)) + { + struct ucred *cred = (struct ucred *)CMSG_DATA(cmsg); + return cred->pid; + } + } + die ("No pid returned on socket"); +} + int raw_clone (unsigned long flags, void *child_stack) diff -Nru bubblewrap-0.2.1/utils.h bubblewrap-0.4.0/utils.h --- bubblewrap-0.2.1/utils.h 2018-04-06 14:52:40.000000000 +0000 +++ bubblewrap-0.4.0/utils.h 2019-11-27 12:34:31.000000000 +0000 @@ -54,6 +54,8 @@ void die_oom (void) __attribute__((__noreturn__)); void die_unless_label_valid (const char *label); +void fork_intermediate_child (void); + void *xmalloc (size_t size); void *xcalloc (size_t size); void *xrealloc (void *ptr, @@ -107,6 +109,9 @@ int mkdir_with_parents (const char *pathname, int mode, bool create_last); +void create_pid_socketpair (int sockets[2]); +void send_pid_on_socket (int socket); +int read_pid_from_socket (int socket); /* syscall wrappers */ int raw_clone (unsigned long flags,