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:
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user