85 lines
3.0 KiB
Diff
85 lines
3.0 KiB
Diff
From 5404a15d34301a5a0dd5930203e03c76b80ebf21 Mon Sep 17 00:00:00 2001
|
|
From: Alexander Larsson <alexl@redhat.com>
|
|
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 <sroettger@google.com>, 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)
|
|
{
|