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 50c7278..3deafb9 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_NET_ADMIN) | 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 */