namespace-util: don't require privileges when there's nothing to enter

When namespace_enter_full() is called with namespaces that the current
process is already a part of, skip the setns() call on the user
namespace. This allows the function to succeed without CAP_SYS_ADMIN.

Follow-up for 211c3c9d41

(cherry picked from commit 27b1fd3a1e2e6c28bb09ec79cb3dc949a4ba416d)
This commit is contained in:
Skye Soss
2026-10-16 17:24:51 +01:00
committed by Luca Boccassi
parent 75b8c3eff1
commit 5cfe544a1a
2 changed files with 32 additions and 5 deletions
+6 -5
View File
@@ -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)
+26
View File
@@ -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;