]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/home/homework-luks.c
Merge pull request #31899 from yuwata/sd-journal-add-match
[thirdparty/systemd.git] / src / home / homework-luks.c
index 659a4826ee4a886ea133fc9f9acc2afa12caa22e..d70926fe33deae3a1a52c172216b8fafd4d656d2 100644 (file)
@@ -33,6 +33,7 @@
 #include "glyph-util.h"
 #include "gpt.h"
 #include "home-util.h"
+#include "homework-blob.h"
 #include "homework-luks.h"
 #include "homework-mount.h"
 #include "io-util.h"
@@ -125,7 +126,6 @@ static int probe_file_system_by_fd(
                 sd_id128_t *ret_uuid) {
 
         _cleanup_(blkid_free_probep) blkid_probe b = NULL;
-        _cleanup_free_ char *s = NULL;
         const char *fstype = NULL, *uuid = NULL;
         sd_id128_t id;
         int r;
@@ -167,13 +167,10 @@ static int probe_file_system_by_fd(
         if (r < 0)
                 return r;
 
-        s = strdup(fstype);
-        if (!s)
-                return -ENOMEM;
-
-        *ret_fstype = TAKE_PTR(s);
+        r = strdup_to(ret_fstype, fstype);
+        if (r < 0)
+                return r;
         *ret_uuid = id;
-
         return 0;
 }
 
@@ -199,7 +196,7 @@ static int block_get_size_by_fd(int fd, uint64_t *ret) {
         if (!S_ISBLK(st.st_mode))
                 return -ENOTBLK;
 
-        return RET_NERRNO(ioctl(fd, BLKGETSIZE64, ret));
+        return blockdev_get_device_size(fd, ret);
 }
 
 static int block_get_size_by_path(const char *path, uint64_t *ret) {
@@ -228,7 +225,7 @@ static int run_fsck(const char *node, const char *fstype) {
         }
 
         r = safe_fork("(fsck)",
-                      FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG|FORK_LOG|FORK_STDOUT_TO_STDERR|FORK_CLOSE_ALL_FDS,
+                      FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_STDOUT_TO_STDERR|FORK_CLOSE_ALL_FDS,
                       &fsck_pid);
         if (r < 0)
                 return r;
@@ -259,43 +256,30 @@ static int run_fsck(const char *node, const char *fstype) {
 
 DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(key_serial_t, keyring_unlink, -1);
 
-static int upload_to_keyring(
-                UserRecord *h,
-                const char *password,
-                key_serial_t *ret_key_serial) {
+static int upload_to_keyring(UserRecord *h, const void *vk, size_t vks, key_serial_t *ret) {
 
         _cleanup_free_ char *name = NULL;
         key_serial_t serial;
 
         assert(h);
-        assert(password);
-
-        /* If auto-shrink-on-logout is turned on, we need to keep the key we used to unlock the LUKS volume
-         * around, since we'll need it when automatically resizing (since we can't ask the user there
-         * again). We do this by uploading it into the kernel keyring, specifically the "session" one. This
-         * is done under the assumption systemd-homed gets its private per-session keyring (i.e. default
-         * service behaviour, given that KeyringMode=private is the default). It will survive between our
-         * systemd-homework invocations that way.
-         *
-         * If auto-shrink-on-logout is disabled we'll skip this step, to be frugal with sensitive data. */
-
-        if (user_record_auto_resize_mode(h) != AUTO_RESIZE_SHRINK_AND_GROW) {  /* Won't need it */
-                if (ret_key_serial)
-                        *ret_key_serial = -1;
-                return 0;
-        }
+        assert(vk);
+        assert(vks > 0);
+
+        /* We upload the LUKS volume key into the kernel session keyring, under the assumption that
+         * systemd-homed gets its own private session keyring (i.e. the default service behavior, given
+         * that KeyringMode=private is the default). That way, the key will survive between invocations
+         * of systemd-homework. */
 
         name = strjoin("homework-user-", h->user_name);
         if (!name)
                 return -ENOMEM;
 
-        serial = add_key("user", name, password, strlen(password), KEY_SPEC_SESSION_KEYRING);
+        serial = add_key("user", name, vk, vks, KEY_SPEC_SESSION_KEYRING);
         if (serial == -1)
                 return -errno;
 
-        if (ret_key_serial)
-                *ret_key_serial = serial;
-
+        if (ret)
+                *ret = serial;
         return 1;
 }
 
@@ -304,13 +288,14 @@ static int luks_try_passwords(
                 struct crypt_device *cd,
                 char **passwords,
                 void *volume_key,
-                size_t *volume_key_size,
-                key_serial_t *ret_key_serial) {
+                size_t *volume_key_size) {
 
         int r;
 
         assert(h);
         assert(cd);
+        assert(volume_key);
+        assert(volume_key_size);
 
         STRV_FOREACH(pp, passwords) {
                 size_t vks = *volume_key_size;
@@ -323,16 +308,6 @@ static int luks_try_passwords(
                                 *pp,
                                 strlen(*pp));
                 if (r >= 0) {
-                        if (ret_key_serial) {
-                                /* If ret_key_serial is non-NULL, let's try to upload the password that
-                                 * worked, and return its serial. */
-                                r = upload_to_keyring(h, *pp, ret_key_serial);
-                                if (r < 0) {
-                                        log_debug_errno(r, "Failed to upload LUKS password to kernel keyring, ignoring: %m");
-                                        *ret_key_serial = -1;
-                                }
-                        }
-
                         *volume_key_size = vks;
                         return 0;
                 }
@@ -343,6 +318,66 @@ static int luks_try_passwords(
         return -ENOKEY;
 }
 
+static int luks_get_volume_key(
+                UserRecord *h,
+                struct crypt_device *cd,
+                const PasswordCache *cache,
+                void *volume_key,
+                size_t *volume_key_size,
+                key_serial_t *ret_key_serial) {
+
+        char **list;
+        size_t vks;
+        int r;
+
+        assert(h);
+        assert(cd);
+        assert(volume_key);
+        assert(volume_key_size);
+
+        if (cache && cache->volume_key) {
+                /* Shortcut: If volume key was loaded from the keyring then just use it */
+                if (cache->volume_key_size > *volume_key_size)
+                        return log_error_errno(SYNTHETIC_ERRNO(ENOBUFS),
+                                               "LUKS volume key from kernel keyring too big for buffer (need %zu bytes, have %zu)",
+                                               cache->volume_key_size, *volume_key_size);
+                memcpy(volume_key, cache->volume_key, cache->volume_key_size);
+                *volume_key_size = cache->volume_key_size;
+                if (ret_key_serial)
+                        *ret_key_serial = -1; /* Key came from keyring. No need to re-upload it */
+                return 0;
+        }
+
+        vks = *volume_key_size;
+
+        FOREACH_ARGUMENT(list,
+                         cache ? cache->pkcs11_passwords : NULL,
+                         cache ? cache->fido2_passwords : NULL,
+                         h->password) {
+
+                r = luks_try_passwords(h, cd, list, volume_key, &vks);
+                if (r == -ENOKEY)
+                        continue;
+                if (r < 0)
+                        return r;
+
+                /* We got a volume key! */
+
+                if (ret_key_serial) {
+                        r = upload_to_keyring(h, volume_key, vks, ret_key_serial);
+                        if (r < 0) {
+                                log_warning_errno(r, "Failed to upload LUKS volume key to kernel keyring, ignoring: %m");
+                                *ret_key_serial = -1;
+                        }
+                }
+
+                *volume_key_size = vks;
+                return 0;
+        }
+
+        return -ENOKEY;
+}
+
 static int luks_setup(
                 UserRecord *h,
                 const char *node,
@@ -351,7 +386,6 @@ static int luks_setup(
                 const char *cipher,
                 const char *cipher_mode,
                 uint64_t volume_key_size,
-                char **passwords,
                 const PasswordCache *cache,
                 bool discard,
                 struct crypt_device **ret,
@@ -365,7 +399,6 @@ static int luks_setup(
         _cleanup_(erase_and_freep) void *vk = NULL;
         sd_id128_t p;
         size_t vks;
-        char **list;
         int r;
 
         assert(h);
@@ -418,16 +451,7 @@ static int luks_setup(
         if (!vk)
                 return log_oom();
 
-        r = -ENOKEY;
-        FOREACH_POINTER(list,
-                        cache ? cache->keyring_passswords : NULL,
-                        cache ? cache->pkcs11_passwords : NULL,
-                        cache ? cache->fido2_passwords : NULL,
-                        passwords) {
-                r = luks_try_passwords(h, cd, list, vk, &vks, ret_key_serial ? &key_serial : NULL);
-                if (r != -ENOKEY)
-                        break;
-        }
+        r = luks_get_volume_key(h, cd, cache, vk, &vks, ret_key_serial ? &key_serial : NULL);
         if (r == -ENOKEY)
                 return log_error_errno(r, "No valid password for LUKS superblock.");
         if (r < 0)
@@ -498,7 +522,7 @@ static int acquire_open_luks_device(
                 return r;
 
         r = sym_crypt_init_by_name(&cd, setup->dm_name);
-        if ((ERRNO_IS_DEVICE_ABSENT(r) || r == -EINVAL) && graceful)
+        if ((ERRNO_IS_NEG_DEVICE_ABSENT(r) || r == -EINVAL) && graceful)
                 return 0;
         if (r < 0)
                 return log_error_errno(r, "Failed to initialize cryptsetup context for %s: %m", setup->dm_name);
@@ -519,7 +543,6 @@ static int luks_open(
 
         _cleanup_(erase_and_freep) void *vk = NULL;
         sd_id128_t p;
-        char **list;
         size_t vks;
         int r;
 
@@ -559,20 +582,11 @@ static int luks_open(
         if (!vk)
                 return log_oom();
 
-        r = -ENOKEY;
-        FOREACH_POINTER(list,
-                        cache ? cache->keyring_passswords : NULL,
-                        cache ? cache->pkcs11_passwords : NULL,
-                        cache ? cache->fido2_passwords : NULL,
-                        h->password) {
-                r = luks_try_passwords(h, setup->crypt_device, list, vk, &vks, NULL);
-                if (r != -ENOKEY)
-                        break;
-        }
+        r = luks_get_volume_key(h, setup->crypt_device, cache, vk, &vks, NULL);
         if (r == -ENOKEY)
                 return log_error_errno(r, "No valid password for LUKS superblock.");
         if (r < 0)
-                return log_error_errno(r, "Failed to unlocks LUKS superblock: %m");
+                return log_error_errno(r, "Failed to unlock LUKS superblock: %m");
 
         log_info("Discovered used LUKS device /dev/mapper/%s, and validated password.", setup->dm_name);
 
@@ -601,7 +615,7 @@ static int fs_validate(
                 sd_id128_t *ret_found_uuid) {
 
         _cleanup_free_ char *fstype = NULL;
-        sd_id128_t u;
+        sd_id128_t u = SD_ID128_NULL; /* avoid false maybe-unitialized warning */
         int r;
 
         assert(dm_node);
@@ -1295,9 +1309,6 @@ int home_setup_luks(
                         if (!IN_SET(errno, ENOTTY, EINVAL))
                                 return log_error_errno(errno, "Failed to get block device metrics of %s: %m", n);
 
-                        if (ioctl(setup->loop->fd, BLKGETSIZE64, &size) < 0)
-                                return log_error_errno(r, "Failed to read block device size of %s: %m", n);
-
                         if (fstat(setup->loop->fd, &st) < 0)
                                 return log_error_errno(r, "Failed to stat block device %s: %m", n);
                         assert(S_ISBLK(st.st_mode));
@@ -1329,6 +1340,8 @@ int home_setup_luks(
 
                                 offset *= 512U;
                         }
+
+                        size = setup->loop->device_size;
                 } else {
 #if HAVE_VALGRIND_MEMCHECK_H
                         VALGRIND_MAKE_MEM_DEFINED(&info, sizeof(info));
@@ -1403,7 +1416,6 @@ int home_setup_luks(
                                h->luks_cipher,
                                h->luks_cipher_mode,
                                h->luks_volume_key_size,
-                               h->password,
                                cache,
                                user_record_luks_discard(h) || user_record_luks_offline_discard(h),
                                &setup->crypt_device,
@@ -1638,7 +1650,7 @@ int home_deactivate_luks(UserRecord *h, HomeSetup *setup) {
                 cryptsetup_enable_logging(setup->crypt_device);
 
                 r = sym_crypt_deactivate_by_name(setup->crypt_device, setup->dm_name, 0);
-                if (ERRNO_IS_DEVICE_ABSENT(r) || r == -EINVAL)
+                if (ERRNO_IS_NEG_DEVICE_ABSENT(r) || r == -EINVAL)
                         log_debug_errno(r, "LUKS device %s is already detached.", setup->dm_node);
                 else if (r < 0)
                         return log_info_errno(r, "LUKS device %s couldn't be deactivated: %m", setup->dm_node);
@@ -1698,12 +1710,13 @@ static struct crypt_pbkdf_type* build_minimal_pbkdf(struct crypt_pbkdf_type *buf
         assert(hr);
 
         /* For PKCS#11 derived keys (which are generated randomly and are of high quality already) we use a
-         * minimal PBKDF */
+         * minimal PBKDF and CRYPT_PBKDF_NO_BENCHMARK flag to skip benchmark. */
         *buffer = (struct crypt_pbkdf_type) {
                 .hash = user_record_luks_pbkdf_hash_algorithm(hr),
                 .type = CRYPT_KDF_PBKDF2,
-                .iterations = 1,
-                .time_ms = 1,
+                .iterations = 1000, /* recommended minimum count for pbkdf2
+                                     * according to NIST SP 800-132, ch. 5.2 */
+                .flags = CRYPT_PBKDF_NO_BENCHMARK
         };
 
         return buffer;
@@ -1868,7 +1881,7 @@ static int make_partition_table(
         if (r < 0)
                 return log_error_errno(r, "Failed to initialize partition type: %m");
 
-        r = fdisk_new_context_fd(fd, /* read_only= */ false, sector_size, &c);
+        r = fdisk_new_context_at(fd, /* path= */ NULL, /* read_only= */ false, sector_size, &c);
         if (r < 0)
                 return log_error_errno(r, "Failed to open device: %m");
 
@@ -2024,11 +2037,10 @@ static int wait_for_devlink(const char *path) {
                         return log_error_errno(SYNTHETIC_ERRNO(ETIMEDOUT), "Device link %s still hasn't shown up, giving up.", path);
 
                 r = fd_wait_for_event(inotify_fd, POLLIN, until - w);
-                if (r < 0) {
-                        if (ERRNO_IS_TRANSIENT(r))
-                                continue;
+                if (ERRNO_IS_NEG_TRANSIENT(r))
+                        continue;
+                if (r < 0)
                         return log_error_errno(r, "Failed to watch inotify: %m");
-                }
 
                 (void) flush_fd(inotify_fd);
         }
@@ -2208,7 +2220,7 @@ int home_create_luks(
                 uint64_t block_device_size;
                 struct stat st;
 
-                /* Let's place the home directory on a real device, i.e. an USB stick or such */
+                /* Let's place the home directory on a real device, i.e. a USB stick or such */
 
                 setup->image_fd = open_image_file(h, ip, &st);
                 if (setup->image_fd < 0)
@@ -2228,8 +2240,9 @@ int home_create_luks(
                 if (flock(setup->image_fd, LOCK_EX) < 0) /* make sure udev doesn't read from it while we operate on the device */
                         return log_error_errno(errno, "Failed to lock block device %s: %m", ip);
 
-                if (ioctl(setup->image_fd, BLKGETSIZE64, &block_device_size) < 0)
-                        return log_error_errno(errno, "Failed to read block device size: %m");
+                r = blockdev_get_device_size(setup->image_fd, &block_device_size);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to read block device size: %m");
 
                 if (h->disk_size == UINT64_MAX) {
 
@@ -2381,7 +2394,7 @@ int home_create_luks(
                 return log_oom();
 
         /* Prefer using a btrfs subvolume if we can, fall back to directory otherwise */
-        r = btrfs_subvol_make_fallback(subdir, 0700);
+        r = btrfs_subvol_make_fallback(AT_FDCWD, subdir, 0700);
         if (r < 0)
                 return log_error_errno(r, "Failed to create user directory in mounted image file: %m");
 
@@ -2597,7 +2610,7 @@ static int ext4_offline_resize_fs(
 
         /* resize2fs requires that the file system is force checked first, do so. */
         r = safe_fork("(e2fsck)",
-                      FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG|FORK_LOG|FORK_STDOUT_TO_STDERR|FORK_CLOSE_ALL_FDS,
+                      FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_STDOUT_TO_STDERR|FORK_CLOSE_ALL_FDS,
                       &fsck_pid);
         if (r < 0)
                 return r;
@@ -2629,7 +2642,7 @@ static int ext4_offline_resize_fs(
 
         /* Resize the thing */
         r = safe_fork("(e2resize)",
-                      FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG|FORK_LOG|FORK_WAIT|FORK_STDOUT_TO_STDERR|FORK_CLOSE_ALL_FDS,
+                      FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_WAIT|FORK_STDOUT_TO_STDERR|FORK_CLOSE_ALL_FDS,
                       &resize_pid);
         if (r < 0)
                 return r;
@@ -2696,7 +2709,7 @@ static int prepare_resize_partition(
                 return 0;
         }
 
-        r = fdisk_new_context_fd(fd, /* read_only= */ false, UINT32_MAX, &c);
+        r = fdisk_new_context_at(fd, /* path= */ NULL, /* read_only= */ false, UINT32_MAX, &c);
         if (r < 0)
                 return log_error_errno(r, "Failed to open device: %m");
 
@@ -2762,13 +2775,9 @@ static int get_maximum_partition_size(
         assert(p);
         assert(ret_maximum_partition_size);
 
-        c = fdisk_new_context();
-        if (!c)
-                return log_oom();
-
-        r = fdisk_assign_device(c, FORMAT_PROC_FD_PATH(fd), 0);
+        r = fdisk_new_context_at(fd, /* path= */ NULL, /* read_only= */ true, /* sector_size= */ UINT32_MAX, &c);
         if (r < 0)
-                return log_error_errno(r, "Failed to open device: %m");
+                return log_error_errno(r, "Failed to create fdisk context: %m");
 
         start_lba = fdisk_partition_get_start(p);
         assert(start_lba <= UINT64_MAX/512);
@@ -2853,7 +2862,7 @@ static int apply_resize_partition(
         if ((size_t) n != ssz * 2)
                 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short write while wiping partition table.");
 
-        r = fdisk_new_context_fd(fd, /* read_only= */ false, ssz, &c);
+        r = fdisk_new_context_at(fd, /* path= */ NULL, /* read_only= */ false, ssz, &c);
         if (r < 0)
                 return log_error_errno(r, "Failed to open device: %m");
 
@@ -3131,7 +3140,7 @@ int home_resize_luks(
         struct fdisk_partition *partition = NULL;
         _cleanup_close_ int opened_image_fd = -EBADF;
         _cleanup_free_ char *whole_disk = NULL;
-        int r, resize_type, image_fd = -EBADF;
+        int r, resize_type, image_fd = -EBADF, reconciled = USER_RECONCILE_IDENTICAL;
         sd_id128_t disk_uuid;
         const char *ip, *ipo;
         struct statfs sfs;
@@ -3188,8 +3197,9 @@ int home_resize_luks(
                 } else
                         log_info("Operating on whole block device %s.", ip);
 
-                if (ioctl(image_fd, BLKGETSIZE64, &old_image_size) < 0)
-                        return log_error_errno(errno, "Failed to determine size of original block device: %m");
+                r = blockdev_get_device_size(image_fd, &old_image_size);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to determine size of original block device: %m");
 
                 if (flock(image_fd, LOCK_EX) < 0) /* make sure udev doesn't read from it while we operate on the device */
                         return log_error_errno(errno, "Failed to lock block device %s: %m", ip);
@@ -3202,7 +3212,7 @@ int home_resize_luks(
 
                 old_image_size = st.st_size;
 
-                /* Note an asymetry here: when we operate on loopback files the specified disk size we get we
+                /* Note an asymmetry here: when we operate on loopback files the specified disk size we get we
                  * apply onto the loopback file as a whole. When we operate on block devices we instead apply
                  * to the partition itself only. */
 
@@ -3236,9 +3246,9 @@ int home_resize_luks(
                 return r;
 
         if (!FLAGS_SET(flags, HOME_SETUP_RESIZE_DONT_SYNC_IDENTITIES)) {
-                r = home_load_embedded_identity(h, setup->root_fd, header_home, USER_RECONCILE_REQUIRE_NEWER_OR_EQUAL, cache, &embedded_home, &new_home);
-                if (r < 0)
-                        return r;
+                reconciled = home_load_embedded_identity(h, setup->root_fd, header_home, USER_RECONCILE_REQUIRE_NEWER_OR_EQUAL, cache, &embedded_home, &new_home);
+                if (reconciled < 0)
+                        return reconciled;
         }
 
         r = home_maybe_shift_uid(h, flags, setup);
@@ -3449,7 +3459,11 @@ int home_resize_luks(
                 /* → Shrink */
 
                 if (!FLAGS_SET(flags, HOME_SETUP_RESIZE_DONT_SYNC_IDENTITIES)) {
-                        r = home_store_embedded_identity(new_home, setup->root_fd, h->uid, embedded_home);
+                        r = home_store_embedded_identity(new_home, setup->root_fd, embedded_home);
+                        if (r < 0)
+                                return r;
+
+                        r = home_reconcile_blob_dirs(new_home, setup->root_fd, reconciled);
                         if (r < 0)
                                 return r;
                 }
@@ -3537,7 +3551,11 @@ int home_resize_luks(
 
         } else { /* → Grow */
                 if (!FLAGS_SET(flags, HOME_SETUP_RESIZE_DONT_SYNC_IDENTITIES)) {
-                        r = home_store_embedded_identity(new_home, setup->root_fd, h->uid, embedded_home);
+                        r = home_store_embedded_identity(new_home, setup->root_fd, embedded_home);
+                        if (r < 0)
+                                return r;
+
+                        r = home_reconcile_blob_dirs(new_home, setup->root_fd, reconciled);
                         if (r < 0)
                                 return r;
                 }
@@ -3587,7 +3605,6 @@ int home_passwd_luks(
         _cleanup_(erase_and_freep) void *volume_key = NULL;
         struct crypt_pbkdf_type good_pbkdf, minimal_pbkdf;
         const char *type;
-        char **list;
         int r;
 
         assert(h);
@@ -3616,21 +3633,11 @@ int home_passwd_luks(
         if (!volume_key)
                 return log_oom();
 
-        r = -ENOKEY;
-        FOREACH_POINTER(list,
-                        cache ? cache->keyring_passswords : NULL,
-                        cache ? cache->pkcs11_passwords : NULL,
-                        cache ? cache->fido2_passwords : NULL,
-                        h->password) {
-
-                r = luks_try_passwords(h, setup->crypt_device, list, volume_key, &volume_key_size, NULL);
-                if (r != -ENOKEY)
-                        break;
-        }
+        r = luks_get_volume_key(h, setup->crypt_device, cache, volume_key, &volume_key_size, NULL);
         if (r == -ENOKEY)
                 return log_error_errno(SYNTHETIC_ERRNO(ENOKEY), "Failed to unlock LUKS superblock with supplied passwords.");
         if (r < 0)
-                return log_error_errno(r, "Failed to unlocks LUKS superblock: %m");
+                return log_error_errno(r, "Failed to unlock LUKS superblock: %m");
 
         n_effective = strv_length(effective_passwords);
 
@@ -3669,11 +3676,6 @@ int home_passwd_luks(
                         return log_error_errno(r, "Failed to set up LUKS password: %m");
 
                 log_info("Updated LUKS key slot %zu.", i);
-
-                /* If we changed the password, then make sure to update the copy in the keyring, so that
-                 * auto-rebalance continues to work. We only do this if we operate on an active home dir. */
-                if (i == 0 && FLAGS_SET(flags, HOME_SETUP_ALREADY_ACTIVATED))
-                        upload_to_keyring(h, effective_passwords[i], NULL);
         }
 
         return 1;
@@ -3711,36 +3713,10 @@ int home_lock_luks(UserRecord *h, HomeSetup *setup) {
         return 0;
 }
 
-static int luks_try_resume(
-                struct crypt_device *cd,
-                const char *dm_name,
-                char **password) {
-
-        int r;
-
-        assert(cd);
-        assert(dm_name);
-
-        STRV_FOREACH(pp, password) {
-                r = sym_crypt_resume_by_passphrase(
-                                cd,
-                                dm_name,
-                                CRYPT_ANY_SLOT,
-                                *pp,
-                                strlen(*pp));
-                if (r >= 0) {
-                        log_info("Resumed LUKS device %s.", dm_name);
-                        return 0;
-                }
-
-                log_debug_errno(r, "Password %zu didn't work for resuming device: %m", (size_t) (pp - password));
-        }
-
-        return -ENOKEY;
-}
-
 int home_unlock_luks(UserRecord *h, HomeSetup *setup, const PasswordCache *cache) {
-        char **list;
+        _cleanup_(keyring_unlinkp) key_serial_t key_serial = -1;
+        _cleanup_(erase_and_freep) void *vk = NULL;
+        size_t vks;
         int r;
 
         assert(h);
@@ -3753,20 +3729,27 @@ int home_unlock_luks(UserRecord *h, HomeSetup *setup, const PasswordCache *cache
 
         log_info("Discovered used LUKS device %s.", setup->dm_node);
 
-        r = -ENOKEY;
-        FOREACH_POINTER(list,
-                        cache ? cache->pkcs11_passwords : NULL,
-                        cache ? cache->fido2_passwords : NULL,
-                        h->password) {
-                r = luks_try_resume(setup->crypt_device, setup->dm_name, list);
-                if (r != -ENOKEY)
-                        break;
-        }
+        r = sym_crypt_get_volume_key_size(setup->crypt_device);
+        if (r <= 0)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to determine LUKS volume key size");
+        vks = (size_t) r;
+
+        vk = malloc(vks);
+        if (!vk)
+                return log_oom();
+
+        r = luks_get_volume_key(h, setup->crypt_device, cache, vk, &vks, &key_serial);
         if (r == -ENOKEY)
                 return log_error_errno(r, "No valid password for LUKS superblock.");
+        if (r < 0)
+                return log_error_errno(r, "Failed to unlock LUKS superblock: %m");
+
+        r = sym_crypt_resume_by_volume_key(setup->crypt_device, setup->dm_name, vk, vks);
         if (r < 0)
                 return log_error_errno(r, "Failed to resume LUKS superblock: %m");
 
+        TAKE_KEY_SERIAL(key_serial); /* Leave key in kernel keyring */
+
         log_info("LUKS device resumed.");
         return 0;
 }