namespace-util: add detach_mount_namespace_harder()

This is just like detach_mount_namespace() but if need be uses unpriv
user namespaces to be able to execute CLONE_NEWNS.
This commit is contained in:
Lennart Poettering 2023-03-13 15:23:11 +01:00
parent afdd0efa63
commit 5783b4a954
2 changed files with 49 additions and 0 deletions

View file

@ -214,6 +214,54 @@ int detach_mount_namespace(void) {
return 0;
}
int detach_mount_namespace_harder(uid_t target_uid, gid_t target_gid) {
int r;
/* Tried detach_mount_namespace() first. If that doesn't work due to permissions, opens up an
* unprivileged user namespace with a mapping of the originating UID/GID to the specified target
* UID/GID. Then, tries detach_mount_namespace() again.
*
* Or in other words: tries much harder to get a mount namespace, making use of unprivileged user
* namespaces if need be.
*
* Note that after this function completed:
*
* if we had privs, afterwards uids/gids on files and processes are as before
*
* if we had no privs, our own id and all our files will show up owned by target_uid/target_gid,
* and everything else owned by nobody.
*
* Yes, that's quite a difference. */
if (!uid_is_valid(target_uid))
return -EINVAL;
if (!gid_is_valid(target_gid))
return -EINVAL;
r = detach_mount_namespace();
if (r != -EPERM)
return r;
if (unshare(CLONE_NEWUSER) < 0)
return log_debug_errno(errno, "Failed to acquire user namespace: %m");
r = write_string_filef("/proc/self/uid_map", 0,
UID_FMT " " UID_FMT " 1\n", target_uid, getuid());
if (r < 0)
return log_debug_errno(r, "Failed to write uid map: %m");
r = write_string_file("/proc/self/setgroups", "deny", 0);
if (r < 0)
return log_debug_errno(r, "Failed to write setgroups file: %m");
r = write_string_filef("/proc/self/gid_map", 0,
GID_FMT " " GID_FMT " 1\n", target_gid, getgid());
if (r < 0)
return log_debug_errno(r, "Failed to write gid map: %m");
return detach_mount_namespace();
}
int userns_acquire(const char *uid_map, const char *gid_map) {
char path[STRLEN("/proc//uid_map") + DECIMAL_STR_MAX(pid_t) + 1];
_cleanup_(sigkill_waitp) pid_t pid = 0;

View file

@ -34,6 +34,7 @@ int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int
int fd_is_ns(int fd, unsigned long nsflag);
int detach_mount_namespace(void);
int detach_mount_namespace_harder(uid_t target_uid, gid_t target_gid);
static inline bool userns_shift_range_valid(uid_t shift, uid_t range) {
/* Checks that the specified userns range makes sense, i.e. contains at least one UID, and the end