]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/home/homework-luks.c
Merge pull request #21034 from poettering/homed-password-cache-tweaks
[thirdparty/systemd.git] / src / home / homework-luks.c
index b0b2d8029fb3517c5f61d51dde6eb76496a1ca1f..05e2950b4b445741c217cd62d4b00e49d4176146 100644 (file)
@@ -8,11 +8,18 @@
 #include <sys/mount.h>
 #include <sys/xattr.h>
 
+#if HAVE_VALGRIND_MEMCHECK_H
+#include <valgrind/memcheck.h>
+#endif
+
+#include "sd-daemon.h"
+
 #include "blkid-util.h"
 #include "blockdev-util.h"
 #include "btrfs-util.h"
 #include "chattr-util.h"
 #include "dm-util.h"
+#include "env-util.h"
 #include "errno-util.h"
 #include "fd-util.h"
 #include "fileio.h"
 #include "resize-fs.h"
 #include "stat-util.h"
 #include "strv.h"
+#include "sync-util.h"
 #include "tmpfile-util.h"
 
-/* Round down to the nearest 1K size. Note that Linux generally handles block devices with 512 blocks only,
- * but actually doesn't accept uneven numbers in many cases. To avoid any confusion around this we'll
- * strictly round disk sizes down to the next 1K boundary.*/
-#define DISK_SIZE_ROUND_DOWN(x) ((x) & ~UINT64_C(1023))
+/* Round down to the nearest 4K size. Given that newer hardware generally prefers 4K sectors, let's align our
+ * partitions to that too. In the worst case we'll waste 3.5K per partition that way, but I think I can live
+ * with that. */
+#define DISK_SIZE_ROUND_DOWN(x) ((x) & ~UINT64_C(4095))
+
+/* Rounds up to the nearest 4K boundary. Returns UINT64_MAX on overflow */
+#define DISK_SIZE_ROUND_UP(x)                                           \
+        ({                                                              \
+                uint64_t _x = (x);                                      \
+                _x > UINT64_MAX - 4095U ? UINT64_MAX : (_x + 4095U) & ~UINT64_C(4095); \
+        })
+
 
 int run_mark_dirty(int fd, bool b) {
         char x = '1';
@@ -199,12 +215,15 @@ static int run_fsck(const char *node, const char *fstype) {
                 return 0;
         }
 
-        r = safe_fork("(fsck)", FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG|FORK_LOG|FORK_STDOUT_TO_STDERR, &fsck_pid);
+        r = safe_fork("(fsck)",
+                      FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG|FORK_LOG|FORK_STDOUT_TO_STDERR|FORK_CLOSE_ALL_FDS,
+                      &fsck_pid);
         if (r < 0)
                 return r;
         if (r == 0) {
                 /* Child */
                 execl("/sbin/fsck", "/sbin/fsck", "-aTl", node, NULL);
+                log_open();
                 log_error_errno(errno, "Failed to execute fsck: %m");
                 _exit(FSCK_OPERATIONAL_ERROR);
         }
@@ -240,7 +259,7 @@ static int luks_try_passwords(
         STRV_FOREACH(pp, passwords) {
                 size_t vks = *volume_key_size;
 
-                r = crypt_volume_key_get(
+                r = sym_crypt_volume_key_get(
                                 cd,
                                 CRYPT_ANY_SLOT,
                                 volume_key,
@@ -273,7 +292,7 @@ static int luks_setup(
                 void **ret_volume_key,
                 size_t *ret_volume_key_size) {
 
-        _cleanup_(crypt_freep) struct crypt_device *cd = NULL;
+        _cleanup_(sym_crypt_freep) struct crypt_device *cd = NULL;
         _cleanup_(erase_and_freep) void *vk = NULL;
         sd_id128_t p;
         size_t vks;
@@ -284,17 +303,17 @@ static int luks_setup(
         assert(dm_name);
         assert(ret);
 
-        r = crypt_init(&cd, node);
+        r = sym_crypt_init(&cd, node);
         if (r < 0)
                 return log_error_errno(r, "Failed to allocate libcryptsetup context: %m");
 
         cryptsetup_enable_logging(cd);
 
-        r = crypt_load(cd, CRYPT_LUKS2, NULL);
+        r = sym_crypt_load(cd, CRYPT_LUKS2, NULL);
         if (r < 0)
                 return log_error_errno(r, "Failed to load LUKS superblock: %m");
 
-        r = crypt_get_volume_key_size(cd);
+        r = sym_crypt_get_volume_key_size(cd);
         if (r <= 0)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to determine LUKS volume key size");
         vks = (size_t) r;
@@ -302,7 +321,7 @@ static int luks_setup(
         if (!sd_id128_is_null(uuid) || ret_found_uuid) {
                 const char *s;
 
-                s = crypt_get_uuid(cd);
+                s = sym_crypt_get_uuid(cd);
                 if (!s)
                         return log_error_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE), "LUKS superblock has no UUID.");
 
@@ -316,10 +335,10 @@ static int luks_setup(
                         return log_error_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE), "LUKS superblock has wrong UUID.");
         }
 
-        if (cipher && !streq_ptr(cipher, crypt_get_cipher(cd)))
+        if (cipher && !streq_ptr(cipher, sym_crypt_get_cipher(cd)))
                 return log_error_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE), "LUKS superblock declares wrong cipher.");
 
-        if (cipher_mode && !streq_ptr(cipher_mode, crypt_get_cipher_mode(cd)))
+        if (cipher_mode && !streq_ptr(cipher_mode, sym_crypt_get_cipher_mode(cd)))
                 return log_error_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE), "LUKS superblock declares wrong cipher mode.");
 
         if (volume_key_size != UINT64_MAX && vks != volume_key_size)
@@ -330,7 +349,10 @@ static int luks_setup(
                 return log_oom();
 
         r = -ENOKEY;
-        FOREACH_POINTER(list, cache->pkcs11_passwords, cache->fido2_passwords, passwords) {
+        FOREACH_POINTER(list,
+                        cache ? cache->pkcs11_passwords : NULL,
+                        cache ? cache->fido2_passwords : NULL,
+                        passwords) {
                 r = luks_try_passwords(cd, list, vk, &vks);
                 if (r != -ENOKEY)
                         break;
@@ -340,7 +362,7 @@ static int luks_setup(
         if (r < 0)
                 return log_error_errno(r, "Failed to unlocks LUKS superblock: %m");
 
-        r = crypt_activate_by_volume_key(
+        r = sym_crypt_activate_by_volume_key(
                         cd,
                         dm_name,
                         vk, vks,
@@ -365,13 +387,13 @@ static int luks_setup(
 static int luks_open(
                 const char *dm_name,
                 char **passwords,
-                PasswordCache *cache,
+                const PasswordCache *cache,
                 struct crypt_device **ret,
                 sd_id128_t *ret_found_uuid,
                 void **ret_volume_key,
                 size_t *ret_volume_key_size) {
 
-        _cleanup_(crypt_freep) struct crypt_device *cd = NULL;
+        _cleanup_(sym_crypt_freep) struct crypt_device *cd = NULL;
         _cleanup_(erase_and_freep) void *vk = NULL;
         sd_id128_t p;
         char **list;
@@ -384,17 +406,17 @@ static int luks_open(
         /* Opens a LUKS device that is already set up. Re-validates the password while doing so (which also
          * provides us with the volume key, which we want). */
 
-        r = crypt_init_by_name(&cd, dm_name);
+        r = sym_crypt_init_by_name(&cd, dm_name);
         if (r < 0)
                 return log_error_errno(r, "Failed to initialize cryptsetup context for %s: %m", dm_name);
 
         cryptsetup_enable_logging(cd);
 
-        r = crypt_load(cd, CRYPT_LUKS2, NULL);
+        r = sym_crypt_load(cd, CRYPT_LUKS2, NULL);
         if (r < 0)
                 return log_error_errno(r, "Failed to load LUKS superblock: %m");
 
-        r = crypt_get_volume_key_size(cd);
+        r = sym_crypt_get_volume_key_size(cd);
         if (r <= 0)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to determine LUKS volume key size");
         vks = (size_t) r;
@@ -402,7 +424,7 @@ static int luks_open(
         if (ret_found_uuid) {
                 const char *s;
 
-                s = crypt_get_uuid(cd);
+                s = sym_crypt_get_uuid(cd);
                 if (!s)
                         return log_error_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE), "LUKS superblock has no UUID.");
 
@@ -416,7 +438,10 @@ static int luks_open(
                 return log_oom();
 
         r = -ENOKEY;
-        FOREACH_POINTER(list, cache->pkcs11_passwords, cache->fido2_passwords, passwords) {
+        FOREACH_POINTER(list,
+                        cache ? cache->pkcs11_passwords : NULL,
+                        cache ? cache->fido2_passwords : NULL,
+                        passwords) {
                 r = luks_try_passwords(cd, list, vk, &vks);
                 if (r != -ENOKEY)
                         break;
@@ -430,7 +455,7 @@ static int luks_open(
 
         /* This is needed so that crypt_resize() can operate correctly for pre-existing LUKS devices. We need
          * to tell libcryptsetup the volume key explicitly, so that it is in the kernel keyring. */
-        r = crypt_activate_by_volume_key(cd, NULL, vk, vks, CRYPT_ACTIVATE_KEYRING_KEY);
+        r = sym_crypt_activate_by_volume_key(cd, NULL, vk, vks, CRYPT_ACTIVATE_KEYRING_KEY);
         if (r < 0)
                 return log_error_errno(r, "Failed to upload volume key again: %m");
 
@@ -518,7 +543,7 @@ static int luks_validate(
         blkid_loff_t offset = 0, size = 0;
         blkid_partlist pl;
         bool found = false;
-        int r, i, n;
+        int r, n;
 
         assert(fd >= 0);
         assert(label);
@@ -570,9 +595,9 @@ static int luks_validate(
         if (n < 0)
                 return errno > 0 ? -errno : -EIO;
 
-        for (i = 0; i < n; i++) {
+        for (int i = 0; i < n; i++) {
                 blkid_partition pp;
-                sd_id128_t id;
+                sd_id128_t id = SD_ID128_NULL;
                 const char *sid;
 
                 errno = 0;
@@ -637,19 +662,19 @@ static int crypt_device_to_evp_cipher(struct crypt_device *cd, const EVP_CIPHER
         /* Let's find the right OpenSSL EVP_CIPHER object that matches the encryption settings of the LUKS
          * device */
 
-        cipher = crypt_get_cipher(cd);
+        cipher = sym_crypt_get_cipher(cd);
         if (!cipher)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot get cipher from LUKS device.");
 
-        cipher_mode = crypt_get_cipher_mode(cd);
+        cipher_mode = sym_crypt_get_cipher_mode(cd);
         if (!cipher_mode)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot get cipher mode from LUKS device.");
 
         e = strchr(cipher_mode, '-');
         if (e)
-                cipher_mode = strndupa(cipher_mode, e - cipher_mode);
+                cipher_mode = strndupa_safe(cipher_mode, e - cipher_mode);
 
-        r = crypt_get_volume_key_size(cd);
+        r = sym_crypt_get_volume_key_size(cd);
         if (r <= 0)
                 return log_error_errno(r < 0 ? r : SYNTHETIC_ERRNO(EINVAL), "Cannot get volume key size from LUKS device.");
 
@@ -681,12 +706,12 @@ static int luks_validate_home_record(
                 PasswordCache *cache,
                 UserRecord **ret_luks_home_record) {
 
-        int r, token;
+        int r;
 
         assert(cd);
         assert(h);
 
-        for (token = 0;; token++) {
+        for (int token = 0; token < sym_crypt_token_max(CRYPT_LUKS2); token++) {
                 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *rr = NULL;
                 _cleanup_(EVP_CIPHER_CTX_freep) EVP_CIPHER_CTX *context = NULL;
                 _cleanup_(user_record_unrefp) UserRecord *lhr = NULL;
@@ -700,7 +725,7 @@ static int luks_validate_home_record(
                 unsigned line, column;
                 const EVP_CIPHER *cc;
 
-                state = crypt_token_status(cd, token, &type);
+                state = sym_crypt_token_status(cd, token, &type);
                 if (state == CRYPT_TOKEN_INACTIVE) /* First unconfigured token, give up */
                         break;
                 if (IN_SET(state, CRYPT_TOKEN_INTERNAL, CRYPT_TOKEN_INTERNAL_UNKNOWN, CRYPT_TOKEN_EXTERNAL))
@@ -711,7 +736,7 @@ static int luks_validate_home_record(
                 if (!streq(type, "systemd-homed"))
                         continue;
 
-                r = crypt_token_json_get(cd, token, &text);
+                r = sym_crypt_token_json_get(cd, token, &text);
                 if (r < 0)
                         return log_error_errno(r, "Failed to read LUKS token %i: %m", token);
 
@@ -776,7 +801,7 @@ static int luks_validate_home_record(
                 if (!lhr)
                         return log_oom();
 
-                r = user_record_load(lhr, rr, USER_RECORD_LOAD_EMBEDDED);
+                r = user_record_load(lhr, rr, USER_RECORD_LOAD_EMBEDDED|USER_RECORD_PERMISSIVE);
                 if (r < 0)
                         return log_error_errno(r, "Failed to parse user record: %m");
 
@@ -882,7 +907,7 @@ int home_store_header_identity_luks(
 
         _cleanup_(user_record_unrefp) UserRecord *header_home = NULL;
         _cleanup_free_ char *text = NULL;
-        int token = 0, r;
+        int r;
 
         assert(h);
 
@@ -899,7 +924,7 @@ int home_store_header_identity_luks(
          * the file system, so that we can validate it first, and only then mount the file system. To keep
          * things simple we use the same encryption settings for this record as for the file system itself. */
 
-        r = user_record_clone(h, USER_RECORD_EXTRACT_EMBEDDED, &header_home);
+        r = user_record_clone(h, USER_RECORD_EXTRACT_EMBEDDED|USER_RECORD_PERMISSIVE, &header_home);
         if (r < 0)
                 return log_error_errno(r, "Failed to determine new header record: %m");
 
@@ -912,11 +937,11 @@ int home_store_header_identity_luks(
         if (r < 0)
                 return r;
 
-        for (;; token++) {
+        for (int token = 0; token < sym_crypt_token_max(CRYPT_LUKS2); token++) {
                 crypt_token_info state;
                 const char *type;
 
-                state = crypt_token_status(setup->crypt_device, token, &type);
+                state = sym_crypt_token_status(setup->crypt_device, token, &type);
                 if (state == CRYPT_TOKEN_INACTIVE) /* First unconfigured token, we are done */
                         break;
                 if (IN_SET(state, CRYPT_TOKEN_INTERNAL, CRYPT_TOKEN_INTERNAL_UNKNOWN, CRYPT_TOKEN_EXTERNAL))
@@ -927,14 +952,13 @@ int home_store_header_identity_luks(
                 if (!streq(type, "systemd-homed"))
                         continue;
 
-                r = crypt_token_json_set(setup->crypt_device, token, text);
+                r = sym_crypt_token_json_set(setup->crypt_device, token, text);
                 if (r < 0)
                         return log_error_errno(r, "Failed to set JSON token for slot %i: %m", token);
 
                 /* Now, let's free the text so that for all further matching tokens we all crypt_json_token_set()
                  * with a NULL text in order to invalidate the tokens. */
                 text = mfree(text);
-                token++;
         }
 
         if (text)
@@ -946,7 +970,6 @@ int home_store_header_identity_luks(
 }
 
 int run_fitrim(int root_fd) {
-        char buf[FORMAT_BYTES_MAX];
         struct fstrim_range range = {
                 .len = UINT64_MAX,
         };
@@ -965,8 +988,7 @@ int run_fitrim(int root_fd) {
                 return log_warning_errno(errno, "Failed to invoke FITRIM, ignoring: %m");
         }
 
-        log_info("Discarded unused %s.",
-                 format_bytes(buf, sizeof(buf), range.len));
+        log_info("Discarded unused %s.", FORMAT_BYTES(range.len));
         return 1;
 }
 
@@ -981,7 +1003,6 @@ int run_fitrim_by_path(const char *root_path) {
 }
 
 int run_fallocate(int backing_fd, const struct stat *st) {
-        char buf[FORMAT_BYTES_MAX];
         struct stat stbuf;
 
         assert(backing_fd >= 0);
@@ -1020,7 +1041,7 @@ int run_fallocate(int backing_fd, const struct stat *st) {
         }
 
         log_info("Allocated additional %s.",
-                 format_bytes(buf, sizeof(buf), (DIV_ROUND_UP(st->st_size, 512) - st->st_blocks) * 512));
+                 FORMAT_BYTES((DIV_ROUND_UP(st->st_size, 512) - st->st_blocks) * 512));
         return 1;
 }
 
@@ -1034,9 +1055,91 @@ int run_fallocate_by_path(const char *backing_path) {
         return run_fallocate(backing_fd, NULL);
 }
 
-int home_prepare_luks(
+static int lock_image_fd(int image_fd, const char *ip) {
+        int r;
+
+        /* If the $SYSTEMD_LUKS_LOCK environment variable is set we'll take an exclusive BSD lock on the
+         * image file, and send it to our parent. homed will keep it open to ensure no other instance of
+         * homed (across the network or such) will also mount the file. */
+
+        r = getenv_bool("SYSTEMD_LUKS_LOCK");
+        if (r == -ENXIO)
+                return 0;
+        if (r < 0)
+                return log_error_errno(r, "Failed to parse $SYSTEMD_LUKS_LOCK environment variable: %m");
+        if (r > 0) {
+                struct stat st;
+
+                if (fstat(image_fd, &st) < 0)
+                        return log_error_errno(errno, "Failed to stat image file: %m");
+                if (S_ISBLK(st.st_mode)) {
+                        /* Locking block devices doesn't really make sense, as this might interfear with
+                         * udev's workings, and these locks aren't network propagated anyway, hence not what
+                         * we are after here. */
+                        log_debug("Not locking image file '%s', since it's a block device.", ip);
+                        return 0;
+                }
+                r = stat_verify_regular(&st);
+                if (r < 0)
+                        return log_error_errno(r, "Image file to lock is not a regular file: %m");
+
+                if (flock(image_fd, LOCK_EX|LOCK_NB) < 0) {
+
+                        if (errno == EWOULDBLOCK)
+                                log_error_errno(errno, "Image file '%s' already locked, can't use.", ip);
+                        else
+                                log_error_errno(errno, "Failed to lock image file '%s': %m", ip);
+
+                        return errno != EWOULDBLOCK ? -errno : -EADDRINUSE; /* Make error recognizable */
+                }
+
+                log_info("Successfully locked image file '%s'.", ip);
+
+                /* Now send it to our parent to keep safe while the home dir is active */
+                r = sd_pid_notify_with_fds(0, false, "SYSTEMD_LUKS_LOCK_FD=1", &image_fd, 1);
+                if (r < 0)
+                        log_warning_errno(r, "Failed to send LUKS lock fd to parent, ignoring: %m");
+        }
+
+        return 0;
+}
+
+static int open_image_file(
                 UserRecord *h,
-                bool already_activated,
+                const char *force_image_path,
+                struct stat *ret_stat) {
+
+        _cleanup_close_ int image_fd = -1;
+        struct stat st;
+        const char *ip;
+        int r;
+
+        ip = force_image_path ?: user_record_image_path(h);
+
+        image_fd = open(ip, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
+        if (image_fd < 0)
+                return log_error_errno(errno, "Failed to open image file %s: %m", ip);
+
+        if (fstat(image_fd, &st) < 0)
+                return log_error_errno(errno, "Failed to fstat() image file: %m");
+        if (!S_ISREG(st.st_mode) && !S_ISBLK(st.st_mode))
+                return log_error_errno(
+                                S_ISDIR(st.st_mode) ? SYNTHETIC_ERRNO(EISDIR) : SYNTHETIC_ERRNO(EBADFD),
+                                "Image file %s is not a regular file or block device: %m", ip);
+
+        r = lock_image_fd(image_fd, ip);
+        if (r < 0)
+                return r;
+
+        if (ret_stat)
+                *ret_stat = st;
+
+        return TAKE_FD(image_fd);
+}
+
+int home_setup_luks(
+                UserRecord *h,
+                HomeSetupFlags flags,
                 const char *force_image_path,
                 PasswordCache *cache,
                 HomeSetup *setup,
@@ -1045,14 +1148,14 @@ int home_prepare_luks(
         sd_id128_t found_partition_uuid, found_luks_uuid, found_fs_uuid;
         _cleanup_(user_record_unrefp) UserRecord *luks_home = NULL;
         _cleanup_(loop_device_unrefp) LoopDevice *loop = NULL;
-        _cleanup_(crypt_freep) struct crypt_device *cd = NULL;
+        _cleanup_(sym_crypt_freep) struct crypt_device *cd = NULL;
         _cleanup_(erase_and_freep) void *volume_key = NULL;
-        _cleanup_close_ int root_fd = -1, image_fd = -1;
+        _cleanup_close_ int opened_image_fd = -1, root_fd = -1;
         bool dm_activated = false, mounted = false;
         size_t volume_key_size = 0;
         bool marked_dirty = false;
         uint64_t offset, size;
-        int r;
+        int r, image_fd = -1;
 
         assert(h);
         assert(setup);
@@ -1061,7 +1164,11 @@ int home_prepare_luks(
 
         assert(user_record_storage(h) == USER_LUKS);
 
-        if (already_activated) {
+        r = dlopen_cryptsetup();
+        if (r < 0)
+                return r;
+
+        if (FLAGS_SET(flags, HOME_SETUP_ALREADY_ACTIVATED)) {
                 struct loop_info64 info;
                 const char *n;
 
@@ -1079,7 +1186,7 @@ int home_prepare_luks(
                 if (r < 0)
                         return r;
 
-                n = crypt_get_device_name(cd);
+                n = sym_crypt_get_device_name(cd);
                 if (!n)
                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to determine backing device for DM %s.", setup->dm_name);
 
@@ -1129,6 +1236,10 @@ int home_prepare_luks(
                                 offset *= 512U;
                         }
                 } else {
+#if HAVE_VALGRIND_MEMCHECK_H
+                        VALGRIND_MAKE_MEM_DEFINED(&info, sizeof(info));
+#endif
+
                         offset = info.lo_offset;
                         size = info.lo_sizelimit;
                 }
@@ -1139,7 +1250,7 @@ int home_prepare_luks(
 
                 root_fd = open(user_record_home_directory(h), O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW);
                 if (root_fd < 0) {
-                        r = log_error_errno(r, "Failed to open home directory: %m");
+                        r = log_error_errno(errno, "Failed to open home directory: %m");
                         goto fail;
                 }
         } else {
@@ -1153,16 +1264,15 @@ int home_prepare_luks(
                 if (!subdir)
                         return log_oom();
 
-                image_fd = open(ip, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
-                if (image_fd < 0)
-                        return log_error_errno(errno, "Failed to open image file %s: %m", ip);
+                /* Reuse the image fd if it has already been opened by an earlier step */
+                if (setup->image_fd < 0) {
+                        opened_image_fd = open_image_file(h, force_image_path, &st);
+                        if (opened_image_fd < 0)
+                                return opened_image_fd;
 
-                if (fstat(image_fd, &st) < 0)
-                        return log_error_errno(errno, "Failed to fstat() image file: %m");
-                if (!S_ISREG(st.st_mode) && !S_ISBLK(st.st_mode))
-                        return log_error_errno(
-                                        S_ISDIR(st.st_mode) ? SYNTHETIC_ERRNO(EISDIR) : SYNTHETIC_ERRNO(EBADFD),
-                                        "Image file %s is not a regular file or block device: %m", ip);
+                        image_fd = opened_image_fd;
+                } else
+                        image_fd = setup->image_fd;
 
                 r = luks_validate(image_fd, user_record_user_name_and_realm(h), h->partition_uuid, &found_partition_uuid, &offset, &size);
                 if (r < 0)
@@ -1226,14 +1336,19 @@ int home_prepare_luks(
 
                 root_fd = open(subdir, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW);
                 if (root_fd < 0) {
-                        r = log_error_errno(r, "Failed to open home directory: %m");
+                        r = log_error_errno(errno, "Failed to open home directory: %m");
                         goto fail;
                 }
 
                 if (user_record_luks_discard(h))
                         (void) run_fitrim(root_fd);
 
-                setup->image_fd = TAKE_FD(image_fd);
+                /* And now, fill in everything */
+                if (opened_image_fd >= 0) {
+                        safe_close(setup->image_fd);
+                        setup->image_fd = TAKE_FD(opened_image_fd);
+                }
+
                 setup->do_offline_fallocate = !(setup->do_offline_fitrim = user_record_luks_offline_discard(h));
                 setup->do_mark_clean = marked_dirty;
         }
@@ -1262,7 +1377,7 @@ fail:
                 (void) umount_verbose(LOG_ERR, "/run/systemd/user-home-mount", UMOUNT_NOFOLLOW);
 
         if (dm_activated)
-                (void) crypt_deactivate(cd, setup->dm_name);
+                (void) sym_crypt_deactivate_by_name(cd, setup->dm_name, 0);
 
         if (image_fd >= 0 && marked_dirty)
                 (void) run_mark_dirty(image_fd, false);
@@ -1271,24 +1386,22 @@ fail:
 }
 
 static void print_size_summary(uint64_t host_size, uint64_t encrypted_size, struct statfs *sfs) {
-        char buffer1[FORMAT_BYTES_MAX], buffer2[FORMAT_BYTES_MAX], buffer3[FORMAT_BYTES_MAX], buffer4[FORMAT_BYTES_MAX];
-
         assert(sfs);
 
         log_info("Image size is %s, file system size is %s, file system payload size is %s, file system free is %s.",
-                 format_bytes(buffer1, sizeof(buffer1), host_size),
-                 format_bytes(buffer2, sizeof(buffer2), encrypted_size),
-                 format_bytes(buffer3, sizeof(buffer3), (uint64_t) sfs->f_blocks * (uint64_t) sfs->f_frsize),
-                 format_bytes(buffer4, sizeof(buffer4), (uint64_t) sfs->f_bfree * (uint64_t) sfs->f_frsize));
+                 FORMAT_BYTES(host_size),
+                 FORMAT_BYTES(encrypted_size),
+                 FORMAT_BYTES((uint64_t) sfs->f_blocks * (uint64_t) sfs->f_frsize),
+                 FORMAT_BYTES((uint64_t) sfs->f_bfree * (uint64_t) sfs->f_frsize));
 }
 
 int home_activate_luks(
                 UserRecord *h,
+                HomeSetup *setup,
                 PasswordCache *cache,
                 UserRecord **ret_home) {
 
         _cleanup_(user_record_unrefp) UserRecord *new_home = NULL, *luks_home_record = NULL;
-        _cleanup_(home_setup_undo) HomeSetup setup = HOME_SETUP_INIT;
         uint64_t host_size, encrypted_size;
         const char *hdo, *hd;
         struct statfs sfs;
@@ -1296,43 +1409,43 @@ int home_activate_luks(
 
         assert(h);
         assert(user_record_storage(h) == USER_LUKS);
+        assert(setup);
         assert(ret_home);
 
+        r = dlopen_cryptsetup();
+        if (r < 0)
+                return r;
+
         assert_se(hdo = user_record_home_directory(h));
-        hd = strdupa(hdo); /* copy the string out, since it might change later in the home record object */
+        hd = strdupa_safe(hdo); /* copy the string out, since it might change later in the home record object */
 
-        r = make_dm_names(h->user_name, &setup.dm_name, &setup.dm_node);
+        r = home_get_state_luks(h, setup);
         if (r < 0)
                 return r;
+        if (r > 0)
+                return log_error_errno(SYNTHETIC_ERRNO(EEXIST), "Device mapper device %s already exists, refusing.", setup->dm_node);
 
-        r = access(setup.dm_node, F_OK);
-        if (r < 0) {
-                if (errno != ENOENT)
-                        return log_error_errno(errno, "Failed to determine whether %s exists: %m", setup.dm_node);
-        } else
-                return log_error_errno(SYNTHETIC_ERRNO(EEXIST), "Device mapper device %s already exists, refusing.", setup.dm_node);
-
-        r = home_prepare_luks(
+        r = home_setup_luks(
                         h,
-                        false,
+                        0,
                         NULL,
                         cache,
-                        &setup,
+                        setup,
                         &luks_home_record);
         if (r < 0)
                 return r;
 
-        r = block_get_size_by_fd(setup.loop->fd, &host_size);
+        r = block_get_size_by_fd(setup->loop->fd, &host_size);
         if (r < 0)
                 return log_error_errno(r, "Failed to get loopback block device size: %m");
 
-        r = block_get_size_by_path(setup.dm_node, &encrypted_size);
+        r = block_get_size_by_path(setup->dm_node, &encrypted_size);
         if (r < 0)
                 return log_error_errno(r, "Failed to get LUKS block device size: %m");
 
         r = home_refresh(
                         h,
-                        &setup,
+                        setup,
                         luks_home_record,
                         cache,
                         &sfs,
@@ -1340,28 +1453,28 @@ int home_activate_luks(
         if (r < 0)
                 return r;
 
-        r = home_extend_embedded_identity(new_home, h, &setup);
+        r = home_extend_embedded_identity(new_home, h, setup);
         if (r < 0)
                 return r;
 
-        setup.root_fd = safe_close(setup.root_fd);
+        setup->root_fd = safe_close(setup->root_fd);
 
         r = home_move_mount(user_record_user_name_and_realm(h), hd);
         if (r < 0)
                 return r;
 
-        setup.undo_mount = false;
-        setup.do_offline_fitrim = false;
+        setup->undo_mount = false;
+        setup->do_offline_fitrim = false;
 
-        loop_device_relinquish(setup.loop);
+        loop_device_relinquish(setup->loop);
 
-        r = crypt_deactivate_by_name(NULL, setup.dm_name, CRYPT_DEACTIVATE_DEFERRED);
+        r = sym_crypt_deactivate_by_name(NULL, setup->dm_name, CRYPT_DEACTIVATE_DEFERRED);
         if (r < 0)
                 log_warning_errno(r, "Failed to relinquish DM device, ignoring: %m");
 
-        setup.undo_dm = false;
-        setup.do_offline_fallocate = false;
-        setup.do_mark_clean = false;
+        setup->undo_dm = false;
+        setup->do_offline_fallocate = false;
+        setup->do_mark_clean = false;
 
         log_info("Everything completed.");
 
@@ -1372,7 +1485,7 @@ int home_activate_luks(
 }
 
 int home_deactivate_luks(UserRecord *h) {
-        _cleanup_(crypt_freep) struct crypt_device *cd = NULL;
+        _cleanup_(sym_crypt_freep) struct crypt_device *cd = NULL;
         _cleanup_free_ char *dm_name = NULL, *dm_node = NULL;
         bool we_detached;
         int r;
@@ -1383,11 +1496,15 @@ int home_deactivate_luks(UserRecord *h) {
          * don't bother about the loopback device because unlike the DM device it doesn't have a fixed
          * name. */
 
+        r = dlopen_cryptsetup();
+        if (r < 0)
+                return r;
+
         r = make_dm_names(h->user_name, &dm_name, &dm_node);
         if (r < 0)
                 return r;
 
-        r = crypt_init_by_name(&cd, dm_name);
+        r = sym_crypt_init_by_name(&cd, dm_name);
         if (IN_SET(r, -ENODEV, -EINVAL, -ENOENT)) {
                 log_debug_errno(r, "LUKS device %s has already been detached.", dm_name);
                 we_detached = false;
@@ -1398,7 +1515,7 @@ int home_deactivate_luks(UserRecord *h) {
 
                 cryptsetup_enable_logging(cd);
 
-                r = crypt_deactivate(cd, dm_name);
+                r = sym_crypt_deactivate_by_name(cd, dm_name, 0);
                 if (IN_SET(r, -ENODEV, -EINVAL, -ENOENT)) {
                         log_debug_errno(r, "LUKS device %s is already detached.", dm_node);
                         we_detached = false;
@@ -1474,20 +1591,20 @@ static int luks_format(
                 struct crypt_device **ret) {
 
         _cleanup_(user_record_unrefp) UserRecord *reduced = NULL;
-        _cleanup_(crypt_freep) struct crypt_device *cd = NULL;
+        _cleanup_(sym_crypt_freep) struct crypt_device *cd = NULL;
         _cleanup_(erase_and_freep) void *volume_key = NULL;
         struct crypt_pbkdf_type good_pbkdf, minimal_pbkdf;
-        char suuid[ID128_UUID_STRING_MAX], **pp;
         _cleanup_free_ char *text = NULL;
         size_t volume_key_size;
         int slot = 0, r;
+        char **pp;
 
         assert(node);
         assert(dm_name);
         assert(hr);
         assert(ret);
 
-        r = crypt_init(&cd, node);
+        r = sym_crypt_init(&cd, node);
         if (r < 0)
                 return log_error_errno(r, "Failed to allocate libcryptsetup context: %m");
 
@@ -1508,7 +1625,7 @@ static int luks_format(
 
 #if HAVE_CRYPT_SET_METADATA_SIZE
         /* Increase the metadata space to 4M, the largest LUKS2 supports */
-        r = crypt_set_metadata_size(cd, 4096U*1024U, 0);
+        r = sym_crypt_set_metadata_size(cd, 4096U*1024U, 0);
         if (r < 0)
                 return log_error_errno(r, "Failed to change LUKS2 metadata size: %m");
 #endif
@@ -1516,19 +1633,20 @@ static int luks_format(
         build_good_pbkdf(&good_pbkdf, hr);
         build_minimal_pbkdf(&minimal_pbkdf, hr);
 
-        r = crypt_format(cd,
-                         CRYPT_LUKS2,
-                         user_record_luks_cipher(hr),
-                         user_record_luks_cipher_mode(hr),
-                         id128_to_uuid_string(uuid, suuid),
-                         volume_key,
-                         volume_key_size,
-                         &(struct crypt_params_luks2) {
-                                 .label = label,
-                                 .subsystem = "systemd-home",
-                                 .sector_size = 512U,
-                                 .pbkdf = &good_pbkdf,
-                         });
+        r = sym_crypt_format(
+                        cd,
+                        CRYPT_LUKS2,
+                        user_record_luks_cipher(hr),
+                        user_record_luks_cipher_mode(hr),
+                        ID128_TO_UUID_STRING(uuid),
+                        volume_key,
+                        volume_key_size,
+                        &(struct crypt_params_luks2) {
+                                .label = label,
+                                .subsystem = "systemd-home",
+                                .sector_size = 512U,
+                                .pbkdf = &good_pbkdf,
+                        });
         if (r < 0)
                 return log_error_errno(r, "Failed to format LUKS image: %m");
 
@@ -1536,18 +1654,17 @@ static int luks_format(
 
         STRV_FOREACH(pp, effective_passwords) {
 
-                if (strv_contains(cache->pkcs11_passwords, *pp) ||
-                    strv_contains(cache->fido2_passwords, *pp)) {
+                if (password_cache_contains(cache, *pp)) { /* is this a fido2 or pkcs11 password? */
                         log_debug("Using minimal PBKDF for slot %i", slot);
-                        r = crypt_set_pbkdf_type(cd, &minimal_pbkdf);
+                        r = sym_crypt_set_pbkdf_type(cd, &minimal_pbkdf);
                 } else {
                         log_debug("Using good PBKDF for slot %i", slot);
-                        r = crypt_set_pbkdf_type(cd, &good_pbkdf);
+                        r = sym_crypt_set_pbkdf_type(cd, &good_pbkdf);
                 }
                 if (r < 0)
                         return log_error_errno(r, "Failed to tweak PBKDF for slot %i: %m", slot);
 
-                r = crypt_keyslot_add_by_volume_key(
+                r = sym_crypt_keyslot_add_by_volume_key(
                                 cd,
                                 slot,
                                 volume_key,
@@ -1561,7 +1678,7 @@ static int luks_format(
                 slot++;
         }
 
-        r = crypt_activate_by_volume_key(
+        r = sym_crypt_activate_by_volume_key(
                         cd,
                         dm_name,
                         volume_key,
@@ -1572,7 +1689,7 @@ static int luks_format(
 
         log_info("LUKS activation by volume key succeeded.");
 
-        r = user_record_clone(hr, USER_RECORD_EXTRACT_EMBEDDED, &reduced);
+        r = user_record_clone(hr, USER_RECORD_EXTRACT_EMBEDDED|USER_RECORD_PERMISSIVE, &reduced);
         if (r < 0)
                 return log_error_errno(r, "Failed to prepare home record for LUKS: %m");
 
@@ -1580,7 +1697,7 @@ static int luks_format(
         if (r < 0)
                 return r;
 
-        r = crypt_token_json_set(cd, CRYPT_ANY_TOKEN, text);
+        r = sym_crypt_token_json_set(cd, CRYPT_ANY_TOKEN, text);
         if (r < 0)
                 return log_error_errno(r, "Failed to set LUKS JSON token: %m");
 
@@ -1592,10 +1709,10 @@ static int luks_format(
         return 0;
 }
 
-DEFINE_TRIVIAL_CLEANUP_FUNC(struct fdisk_context*, fdisk_unref_context);
-DEFINE_TRIVIAL_CLEANUP_FUNC(struct fdisk_partition*, fdisk_unref_partition);
-DEFINE_TRIVIAL_CLEANUP_FUNC(struct fdisk_parttype*, fdisk_unref_parttype);
-DEFINE_TRIVIAL_CLEANUP_FUNC(struct fdisk_table*, fdisk_unref_table);
+DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(struct fdisk_context*, fdisk_unref_context, NULL);
+DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(struct fdisk_partition*, fdisk_unref_partition, NULL);
+DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(struct fdisk_parttype*, fdisk_unref_parttype, NULL);
+DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(struct fdisk_table*, fdisk_unref_table, NULL);
 
 static int make_partition_table(
                 int fd,
@@ -1609,9 +1726,8 @@ static int make_partition_table(
         _cleanup_(fdisk_unref_parttypep) struct fdisk_parttype *t = NULL;
         _cleanup_(fdisk_unref_contextp) struct fdisk_context *c = NULL;
         _cleanup_free_ char *path = NULL, *disk_uuid_as_string = NULL;
-        uint64_t offset, size;
+        uint64_t offset, size, first_lba, start, last_lba, end;
         sd_id128_t disk_uuid;
-        char uuids[ID128_UUID_STRING_MAX];
         int r;
 
         assert(fd >= 0);
@@ -1650,23 +1766,37 @@ static int make_partition_table(
         if (r < 0)
                 return log_error_errno(r, "Failed to set partition type: %m");
 
-        r = fdisk_partition_start_follow_default(p, 1);
-        if (r < 0)
-                return log_error_errno(r, "Failed to place partition at beginning of space: %m");
-
         r = fdisk_partition_partno_follow_default(p, 1);
         if (r < 0)
                 return log_error_errno(r, "Failed to place partition at first free partition index: %m");
 
-        r = fdisk_partition_end_follow_default(p, 1);
+        first_lba = fdisk_get_first_lba(c); /* Boundary where usable space starts */
+        assert(first_lba <= UINT64_MAX/512);
+        start = DISK_SIZE_ROUND_UP(first_lba * 512); /* Round up to multiple of 4K */
+
+        if (start == UINT64_MAX)
+                return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Overflow while rounding up start LBA.");
+
+        last_lba = fdisk_get_last_lba(c); /* One sector before boundary where usable space ends */
+        assert(last_lba < UINT64_MAX/512);
+        end = DISK_SIZE_ROUND_DOWN((last_lba + 1) * 512); /* Round down to multiple of 4K */
+
+        if (end <= start)
+                return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Resulting partition size zero or negative.");
+
+        r = fdisk_partition_set_start(p, start / 512);
+        if (r < 0)
+                return log_error_errno(r, "Failed to place partition at offset %" PRIu64 ": %m", start);
+
+        r = fdisk_partition_set_size(p, (end - start) / 512);
         if (r < 0)
-                return log_error_errno(r, "Failed to make partition cover all free space: %m");
+                return log_error_errno(r, "Failed to end partition at offset %" PRIu64 ": %m", end);
 
         r = fdisk_partition_set_name(p, label);
         if (r < 0)
                 return log_error_errno(r, "Failed to set partition name: %m");
 
-        r = fdisk_partition_set_uuid(p, id128_to_uuid_string(uuid, uuids));
+        r = fdisk_partition_set_uuid(p, ID128_TO_UUID_STRING(uuid));
         if (r < 0)
                 return log_error_errno(r, "Failed to set partition UUID: %m");
 
@@ -1778,7 +1908,6 @@ static int wait_for_devlink(const char *path) {
 }
 
 static int calculate_disk_size(UserRecord *h, const char *parent_dir, uint64_t *ret) {
-        char buf[FORMAT_BYTES_MAX];
         struct statfs sfs;
         uint64_t m;
 
@@ -1805,14 +1934,14 @@ static int calculate_disk_size(UserRecord *h, const char *parent_dir, uint64_t *
 
                 log_info("Sizing home to %u%% of available disk space, which is %s.",
                          USER_DISK_SIZE_DEFAULT_PERCENT,
-                         format_bytes(buf, sizeof(buf), *ret));
+                         FORMAT_BYTES(*ret));
         } else {
                 *ret = DISK_SIZE_ROUND_DOWN((uint64_t) ((double) m * (double) h->disk_size_relative / (double) UINT32_MAX));
 
                 log_info("Sizing home to %" PRIu64 ".%01" PRIu64 "%% of available disk space, which is %s.",
                          (h->disk_size_relative * 100) / UINT32_MAX,
                          ((h->disk_size_relative * 1000) / UINT32_MAX) % 10,
-                         format_bytes(buf, sizeof(buf), *ret));
+                         FORMAT_BYTES(*ret));
         }
 
         if (*ret < USER_DISK_SIZE_MIN)
@@ -1862,17 +1991,18 @@ static int home_truncate(
 
 int home_create_luks(
                 UserRecord *h,
-                PasswordCache *cache,
+                const PasswordCache *cache,
                 char **effective_passwords,
                 UserRecord **ret_home) {
 
         _cleanup_free_ char *dm_name = NULL, *dm_node = NULL, *subdir = NULL, *disk_uuid_path = NULL, *temporary_image_path = NULL;
-        uint64_t host_size, encrypted_size, partition_offset, partition_size;
+        uint64_t encrypted_size,
+                host_size = 0, partition_offset = 0, partition_size = 0; /* Unnecessary initialization to appease gcc */
         bool image_created = false, dm_activated = false, mounted = false;
         _cleanup_(user_record_unrefp) UserRecord *new_home = NULL;
         sd_id128_t partition_uuid, fs_uuid, luks_uuid, disk_uuid;
         _cleanup_(loop_device_unrefp) LoopDevice *loop = NULL;
-        _cleanup_(crypt_freep) struct crypt_device *cd = NULL;
+        _cleanup_(sym_crypt_freep) struct crypt_device *cd = NULL;
         _cleanup_close_ int image_fd = -1, root_fd = -1;
         const char *fstype, *ip;
         struct statfs sfs;
@@ -1882,6 +2012,10 @@ int home_create_luks(
         assert(h->storage < 0 || h->storage == USER_LUKS);
         assert(ret_home);
 
+        r = dlopen_cryptsetup();
+        if (r < 0)
+                return r;
+
         assert_se(ip = user_record_image_path(h));
 
         fstype = user_record_file_system_type(h);
@@ -2135,7 +2269,7 @@ int home_create_luks(
         if (r < 0)
                 goto fail;
 
-        r = user_record_clone(h, USER_RECORD_LOAD_MASK_SECRET|USER_RECORD_LOG, &new_home);
+        r = user_record_clone(h, USER_RECORD_LOAD_MASK_SECRET|USER_RECORD_LOG|USER_RECORD_PERMISSIVE, &new_home);
         if (r < 0) {
                 log_error_errno(r, "Failed to clone record: %m");
                 goto fail;
@@ -2148,8 +2282,8 @@ int home_create_luks(
                         partition_uuid,
                         luks_uuid,
                         fs_uuid,
-                        crypt_get_cipher(cd),
-                        crypt_get_cipher_mode(cd),
+                        sym_crypt_get_cipher(cd),
+                        sym_crypt_get_cipher_mode(cd),
                         luks_volume_key_size_convert(cd),
                         fstype,
                         NULL,
@@ -2174,13 +2308,13 @@ int home_create_luks(
 
         mounted = false;
 
-        r = crypt_deactivate(cd, dm_name);
+        r = sym_crypt_deactivate_by_name(cd, dm_name, 0);
         if (r < 0) {
                 log_error_errno(r, "Failed to deactivate LUKS device: %m");
                 goto fail;
         }
 
-        crypt_free(cd);
+        sym_crypt_free(cd);
         cd = NULL;
 
         dm_activated = false;
@@ -2241,7 +2375,7 @@ fail:
                 (void) umount_verbose(LOG_WARNING, "/run/systemd/user-home-mount", UMOUNT_NOFOLLOW);
 
         if (dm_activated)
-                (void) crypt_deactivate(cd, dm_name);
+                (void) sym_crypt_deactivate_by_name(cd, dm_name, 0);
 
         loop = loop_device_unref(loop);
 
@@ -2251,7 +2385,7 @@ fail:
         return r;
 }
 
-int home_validate_update_luks(UserRecord *h, HomeSetup *setup) {
+int home_get_state_luks(UserRecord *h, HomeSetup *setup) {
         _cleanup_free_ char *dm_name = NULL, *dm_node = NULL;
         int r;
 
@@ -2351,12 +2485,15 @@ static int ext4_offline_resize_fs(HomeSetup *setup, uint64_t new_size, bool disc
         log_info("Temporary unmounting of file system completed.");
 
         /* 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, &fsck_pid);
+        r = safe_fork("(e2fsck)",
+                      FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG|FORK_LOG|FORK_STDOUT_TO_STDERR|FORK_CLOSE_ALL_FDS,
+                      &fsck_pid);
         if (r < 0)
                 return r;
         if (r == 0) {
                 /* Child */
                 execlp("e2fsck" ,"e2fsck", "-fp", setup->dm_node, NULL);
+                log_open();
                 log_error_errno(errno, "Failed to execute e2fsck: %m");
                 _exit(EXIT_FAILURE);
         }
@@ -2380,12 +2517,15 @@ static int ext4_offline_resize_fs(HomeSetup *setup, uint64_t new_size, bool disc
                 return log_oom();
 
         /* Resize the thing */
-        r = safe_fork("(e2resize)", FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG|FORK_LOG|FORK_WAIT|FORK_STDOUT_TO_STDERR, &resize_pid);
+        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,
+                      &resize_pid);
         if (r < 0)
                 return r;
         if (r == 0) {
                 /* Child */
                 execlp("resize2fs" ,"resize2fs", setup->dm_node, size_str, NULL);
+                log_open();
                 log_error_errno(errno, "Failed to execute resize2fs: %m");
                 _exit(EXIT_FAILURE);
         }
@@ -2423,7 +2563,7 @@ static int prepare_resize_partition(
         _cleanup_(fdisk_unref_contextp) struct fdisk_context *c = NULL;
         _cleanup_(fdisk_unref_tablep) struct fdisk_table *t = NULL;
         _cleanup_free_ char *path = NULL, *disk_uuid_as_string = NULL;
-        size_t n_partitions, i;
+        size_t n_partitions;
         sd_id128_t disk_uuid;
         bool found = false;
         int r;
@@ -2473,7 +2613,7 @@ static int prepare_resize_partition(
                 return log_error_errno(r, "Failed to acquire partition table: %m");
 
         n_partitions = fdisk_table_get_nents(t);
-        for (i = 0; i < n_partitions; i++)  {
+        for (size_t i = 0; i < n_partitions; i++)  {
                 struct fdisk_partition *p;
 
                 p = fdisk_table_get_partition(t, i);
@@ -2600,38 +2740,45 @@ static int apply_resize_partition(int fd, sd_id128_t disk_uuids, struct fdisk_ta
 
 int home_resize_luks(
                 UserRecord *h,
-                bool already_activated,
+                HomeSetupFlags flags,
                 PasswordCache *cache,
                 HomeSetup *setup,
                 UserRecord **ret_home) {
 
-        char buffer1[FORMAT_BYTES_MAX], buffer2[FORMAT_BYTES_MAX], buffer3[FORMAT_BYTES_MAX],
-                buffer4[FORMAT_BYTES_MAX], buffer5[FORMAT_BYTES_MAX], buffer6[FORMAT_BYTES_MAX];
         uint64_t old_image_size, new_image_size, old_fs_size, new_fs_size, crypto_offset, new_partition_size;
         _cleanup_(user_record_unrefp) UserRecord *header_home = NULL, *embedded_home = NULL, *new_home = NULL;
         _cleanup_(fdisk_unref_tablep) struct fdisk_table *table = NULL;
+        _cleanup_close_ int opened_image_fd = -1;
         _cleanup_free_ char *whole_disk = NULL;
-        _cleanup_close_ int image_fd = -1;
+        int r, resize_type, image_fd = -1;
         sd_id128_t disk_uuid;
         const char *ip, *ipo;
         struct statfs sfs;
         struct stat st;
-        int r, resize_type;
 
         assert(h);
         assert(user_record_storage(h) == USER_LUKS);
         assert(setup);
         assert(ret_home);
 
+        r = dlopen_cryptsetup();
+        if (r < 0)
+                return r;
+
         assert_se(ipo = user_record_image_path(h));
-        ip = strdupa(ipo); /* copy out since original might change later in home record object */
+        ip = strdupa_safe(ipo); /* copy out since original might change later in home record object */
 
-        image_fd = open(ip, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
-        if (image_fd < 0)
-                return log_error_errno(errno, "Failed to open image file %s: %m", ip);
+        if (setup->image_fd < 0) {
+                setup->image_fd = open_image_file(h, NULL, &st);
+                if (setup->image_fd < 0)
+                        return setup->image_fd;
+        } else {
+                if (fstat(setup->image_fd, &st) < 0)
+                        return log_error_errno(errno, "Failed to stat image file %s: %m", ip);
+        }
+
+        image_fd = setup->image_fd;
 
-        if (fstat(image_fd, &st) < 0)
-                return log_error_errno(errno, "Failed to stat image file %s: %m", ip);
         if (S_ISBLK(st.st_mode)) {
                 dev_t parent;
 
@@ -2649,12 +2796,12 @@ int home_resize_luks(
                         if (r < 0)
                                 return log_error_errno(r, "Failed to derive whole disk path for %s: %m", ip);
 
-                        safe_close(image_fd);
-
-                        image_fd = open(whole_disk, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
-                        if (image_fd < 0)
+                        opened_image_fd = open(whole_disk, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
+                        if (opened_image_fd < 0)
                                 return log_error_errno(errno, "Failed to open whole block device %s: %m", whole_disk);
 
+                        image_fd = opened_image_fd;
+
                         if (fstat(image_fd, &st) < 0)
                                 return log_error_errno(errno, "Failed to stat whole block device %s: %m", whole_disk);
                         if (!S_ISBLK(st.st_mode))
@@ -2670,6 +2817,8 @@ int home_resize_luks(
 
                 new_image_size = old_image_size; /* we can't resize physical block devices */
         } else {
+                uint64_t new_image_size_rounded;
+
                 r = stat_verify_regular(&st);
                 if (r < 0)
                         return log_error_errno(r, "Image %s is not a block device nor regular file: %m", ip);
@@ -2680,14 +2829,19 @@ int home_resize_luks(
                  * apply onto the loopback file as a whole. When we operate on block devices we instead apply
                  * to the partition itself only. */
 
-                new_image_size = DISK_SIZE_ROUND_DOWN(h->disk_size);
-                if (new_image_size == old_image_size) {
+                new_image_size_rounded = DISK_SIZE_ROUND_DOWN(h->disk_size);
+
+                if (old_image_size == h->disk_size ||
+                    old_image_size == new_image_size_rounded) {
+                        /* If exact match, or a match after we rounded down, don't do a thing */
                         log_info("Image size already matching, skipping operation.");
                         return 0;
                 }
+
+                new_image_size = new_image_size_rounded;
         }
 
-        r = home_prepare_luks(h, already_activated, whole_disk, cache, setup, &header_home);
+        r = home_setup_luks(h, flags, whole_disk, cache, setup, &header_home);
         if (r < 0)
                 return r;
 
@@ -2708,44 +2862,50 @@ int home_resize_luks(
                 if (new_image_size <= partition_table_extra)
                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "New size smaller than partition table metadata.");
 
-                new_partition_size = new_image_size - partition_table_extra;
+                new_partition_size = DISK_SIZE_ROUND_DOWN(new_image_size - partition_table_extra);
         } else {
+                uint64_t new_partition_size_rounded;
+
                 assert(S_ISBLK(st.st_mode));
 
-                new_partition_size = DISK_SIZE_ROUND_DOWN(h->disk_size);
-                if (new_partition_size == setup->partition_size) {
+                new_partition_size_rounded = DISK_SIZE_ROUND_DOWN(h->disk_size);
+
+                if (h->disk_size == setup->partition_size ||
+                    new_partition_size_rounded == setup->partition_size) {
                         log_info("Partition size already matching, skipping operation.");
                         return 0;
                 }
+
+                new_partition_size = new_partition_size_rounded;
         }
 
         if ((UINT64_MAX - setup->partition_offset) < new_partition_size ||
             setup->partition_offset + new_partition_size > new_image_size)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "New partition doesn't fit into backing storage, refusing.");
 
-        crypto_offset = crypt_get_data_offset(setup->crypt_device);
+        crypto_offset = sym_crypt_get_data_offset(setup->crypt_device);
         if (setup->partition_size / 512U <= crypto_offset)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Weird, old crypto payload offset doesn't actually fit in partition size?");
         if (new_partition_size / 512U <= crypto_offset)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "New size smaller than crypto payload offset?");
 
         old_fs_size = (setup->partition_size / 512U - crypto_offset) * 512U;
-        new_fs_size = (new_partition_size / 512U - crypto_offset) * 512U;
+        new_fs_size = DISK_SIZE_ROUND_DOWN((new_partition_size / 512U - crypto_offset) * 512U);
 
         /* Before we start doing anything, let's figure out if we actually can */
         resize_type = can_resize_fs(setup->root_fd, old_fs_size, new_fs_size);
         if (resize_type < 0)
                 return resize_type;
-        if (resize_type == CAN_RESIZE_OFFLINE && already_activated)
+        if (resize_type == CAN_RESIZE_OFFLINE && FLAGS_SET(flags, HOME_SETUP_ALREADY_ACTIVATED))
                 return log_error_errno(SYNTHETIC_ERRNO(ETXTBSY), "File systems of this type can only be resized offline, but is currently online.");
 
         log_info("Ready to resize image size %s → %s, partition size %s → %s, file system size %s → %s.",
-                 format_bytes(buffer1, sizeof(buffer1), old_image_size),
-                 format_bytes(buffer2, sizeof(buffer2), new_image_size),
-                 format_bytes(buffer3, sizeof(buffer3), setup->partition_size),
-                 format_bytes(buffer4, sizeof(buffer4), new_partition_size),
-                 format_bytes(buffer5, sizeof(buffer5), old_fs_size),
-                 format_bytes(buffer6, sizeof(buffer6), new_fs_size));
+                 FORMAT_BYTES(old_image_size),
+                 FORMAT_BYTES(new_image_size),
+                 FORMAT_BYTES(setup->partition_size),
+                 FORMAT_BYTES(new_partition_size),
+                 FORMAT_BYTES(old_fs_size),
+                 FORMAT_BYTES(new_fs_size));
 
         r = prepare_resize_partition(
                         image_fd,
@@ -2783,11 +2943,11 @@ int home_resize_luks(
                 if (r > 0)
                         log_info("Growing of partition completed.");
 
-                if (ioctl(image_fd, BLKRRPART, 0) < 0)
+                if (S_ISBLK(st.st_mode) && ioctl(image_fd, BLKRRPART, 0) < 0)
                         log_debug_errno(errno, "BLKRRPART failed on block device, ignoring: %m");
 
                 /* Tell LUKS about the new bigger size too */
-                r = crypt_resize(setup->crypt_device, setup->dm_name, new_fs_size / 512U);
+                r = sym_crypt_resize(setup->crypt_device, setup->dm_name, new_fs_size / 512U);
                 if (r < 0)
                         return log_error_errno(r, "Failed to grow LUKS device: %m");
 
@@ -2811,12 +2971,15 @@ int home_resize_luks(
         }
 
         /* Now resize the file system */
-        if (resize_type == CAN_RESIZE_ONLINE)
+        if (resize_type == CAN_RESIZE_ONLINE) {
                 r = resize_fs(setup->root_fd, new_fs_size, NULL);
-        else
+                if (r < 0)
+                        return log_error_errno(r, "Failed to resize file system: %m");
+        } else {
                 r = ext4_offline_resize_fs(setup, new_fs_size, user_record_luks_discard(h), user_record_mount_flags(h));
-        if (r < 0)
-                return log_error_errno(r, "Failed to resize file system: %m");
+                if (r < 0)
+                        return r;
+        }
 
         log_info("File system resizing completed.");
 
@@ -2828,7 +2991,7 @@ int home_resize_luks(
         if (new_fs_size < old_fs_size) {
 
                 /* Shrink the LUKS device now, matching the new file system size */
-                r = crypt_resize(setup->crypt_device, setup->dm_name, new_fs_size / 512);
+                r = sym_crypt_resize(setup->crypt_device, setup->dm_name, new_fs_size / 512);
                 if (r < 0)
                         return log_error_errno(r, "Failed to shrink LUKS device: %m");
 
@@ -2857,7 +3020,7 @@ int home_resize_luks(
                 if (r > 0)
                         log_info("Shrinking of partition completed.");
 
-                if (ioctl(image_fd, BLKRRPART, 0) < 0)
+                if (S_ISBLK(st.st_mode) && ioctl(image_fd, BLKRRPART, 0) < 0)
                         log_debug_errno(errno, "BLKRRPART failed on block device, ignoring: %m");
         } else {
                 r = home_store_embedded_identity(new_home, setup->root_fd, h->uid, embedded_home);
@@ -2880,7 +3043,7 @@ int home_resize_luks(
         if (r < 0)
                 return r;
 
-        r = home_setup_undo(setup);
+        r = home_setup_done(setup);
         if (r < 0)
                 return r;
 
@@ -2895,10 +3058,10 @@ int home_resize_luks(
 int home_passwd_luks(
                 UserRecord *h,
                 HomeSetup *setup,
-                PasswordCache *cache,      /* the passwords acquired via PKCS#11/FIDO2 security tokens */
+                const PasswordCache *cache,      /* the passwords acquired via PKCS#11/FIDO2 security tokens */
                 char **effective_passwords /* new passwords */) {
 
-        size_t volume_key_size, i, max_key_slots, n_effective;
+        size_t volume_key_size, max_key_slots, n_effective;
         _cleanup_(erase_and_freep) void *volume_key = NULL;
         struct crypt_pbkdf_type good_pbkdf, minimal_pbkdf;
         const char *type;
@@ -2909,16 +3072,20 @@ int home_passwd_luks(
         assert(user_record_storage(h) == USER_LUKS);
         assert(setup);
 
-        type = crypt_get_type(setup->crypt_device);
+        r = dlopen_cryptsetup();
+        if (r < 0)
+                return r;
+
+        type = sym_crypt_get_type(setup->crypt_device);
         if (!type)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to determine crypto device type.");
 
-        r = crypt_keyslot_max(type);
+        r = sym_crypt_keyslot_max(type);
         if (r <= 0)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to determine number of key slots.");
         max_key_slots = r;
 
-        r = crypt_get_volume_key_size(setup->crypt_device);
+        r = sym_crypt_get_volume_key_size(setup->crypt_device);
         if (r <= 0)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to determine volume key size.");
         volume_key_size = (size_t) r;
@@ -2928,7 +3095,11 @@ int home_passwd_luks(
                 return log_oom();
 
         r = -ENOKEY;
-        FOREACH_POINTER(list, cache->pkcs11_passwords, cache->fido2_passwords, h->password) {
+        FOREACH_POINTER(list,
+                        cache ? cache->pkcs11_passwords : NULL,
+                        cache ? cache->fido2_passwords : NULL,
+                        h->password) {
+
                 r = luks_try_passwords(setup->crypt_device, list, volume_key, &volume_key_size);
                 if (r != -ENOKEY)
                         break;
@@ -2943,8 +3114,8 @@ int home_passwd_luks(
         build_good_pbkdf(&good_pbkdf, h);
         build_minimal_pbkdf(&minimal_pbkdf, h);
 
-        for (i = 0; i < max_key_slots; i++) {
-                r = crypt_keyslot_destroy(setup->crypt_device, i);
+        for (size_t i = 0; i < max_key_slots; i++) {
+                r = sym_crypt_keyslot_destroy(setup->crypt_device, i);
                 if (r < 0 && !IN_SET(r, -ENOENT, -EINVAL)) /* Returns EINVAL or ENOENT if there's no key in this slot already */
                         return log_error_errno(r, "Failed to destroy LUKS password: %m");
 
@@ -2954,18 +3125,17 @@ int home_passwd_luks(
                         continue;
                 }
 
-                if (strv_contains(cache->pkcs11_passwords, effective_passwords[i]) ||
-                    strv_contains(cache->fido2_passwords, effective_passwords[i])) {
+                if (password_cache_contains(cache, effective_passwords[i])) { /* Is this a FIDO2 or PKCS#11 password? */
                         log_debug("Using minimal PBKDF for slot %zu", i);
-                        r = crypt_set_pbkdf_type(setup->crypt_device, &minimal_pbkdf);
+                        r = sym_crypt_set_pbkdf_type(setup->crypt_device, &minimal_pbkdf);
                 } else {
                         log_debug("Using good PBKDF for slot %zu", i);
-                        r = crypt_set_pbkdf_type(setup->crypt_device, &good_pbkdf);
+                        r = sym_crypt_set_pbkdf_type(setup->crypt_device, &good_pbkdf);
                 }
                 if (r < 0)
                         return log_error_errno(r, "Failed to tweak PBKDF for slot %zu: %m", i);
 
-                r = crypt_keyslot_add_by_volume_key(
+                r = sym_crypt_keyslot_add_by_volume_key(
                                 setup->crypt_device,
                                 i,
                                 volume_key,
@@ -2982,7 +3152,7 @@ int home_passwd_luks(
 }
 
 int home_lock_luks(UserRecord *h) {
-        _cleanup_(crypt_freep) struct crypt_device *cd = NULL;
+        _cleanup_(sym_crypt_freep) struct crypt_device *cd = NULL;
         _cleanup_free_ char *dm_name = NULL, *dm_node = NULL;
         _cleanup_close_ int root_fd = -1;
         const char *p;
@@ -2999,7 +3169,11 @@ int home_lock_luks(UserRecord *h) {
         if (r < 0)
                 return r;
 
-        r = crypt_init_by_name(&cd, dm_name);
+        r = dlopen_cryptsetup();
+        if (r < 0)
+                return r;
+
+        r = sym_crypt_init_by_name(&cd, dm_name);
         if (r < 0)
                 return log_error_errno(r, "Failed to initialize cryptsetup context for %s: %m", dm_name);
 
@@ -3015,7 +3189,7 @@ int home_lock_luks(UserRecord *h) {
 
         /* Note that we don't invoke FIFREEZE here, it appears libcryptsetup/device-mapper already does that on its own for us */
 
-        r = crypt_suspend(cd, dm_name);
+        r = sym_crypt_suspend(cd, dm_name);
         if (r < 0)
                 return log_error_errno(r, "Failed to suspend cryptsetup device: %s: %m", dm_node);
 
@@ -3035,7 +3209,7 @@ static int luks_try_resume(
         assert(dm_name);
 
         STRV_FOREACH(pp, password) {
-                r = crypt_resume_by_passphrase(
+                r = sym_crypt_resume_by_passphrase(
                                 cd,
                                 dm_name,
                                 CRYPT_ANY_SLOT,
@@ -3052,9 +3226,9 @@ static int luks_try_resume(
         return -ENOKEY;
 }
 
-int home_unlock_luks(UserRecord *h, PasswordCache *cache) {
+int home_unlock_luks(UserRecord *h, const PasswordCache *cache) {
         _cleanup_free_ char *dm_name = NULL, *dm_node = NULL;
-        _cleanup_(crypt_freep) struct crypt_device *cd = NULL;
+        _cleanup_(sym_crypt_freep) struct crypt_device *cd = NULL;
         char **list;
         int r;
 
@@ -3064,7 +3238,11 @@ int home_unlock_luks(UserRecord *h, PasswordCache *cache) {
         if (r < 0)
                 return r;
 
-        r = crypt_init_by_name(&cd, dm_name);
+        r = dlopen_cryptsetup();
+        if (r < 0)
+                return r;
+
+        r = sym_crypt_init_by_name(&cd, dm_name);
         if (r < 0)
                 return log_error_errno(r, "Failed to initialize cryptsetup context for %s: %m", dm_name);
 
@@ -3072,7 +3250,10 @@ int home_unlock_luks(UserRecord *h, PasswordCache *cache) {
         cryptsetup_enable_logging(cd);
 
         r = -ENOKEY;
-        FOREACH_POINTER(list, cache->pkcs11_passwords, cache->fido2_passwords, h->password) {
+        FOREACH_POINTER(list,
+                        cache ? cache->pkcs11_passwords : NULL,
+                        cache ? cache->fido2_passwords : NULL,
+                        h->password) {
                 r = luks_try_resume(cd, dm_name, list);
                 if (r != -ENOKEY)
                         break;