mirror of
https://github.com/projectatomic/bubblewrap.git
synced 2026-02-05 15:45:22 +01:00
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. Closes: #72 Approved by: cgwalters
This commit is contained in:
committed by
Atomic Bot
parent
fdf82f9b14
commit
e0a07f4de7
@@ -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
|
||||
|
||||
|
||||
79
bubblewrap.c
79
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 */
|
||||
|
||||
Reference in New Issue
Block a user