diff --git a/bubblewrap.c b/bubblewrap.c index 8d0c5f7..87000e8 100644 --- 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 --git a/demos/userns-block-fd.py b/demos/userns-block-fd.py index 4c68242..2ef2fd6 100755 --- a/demos/userns-block-fd.py +++ b/demos/userns-block-fd.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 import os, select, subprocess, sys, json diff --git a/tests/test-run.sh b/tests/test-run.sh index a01f41c..95c77d8 100755 --- a/tests/test-run.sh +++ b/tests/test-run.sh @@ -215,18 +215,25 @@ 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 # Test --die-with-parent cat >lockf-n.py <