Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

homed uidmapping (just for the directory backend) #21136

Merged
merged 6 commits into from
Oct 27, 2021
4 changes: 4 additions & 0 deletions src/home/home-util.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
#include "time-util.h"
#include "user-record.h"

/* See https://systemd.io/UIDS-GIDS for details how this range fits into the rest of the world */
#define HOME_UID_MIN 60001
#define HOME_UID_MAX 60513
poettering marked this conversation as resolved.
Show resolved Hide resolved

bool suitable_user_name(const char *name);
int suitable_realm(const char *realm);
int suitable_image_path(const char *path);
Expand Down
3 changes: 0 additions & 3 deletions src/home/homed-manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@ typedef struct Manager Manager;
#include "homed-home.h"
#include "varlink.h"

#define HOME_UID_MIN 60001
#define HOME_UID_MAX 60513

struct Manager {
sd_event *event;
sd_bus *bus;
Expand Down
119 changes: 95 additions & 24 deletions src/home/homework-directory.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,51 @@
#include "btrfs-util.h"
#include "fd-util.h"
#include "homework-directory.h"
#include "homework-mount.h"
#include "homework-quota.h"
#include "mkdir.h"
#include "mount-util.h"
#include "path-util.h"
#include "rm-rf.h"
#include "tmpfile-util.h"
#include "umask-util.h"
#include "user-util.h"

int home_setup_directory(UserRecord *h, HomeSetup *setup) {
const char *ip;
int r;

assert(h);
assert(IN_SET(user_record_storage(h), USER_DIRECTORY, USER_SUBVOLUME));
assert(setup);
assert(!setup->undo_mount);
assert(setup->root_fd < 0);

/* We'll bind mount the image directory to a new mount point where we'll start adjusting it. Only
* once that's complete we'll move the thing to its final place eventually. */
r = home_unshare_and_mkdir();
if (r < 0)
return r;

assert_se(ip = user_record_image_path(h));

r = mount_follow_verbose(LOG_ERR, ip, HOME_RUNTIME_WORK_DIR, NULL, MS_BIND, NULL);
if (r < 0)
return r;

setup->undo_mount = true;

/* Turn off any form of propagation for this */
r = mount_nofollow_verbose(LOG_ERR, NULL, HOME_RUNTIME_WORK_DIR, NULL, MS_PRIVATE, NULL);
if (r < 0)
return r;

setup->root_fd = open(user_record_image_path(h), O_RDONLY|O_CLOEXEC|O_DIRECTORY);
/* Adjust MS_SUID and similar flags */
r = mount_nofollow_verbose(LOG_ERR, NULL, HOME_RUNTIME_WORK_DIR, NULL, MS_BIND|MS_REMOUNT|user_record_mount_flags(h), NULL);
if (r < 0)
return r;

setup->root_fd = open(HOME_RUNTIME_WORK_DIR, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW);
if (setup->root_fd < 0)
return log_error_errno(errno, "Failed to open home directory: %m");

Expand All @@ -31,17 +63,14 @@ int home_activate_directory(
UserRecord **ret_home) {

_cleanup_(user_record_unrefp) UserRecord *new_home = NULL, *header_home = NULL;
const char *hdo, *hd, *ipo, *ip;
const char *hd, *hdo;
int r;

assert(h);
assert(IN_SET(user_record_storage(h), USER_DIRECTORY, USER_SUBVOLUME, USER_FSCRYPT));
assert(setup);
assert(ret_home);

assert_se(ipo = user_record_image_path(h));
ip = strdupa_safe(ipo); /* copy out, since reconciliation might cause changing of the field */

assert_se(hdo = user_record_home_directory(h));
hd = strdupa_safe(hdo);

Expand All @@ -53,24 +82,19 @@ int home_activate_directory(
if (r < 0)
return r;

setup->root_fd = safe_close(setup->root_fd);
r = home_extend_embedded_identity(new_home, h, setup);
if (r < 0)
return r;

/* Create mount point to mount over if necessary */
if (!path_equal(ip, hd))
(void) mkdir_p(hd, 0700);
/* Close fd to private mount before moving mount */
setup->root_fd = safe_close(setup->root_fd);

/* Create a mount point (even if the directory is already placed correctly), as a way to indicate
* this mount point is now "activated". Moreover, we want to set per-user
* MS_NOSUID/MS_NOEXEC/MS_NODEV. */
r = mount_nofollow_verbose(LOG_ERR, ip, hd, NULL, MS_BIND, NULL);
/* We are now done with everything, move the mount into place */
r = home_move_mount(NULL, hd);
if (r < 0)
return r;

r = mount_nofollow_verbose(LOG_ERR, NULL, hd, NULL, MS_BIND|MS_REMOUNT|user_record_mount_flags(h), NULL);
if (r < 0) {
(void) umount_verbose(LOG_ERR, hd, UMOUNT_NOFOLLOW);
return r;
}
setup->undo_mount = false;

setup->do_drop_caches = false;

Expand All @@ -80,16 +104,18 @@ int home_activate_directory(
return 0;
}

int home_create_directory_or_subvolume(UserRecord *h, UserRecord **ret_home) {
int home_create_directory_or_subvolume(UserRecord *h, HomeSetup *setup, UserRecord **ret_home) {
_cleanup_(rm_rf_subvolume_and_freep) char *temporary = NULL;
_cleanup_(user_record_unrefp) UserRecord *new_home = NULL;
_cleanup_close_ int root_fd = -1;
_cleanup_close_ int mount_fd = -1;
_cleanup_free_ char *d = NULL;
bool is_subvolume = false;
const char *ip;
int r;

assert(h);
assert(IN_SET(user_record_storage(h), USER_DIRECTORY, USER_SUBVOLUME));
assert(setup);
assert(ret_home);

assert_se(ip = user_record_image_path(h));
Expand All @@ -108,6 +134,7 @@ int home_create_directory_or_subvolume(UserRecord *h, UserRecord **ret_home) {

if (r >= 0) {
log_info("Subvolume created.");
is_subvolume = true;

if (h->disk_size != UINT64_MAX) {

Expand Down Expand Up @@ -149,15 +176,44 @@ int home_create_directory_or_subvolume(UserRecord *h, UserRecord **ret_home) {

temporary = TAKE_PTR(d); /* Needs to be destroyed now */

root_fd = open(temporary, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW);
if (root_fd < 0)
/* Let's decouple namespaces now, so that we can possibly mount a UID map mount into
* /run/systemd/user-home-mount/ that noone will see but us. */
r = home_unshare_and_mkdir();
if (r < 0)
return r;

setup->root_fd = open(temporary, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW);
if (setup->root_fd < 0)
return log_error_errno(errno, "Failed to open temporary home directory: %m");

r = home_populate(h, root_fd);
/* Try to apply a UID shift, so that the directory is actually owned by "nobody", and is only mapped
* to the proper UID while active. — Well, that's at least the theory. Unfortunately, only btrfs does
* per-subvolume quota. The others do per-uid quota. Which means mapping all home directories to the
* same UID of "nobody" makes quota impossible. Hence unless we actually managed to create a btrfs
* subvolume for this user we'll map the user's UID to itself. Now you might ask: why bother mapping
* at all? It's because we want to restrict the UIDs used on the home directory: we leave all other
* UIDs of the homed UID range unmapped, thus making them unavailable to programs accessing the
* mount. */
r = home_shift_uid(setup->root_fd, HOME_RUNTIME_WORK_DIR, is_subvolume ? UID_NOBODY : h->uid, h->uid, &mount_fd);
if (r > 0)
setup->undo_mount = true; /* If uidmaps worked we have a mount to undo again */

if (mount_fd >= 0) {
/* If we have established a new mount, then we can use that as new root fd to our home directory. */
safe_close(setup->root_fd);

setup->root_fd = fd_reopen(mount_fd, O_RDONLY|O_CLOEXEC|O_DIRECTORY);
if (setup->root_fd < 0)
return log_error_errno(setup->root_fd, "Unable to convert mount fd into proper directory fd: %m");

mount_fd = safe_close(mount_fd);
}

r = home_populate(h, setup->root_fd);
if (r < 0)
return r;

r = home_sync_and_statfs(root_fd, NULL);
r = home_sync_and_statfs(setup->root_fd, NULL);
if (r < 0)
return r;

Expand All @@ -182,6 +238,17 @@ int home_create_directory_or_subvolume(UserRecord *h, UserRecord **ret_home) {
if (r < 0)
return log_error_errno(r, "Failed to add binding to record: %m");

setup->root_fd = safe_close(setup->root_fd);

/* Unmount mapped mount before we move the dir into place */
if (setup->undo_mount) {
r = umount_verbose(LOG_ERR, HOME_RUNTIME_WORK_DIR, UMOUNT_NOFOLLOW);
if (r < 0)
return r;

setup->undo_mount = false;
}

if (rename(temporary, ip) < 0)
return log_error_errno(errno, "Failed to rename %s to %s: %m", temporary, ip);

Expand Down Expand Up @@ -216,6 +283,10 @@ int home_resize_directory(
if (r < 0)
return r;

r = home_maybe_shift_uid(h, setup);
if (r < 0)
return r;

r = home_update_quota_auto(h, NULL);
if (ERRNO_IS_NOT_SUPPORTED(r))
return -ESOCKTNOSUPPORT; /* make recognizable */
Expand Down
2 changes: 1 addition & 1 deletion src/home/homework-directory.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@

int home_setup_directory(UserRecord *h, HomeSetup *setup);
int home_activate_directory(UserRecord *h, HomeSetup *setup, PasswordCache *cache, UserRecord **ret_home);
int home_create_directory_or_subvolume(UserRecord *h, UserRecord **ret_home);
int home_create_directory_or_subvolume(UserRecord *h, HomeSetup *setup, UserRecord **ret_home);
int home_resize_directory(UserRecord *h, HomeSetupFlags flags, PasswordCache *cache, HomeSetup *setup, UserRecord **ret_home);