From d8cbc3d8e3f43e56f43285f6fe4cb2c923f794cc Mon Sep 17 00:00:00 2001
From: Simon McVittie <smcv@debian.org>
Date: Sat, 21 May 2016 17:31:10 +0100
Subject: [PATCH 06/11] Add patches from upstream bug 71 to make --dev coexist
 with --unshare-user

---
 debian/changelog                                   |   2 +
 debian/patches/Add-unshare-user-try.patch          |  57 ++++++
 ...tuid-no-unprivileged-user-namespaces-work.patch | 197 +++++++++++++++++++++
 debian/patches/series                              |   2 +
 4 files changed, 258 insertions(+)
 create mode 100644 debian/patches/Add-unshare-user-try.patch
 create mode 100644 debian/patches/Make-setuid-no-unprivileged-user-namespaces-work.patch
 create mode 100644 debian/patches/series

diff --git a/debian/changelog b/debian/changelog
index 00aad10..77fece6 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -7,6 +7,8 @@ bubblewrap (0~git160513-3) UNRELEASED; urgency=medium
   * Increase Priority to optional, because this tool is likely to be
     depended on by gnome-software (via Flatpak) in future
   * debian/gbp.conf: add DEP-14-style git-buildpackage configuration
+  * Add patches from upstream bug 71 to make --dev coexist with
+    --unshare-user
 
  -- Simon McVittie <smcv@debian.org>  Sat, 21 May 2016 15:10:56 +0100
 
diff --git a/debian/patches/Add-unshare-user-try.patch b/debian/patches/Add-unshare-user-try.patch
new file mode 100644
index 0000000..1053ad7
--- /dev/null
+++ b/debian/patches/Add-unshare-user-try.patch
@@ -0,0 +1,57 @@
+From: Alexander Larsson <alexl@redhat.com>
+Date: Fri, 20 May 2016 15:13:57 +0200
+Subject: Add --unshare-user-try
+
+This optionally enables user namespaces, but ignores it if its
+not supported by the kernel.
+
+Note: For this to make any sense, bwrap has to be setuid,
+because unprivileged use requires user namespaces.
+
+Bug: https://github.com/projectatomic/bubblewrap/pull/71
+---
+ bubblewrap.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+diff --git a/bubblewrap.c b/bubblewrap.c
+index 59407a7..6c4664e 100644
+--- a/bubblewrap.c
++++ b/bubblewrap.c
+@@ -148,6 +148,7 @@ usage (int ecode, FILE *out)
+            "    --version                    Print version\n"
+            "    --args FD                    Parse nul-separated args from FD\n"
+            "    --unshare-user               Create new user namespace (may be automatically implied if not setuid)\n"
++           "    --unshare-user-try           Create new user namespace if possible else continue by skipping it\n"
+            "    --unshare-ipc                Create new ipc namespace\n"
+            "    --unshare-pid                Create new pid namespace\n"
+            "    --unshare-net                Create new network namespace\n"
+@@ -840,6 +841,7 @@ read_priv_sec_op (int          read_socket,
+ 
+ char *opt_chdir_path = NULL;
+ bool opt_unshare_user = FALSE;
++bool opt_unshare_user_try = FALSE;
+ bool opt_unshare_pid = FALSE;
+ bool opt_unshare_ipc = FALSE;
+ bool opt_unshare_net = FALSE;
+@@ -955,6 +957,10 @@ parse_args_recurse (int    *argcp,
+         {
+           opt_unshare_user = TRUE;
+         }
++      else if (strcmp (arg, "--unshare-user-try") == 0)
++        {
++          opt_unshare_user_try = TRUE;
++        }
+       else if (strcmp (arg, "--unshare-ipc") == 0)
+         {
+           opt_unshare_ipc = TRUE;
+@@ -1327,6 +1333,10 @@ main (int    argc,
+   if (!is_privileged)
+     opt_unshare_user = TRUE;
+ 
++  if (opt_unshare_user_try &&
++      stat ("/proc/self/ns/user", &sbuf) == 0)
++    opt_unshare_user = TRUE;
++
+   if (argc == 0)
+     usage (EXIT_FAILURE, stderr);
+ 
diff --git a/debian/patches/Make-setuid-no-unprivileged-user-namespaces-work.patch b/debian/patches/Make-setuid-no-unprivileged-user-namespaces-work.patch
new file mode 100644
index 0000000..ec18f7e
--- /dev/null
+++ b/debian/patches/Make-setuid-no-unprivileged-user-namespaces-work.patch
@@ -0,0 +1,197 @@
+From: Alexander Larsson <alexl@redhat.com>
+Date: Fri, 20 May 2016 14:59:43 +0200
+Subject: Make setuid + no-unprivileged user namespaces work
+
+On e.g. debian by default unprivileged namespaces are not allowed.
+Typically the setuid mode is then used. However, if /dev is mounted
+(and thus devpts) then we need to do some workaround in how we
+create the uid/gid maps so uid 0 is mapped while we mount devpts.
+
+Unfortunately the way we were working around that is by using an
+unprivileged unshare(NEWUSER) in the sandbox, which doesn't work.
+See https://github.com/flatpak/flatpak/issues/2 for details.
+
+We work around this by mapping uid/gid 0 + the user. However, since
+this is a privileged operation we need to do that in the parent
+namespace, and we need setuid/setgid rights.
+
+Bug: https://github.com/projectatomic/bubblewrap/pull/71
+---
+ Makefile.am  |  2 +-
+ bubblewrap.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++--------
+ 2 files changed, 70 insertions(+), 11 deletions(-)
+
+diff --git a/Makefile.am b/Makefile.am
+index e4d250b..6e47b5f 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -14,7 +14,7 @@ if PRIV_MODE_SETUID
+ 	$(SUDO_BIN) chmod u+s $(DESTDIR)$(bindir)/bwrap
+ else
+ if PRIV_MODE_FILECAPS
+-	$(SUDO_BIN) setcap cap_sys_admin,cap_net_admin,cap_sys_chroot+ep $(DESTDIR)$(bindir)/bwrap
++	$(SUDO_BIN) setcap cap_sys_admin,cap_net_admin,cap_sys_chroot,cap_setuid,cap_setgid+ep $(DESTDIR)$(bindir)/bwrap
+ endif
+ endif
+ 
+diff --git a/bubblewrap.c b/bubblewrap.c
+index 1f9331a..59407a7 100644
+--- a/bubblewrap.c
++++ b/bubblewrap.c
+@@ -358,7 +358,7 @@ do_init (int event_fd, pid_t initial_pid)
+ }
+ 
+ /* low 32bit caps needed */
+-#define REQUIRED_CAPS_0 (CAP_TO_MASK (CAP_SYS_ADMIN) | CAP_TO_MASK (CAP_SYS_CHROOT) | CAP_TO_MASK (CAP_NET_ADMIN))
++#define REQUIRED_CAPS_0 (CAP_TO_MASK (CAP_SYS_ADMIN) | CAP_TO_MASK (CAP_SYS_CHROOT) | CAP_TO_MASK (CAP_SETUID) | CAP_TO_MASK (CAP_SETGID))
+ /* high 32bit caps needed */
+ #define REQUIRED_CAPS_1 0
+ 
+@@ -441,21 +441,44 @@ write_uid_gid_map (uid_t sandbox_uid,
+                    uid_t parent_uid,
+                    uid_t sandbox_gid,
+                    uid_t parent_gid,
+-                   bool  deny_groups)
++                   pid_t pid,
++                   bool  deny_groups,
++                   bool  map_root)
+ {
+   cleanup_free char *uid_map = NULL;
+   cleanup_free char *gid_map = NULL;
++  cleanup_free char *dir = NULL;
++  cleanup_fd int dir_fd = -1;
+ 
+-  uid_map = xasprintf ("%d %d 1\n", sandbox_uid, parent_uid);
+-  if (write_file_at (proc_fd, "self/uid_map", uid_map) != 0)
++  if (pid == -1)
++    dir = xstrdup ("self");
++  else
++    dir = xasprintf ("%d", pid);
++
++  dir_fd = openat (proc_fd, dir, O_RDONLY | O_PATH);
++  if (dir_fd < 0)
++    die_with_error ("open /proc/%s failed", dir);
++
++  if (map_root && parent_uid != 0 && sandbox_uid != 0)
++    uid_map = xasprintf ("0 0 1\n"
++                         "%d %d 1\n", sandbox_uid, parent_uid);
++  else
++    uid_map = xasprintf ("%d %d 1\n", sandbox_uid, parent_uid);
++
++  if (write_file_at (dir_fd, "uid_map", uid_map) != 0)
+     die_with_error ("setting up uid map");
+ 
+   if (deny_groups &&
+-      write_file_at (proc_fd, "self/setgroups", "deny\n") != 0)
++      write_file_at (dir_fd, "setgroups", "deny\n") != 0)
+     die_with_error ("error writing to setgroups");
+ 
+-  gid_map = xasprintf ("%d %d 1\n", sandbox_gid, parent_gid);
+-  if (write_file_at (proc_fd, "self/gid_map", gid_map) != 0)
++  if (map_root && parent_gid != 0 && sandbox_gid != 0)
++    gid_map = xasprintf ("0 0 1\n"
++                         "%d %d 1\n", sandbox_gid, parent_gid);
++  else
++    gid_map = xasprintf ("%d %d 1\n", sandbox_gid, parent_gid);
++
++  if (write_file_at (dir_fd, "gid_map", gid_map) != 0)
+     die_with_error ("setting up gid map");
+ }
+ 
+@@ -1269,10 +1292,13 @@ main (int    argc,
+   char *old_cwd = NULL;
+   pid_t pid;
+   int event_fd = -1;
++  int child_wait_fd = -1;
+   const char *new_cwd;
+   uid_t ns_uid;
+   gid_t ns_gid;
+   struct stat sbuf;
++  uint64_t val;
++  int res UNUSED;
+ 
+   /* Get the (optional) capabilities we need, drop root */
+   acquire_caps ();
+@@ -1374,6 +1400,10 @@ main (int    argc,
+     if (!stat ("/proc/self/ns/cgroup", &sbuf))
+       clone_flags |= CLONE_NEWCGROUP;
+ 
++  child_wait_fd = eventfd (0, EFD_CLOEXEC);
++  if (child_wait_fd == -1)
++    die_with_error ("eventfd()");
++
+   pid = raw_clone (clone_flags, NULL);
+   if (pid == -1)
+     {
+@@ -1388,23 +1418,52 @@ main (int    argc,
+       die_with_error ("Creating new namespace failed");
+     }
+ 
++  ns_uid = opt_sandbox_uid;
++  ns_gid = opt_sandbox_gid;
++
+   if (pid != 0)
+     {
++      if (is_privileged && opt_unshare_user)
++        {
++          /* Map the uid/gid 0 if opt_needs_devpts, as otherwise
++           * mounting it will fail.
++           * Due to this non-direct mapping we need to have set[ug]id
++           * caps in the parent namespaces, and thus we need to write
++           * the map in the parent namespace, not the child. */
++          write_uid_gid_map (ns_uid, uid,
++                             ns_gid, gid,
++                             pid, TRUE, opt_needs_devpts);
++        }
++
+       /* Initial launched process, wait for exec:ed command to exit */
+ 
+       /* We don't need any caps in the launcher, drop them immediately. */
+       drop_caps ();
++
++      /* Let child run */
++      val = 1;
++      res = write (child_wait_fd, &val, 8);
++      /* 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);
+       exit (0); /* Should not be reached, but better safe... */
+     }
+ 
++  /* Wait for the parent to init uid/gid maps and drop caps */
++  res = read (child_wait_fd, &val, 8);
++  close (child_wait_fd);
++
+   if (opt_unshare_net && loopback_setup () != 0)
+     die ("Can't create loopback device");
+ 
+   ns_uid = opt_sandbox_uid;
+   ns_gid = opt_sandbox_gid;
+-  if (opt_unshare_user)
++  if (!is_privileged && opt_unshare_user)
+     {
++      /* In the unprivileged case we have to write the uid/gid maps in
++       * the child, because we have no caps in the parent */
++
+       if (opt_needs_devpts)
+         {
+           /* This is a bit hacky, but we need to first map the real uid/gid to
+@@ -1417,7 +1476,7 @@ main (int    argc,
+ 
+       write_uid_gid_map (ns_uid, uid,
+                          ns_gid, gid,
+-                         TRUE);
++                         -1, TRUE, FALSE);
+     }
+ 
+   old_umask = umask (0);
+@@ -1523,7 +1582,7 @@ main (int    argc,
+ 
+       write_uid_gid_map (opt_sandbox_uid, ns_uid,
+                          opt_sandbox_gid, ns_gid,
+-                         FALSE);
++                         -1, FALSE, FALSE);
+     }
+ 
+   /* Now make /newroot the real root */
diff --git a/debian/patches/series b/debian/patches/series
new file mode 100644
index 0000000..850bb4f
--- /dev/null
+++ b/debian/patches/series
@@ -0,0 +1,2 @@
+Make-setuid-no-unprivileged-user-namespaces-work.patch
+Add-unshare-user-try.patch
-- 
2.8.1

