diff --git a/src/basic/namespace-util.c b/src/basic/namespace-util.c index c103df5c00..a1a1063b72 100644 --- a/src/basic/namespace-util.c +++ b/src/basic/namespace-util.c @@ -300,15 +300,16 @@ int namespace_enter_full( /* If we don't have CAP_SYS_ADMIN in our own user namespace, our best bet is to enter the * user namespace first (if we got one) to get CAP_SYS_ADMIN within the child user namespace, * and then hope the other namespaces are owned by the child user namespace. If they aren't, - * we'll just get an EPERM later on when trying to setns() to them. */ + * we'll just get an EPERM later on when trying to setns() to them. If there's no namespace + * left to join, there's nothing to need privileges for. */ - if (userns_fd < 0) + if (userns_fd >= 0) { + if (setns(userns_fd, CLONE_NEWUSER) < 0) + return -errno; + } else if (pidns_fd >= 0 || mntns_fd >= 0 || netns_fd >= 0 || root_fd >= 0) return log_debug_errno( SYNTHETIC_ERRNO(EPERM), "Need CAP_SYS_ADMIN or a child user namespace to enter namespaces."); - - if (setns(userns_fd, CLONE_NEWUSER) < 0) - return -errno; } if (pidns_fd >= 0) diff --git a/src/test/test-namespace-util.c b/src/test/test-namespace-util.c index b05bbe08b9..de8d6da510 100644 --- a/src/test/test-namespace-util.c +++ b/src/test/test-namespace-util.c @@ -54,6 +54,32 @@ TEST(namespace_enter) { } } +TEST(namespace_enter_nothing) { + int r; + + /* With nothing to join, or only namespaces we're already in, entering must succeed without + * CAP_SYS_ADMIN, since no setns() call is necessary. */ + r = ASSERT_OK(pidref_safe_fork( + "test-ns-enter-nothing", + FORK_LOG|FORK_WAIT|FORK_DEATHSIG_SIGKILL, + NULL)); + if (r == 0) { + ASSERT_OK(drop_capability(CAP_SYS_ADMIN)); + + ASSERT_OK(namespace_enter(-EBADF, -EBADF, -EBADF, -EBADF, -EBADF)); + + _cleanup_close_ int userns_fd = ASSERT_OK_ERRNO(open("/proc/self/ns/user", O_RDONLY|O_CLOEXEC)); + ASSERT_OK(namespace_enter(-EBADF, -EBADF, -EBADF, userns_fd, -EBADF)); + + _cleanup_close_ int mntns_fd = ASSERT_OK_ERRNO(open("/proc/self/ns/mnt", O_RDONLY|O_CLOEXEC)); + _cleanup_close_ int pidns_fd = ASSERT_OK_ERRNO(open("/proc/self/ns/pid", O_RDONLY|O_CLOEXEC)); + _cleanup_close_ int netns_fd = ASSERT_OK_ERRNO(open("/proc/self/ns/net", O_RDONLY|O_CLOEXEC)); + ASSERT_OK(namespace_enter(pidns_fd, mntns_fd, netns_fd, userns_fd, -EBADF)); + + _exit(EXIT_SUCCESS); + } +} + TEST(namespace_enter_mapped) { static const struct { uid_t uid;