From 655807f572def087d230e22659ccd9a45cccf77b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 21 Oct 2021 13:46:01 +0200 Subject: [PATCH 1/3] homework: make sure fscrypt backend takes a HomeSetup object for all calls Similar to the same chage we did for the directory backend. Let's always path the setup context object, i.e. HomeSetup, and store whatever we set up in there. No actual change in behaviour. --- src/home/homework-fscrypt.c | 17 +++++++++-------- src/home/homework-fscrypt.h | 3 ++- src/home/homework.c | 2 +- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/home/homework-fscrypt.c b/src/home/homework-fscrypt.c index 899d4e10c6c..77185c9c747 100644 --- a/src/home/homework-fscrypt.c +++ b/src/home/homework-fscrypt.c @@ -456,6 +456,7 @@ finish: int home_create_fscrypt( UserRecord *h, + HomeSetup *setup, char **effective_passwords, UserRecord **ret_home) { @@ -464,7 +465,6 @@ int home_create_fscrypt( _cleanup_(erase_and_freep) void *volume_key = NULL; struct fscrypt_policy policy = {}; size_t volume_key_size = 512 / 8; - _cleanup_close_ int root_fd = -1; _cleanup_free_ char *d = NULL; uint32_t nr = 0; const char *ip; @@ -473,6 +473,7 @@ int home_create_fscrypt( assert(h); assert(user_record_storage(h) == USER_FSCRYPT); + assert(setup); assert(ret_home); assert_se(ip = user_record_image_path(h)); @@ -488,11 +489,11 @@ int home_create_fscrypt( 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) + 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"); - if (ioctl(root_fd, FS_IOC_GET_ENCRYPTION_POLICY, &policy) < 0) { + if (ioctl(setup->root_fd, FS_IOC_GET_ENCRYPTION_POLICY, &policy) < 0) { if (ERRNO_IS_NOT_SUPPORTED(errno)) { log_error_errno(errno, "File system does not support fscrypt: %m"); return -ENOLINK; /* make recognizable */ @@ -526,13 +527,13 @@ int home_create_fscrypt( log_info("Uploaded volume key to kernel."); - if (ioctl(root_fd, FS_IOC_SET_ENCRYPTION_POLICY, &policy) < 0) + if (ioctl(setup->root_fd, FS_IOC_SET_ENCRYPTION_POLICY, &policy) < 0) return log_error_errno(errno, "Failed to set fscrypt policy on directory: %m"); log_info("Encryption policy set."); STRV_FOREACH(i, effective_passwords) { - r = fscrypt_slot_set(root_fd, volume_key, volume_key_size, *i, nr); + r = fscrypt_slot_set(setup->root_fd, volume_key, volume_key_size, *i, nr); if (r < 0) return r; @@ -541,11 +542,11 @@ int home_create_fscrypt( (void) home_update_quota_classic(h, temporary); - r = home_populate(h, root_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; diff --git a/src/home/homework-fscrypt.h b/src/home/homework-fscrypt.h index 736bcb9dcd9..e9262c6349a 100644 --- a/src/home/homework-fscrypt.h +++ b/src/home/homework-fscrypt.h @@ -5,6 +5,7 @@ #include "user-record.h" int home_setup_fscrypt(UserRecord *h, const PasswordCache *cache, HomeSetup *setup); -int home_create_fscrypt(UserRecord *h, char **effective_passwords, UserRecord **ret_home); + +int home_create_fscrypt(UserRecord *h, HomeSetup *setup, char **effective_passwords, UserRecord **ret_home); int home_passwd_fscrypt(UserRecord *h, HomeSetup *setup, const PasswordCache *cache, char **effective_passwords); diff --git a/src/home/homework.c b/src/home/homework.c index cfc0c945def..7d1ea481cea 100644 --- a/src/home/homework.c +++ b/src/home/homework.c @@ -1272,7 +1272,7 @@ static int home_create(UserRecord *h, UserRecord **ret_home) { break; case USER_FSCRYPT: - r = home_create_fscrypt(h, effective_passwords, &new_home); + r = home_create_fscrypt(h, &setup, effective_passwords, &new_home); break; case USER_CIFS: From 55166094ae68951f3905d376e3e4a32669b34372 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 21 Oct 2021 13:58:10 +0200 Subject: [PATCH 2/3] homework: add new helper home_setup_undo_mount() --- src/home/homework-directory.c | 10 +++------- src/home/homework.c | 24 +++++++++++++++++++----- src/home/homework.h | 2 ++ 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/src/home/homework-directory.c b/src/home/homework-directory.c index 65575713617..3418034bd1a 100644 --- a/src/home/homework-directory.c +++ b/src/home/homework-directory.c @@ -241,13 +241,9 @@ int home_create_directory_or_subvolume(UserRecord *h, HomeSetup *setup, UserReco 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; - } + r = home_setup_undo_mount(setup, LOG_ERR); + if (r < 0) + return r; if (rename(temporary, ip) < 0) return log_error_errno(errno, "Failed to rename %s to %s: %m", temporary, ip); diff --git a/src/home/homework.c b/src/home/homework.c index 7d1ea481cea..8dc9004c064 100644 --- a/src/home/homework.c +++ b/src/home/homework.c @@ -298,6 +298,22 @@ static void drop_caches_now(void) { log_debug("Dropped caches."); } +int home_setup_undo_mount(HomeSetup *setup, int level) { + int r; + + assert(setup); + + if (!setup->undo_mount) + return 0; + + r = umount_verbose(level, HOME_RUNTIME_WORK_DIR, UMOUNT_NOFOLLOW); + if (r < 0) + return r; + + setup->undo_mount = false; + return 1; +} + int home_setup_done(HomeSetup *setup) { int r = 0, q; @@ -316,11 +332,9 @@ int home_setup_done(HomeSetup *setup) { setup->root_fd = safe_close(setup->root_fd); } - if (setup->undo_mount) { - q = umount_verbose(LOG_DEBUG, HOME_RUNTIME_WORK_DIR, UMOUNT_NOFOLLOW); - if (q < 0) - r = q; - } + q = home_setup_undo_mount(setup, LOG_DEBUG); + if (q < 0) + r = q; if (setup->undo_dm && setup->crypt_device && setup->dm_name) { q = sym_crypt_deactivate_by_name(setup->crypt_device, setup->dm_name, 0); diff --git a/src/home/homework.h b/src/home/homework.h index 1fa5a1e37a5..0ce8457727c 100644 --- a/src/home/homework.h +++ b/src/home/homework.h @@ -70,6 +70,8 @@ typedef enum HomeSetupFlags { int home_setup_done(HomeSetup *setup); +int home_setup_undo_mount(HomeSetup *setup, int level); + int home_setup(UserRecord *h, HomeSetupFlags flags, PasswordCache *cache, HomeSetup *setup, UserRecord **ret_header_home); int home_refresh(UserRecord *h, HomeSetup *setup, UserRecord *header_home, PasswordCache *cache, struct statfs *ret_statfs, UserRecord **ret_new_home); From 65400de021a9088066f0524ec20934900ce93767 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 21 Oct 2021 17:32:51 +0200 Subject: [PATCH 3/3] homework: support uidmaps in fscrypt backend --- src/home/homework-fscrypt.c | 59 +++++++++++++++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 2 deletions(-) diff --git a/src/home/homework-fscrypt.c b/src/home/homework-fscrypt.c index 77185c9c747..057bf0d45ed 100644 --- a/src/home/homework-fscrypt.c +++ b/src/home/homework-fscrypt.c @@ -10,11 +10,13 @@ #include "fd-util.h" #include "hexdecoct.h" #include "homework-fscrypt.h" +#include "homework-mount.h" #include "homework-quota.h" #include "memory-util.h" #include "missing_keyctl.h" #include "missing_syscall.h" #include "mkdir.h" +#include "mount-util.h" #include "nulstr-util.h" #include "openssl-util.h" #include "parse-util.h" @@ -290,8 +292,9 @@ int home_setup_fscrypt( int r; assert(h); - assert(setup); assert(user_record_storage(h) == USER_FSCRYPT); + assert(setup); + assert(setup->root_fd < 0); assert_se(ip = user_record_image_path(h)); @@ -361,6 +364,33 @@ int home_setup_fscrypt( } } + /* 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; + + 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; + + /* 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; + + safe_close(setup->root_fd); + 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"); + return 0; } @@ -463,6 +493,7 @@ int home_create_fscrypt( _cleanup_(rm_rf_physical_and_freep) char *temporary = NULL; _cleanup_(user_record_unrefp) UserRecord *new_home = NULL; _cleanup_(erase_and_freep) void *volume_key = NULL; + _cleanup_close_ int mount_fd = -1; struct fscrypt_policy policy = {}; size_t volume_key_size = 512 / 8; _cleanup_free_ char *d = NULL; @@ -489,6 +520,10 @@ int home_create_fscrypt( temporary = TAKE_PTR(d); /* Needs to be destroyed now */ + 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"); @@ -542,6 +577,21 @@ int home_create_fscrypt( (void) home_update_quota_classic(h, temporary); + r = home_shift_uid(setup->root_fd, HOME_RUNTIME_WORK_DIR, 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; @@ -571,6 +621,12 @@ int home_create_fscrypt( if (r < 0) return log_error_errno(r, "Failed to add binding to record: %m"); + setup->root_fd = safe_close(setup->root_fd); + + r = home_setup_undo_mount(setup, LOG_ERR); + if (r < 0) + return r; + if (rename(temporary, ip) < 0) return log_error_errno(errno, "Failed to rename %s to %s: %m", temporary, ip); @@ -636,7 +692,6 @@ int home_passwd_fscrypt( continue; if (fremovexattr(setup->root_fd, xa) < 0) - if (errno != ENODATA) log_warning_errno(errno, "Failed to remove xattr %s: %m", xa); }