summaryrefslogtreecommitdiffstats
path: root/src/home/homework-luks.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-12 03:50:40 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-12 03:50:40 +0000
commitfc53809803cd2bc2434e312b19a18fa36776da12 (patch)
treeb4b43bd6538f51965ce32856e9c053d0f90919c8 /src/home/homework-luks.c
parentAdding upstream version 255.5. (diff)
downloadsystemd-fc53809803cd2bc2434e312b19a18fa36776da12.tar.xz
systemd-fc53809803cd2bc2434e312b19a18fa36776da12.zip
Adding upstream version 256.upstream/256
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/home/homework-luks.c')
-rw-r--r--src/home/homework-luks.c280
1 files changed, 134 insertions, 146 deletions
diff --git a/src/home/homework-luks.c b/src/home/homework-luks.c
index 5bd78a0..20ff4c3 100644
--- a/src/home/homework-luks.c
+++ b/src/home/homework-luks.c
@@ -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) {
@@ -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);
@@ -385,7 +418,7 @@ static int luks_setup(
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");
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to determine LUKS volume key size.");
vks = (size_t) r;
if (!sd_id128_is_null(uuid) || ret_found_uuid) {
@@ -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)
@@ -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;
@@ -540,7 +563,7 @@ static int luks_open(
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");
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to determine LUKS volume key size.");
vks = (size_t) r;
if (ret_found_uuid) {
@@ -559,16 +582,7 @@ 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)
@@ -979,7 +993,7 @@ static int format_luks_token_text(
assert((size_t) encrypted_size_out1 <= encrypted_size);
if (EVP_EncryptFinal_ex(context, (uint8_t*) encrypted + encrypted_size_out1, &encrypted_size_out2) != 1)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to finish encryption of JSON record. ");
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to finish encryption of JSON record.");
assert((size_t) encrypted_size_out1 + (size_t) encrypted_size_out2 <= encrypted_size);
@@ -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,
@@ -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;
@@ -2227,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) {
@@ -2539,7 +2553,7 @@ static int can_resize_fs(int fd, uint64_t old_size, uint64_t new_size) {
/* btrfs can grow and shrink online */
- } else if (is_fs_type(&sfs, XFS_SB_MAGIC)) {
+ } else if (is_fs_type(&sfs, XFS_SUPER_MAGIC)) {
if (new_size < XFS_MINIMAL_SIZE)
return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "New file system size too small for xfs (needs to be 14M at least).");
@@ -2602,7 +2616,7 @@ static int ext4_offline_resize_fs(
return r;
if (r == 0) {
/* Child */
- execlp("e2fsck" ,"e2fsck", "-fp", setup->dm_node, NULL);
+ execlp("e2fsck", "e2fsck", "-fp", setup->dm_node, NULL);
log_open();
log_error_errno(errno, "Failed to execute e2fsck: %m");
_exit(EXIT_FAILURE);
@@ -2634,7 +2648,7 @@ static int ext4_offline_resize_fs(
return r;
if (r == 0) {
/* Child */
- execlp("resize2fs" ,"resize2fs", setup->dm_node, size_str, NULL);
+ execlp("resize2fs", "resize2fs", setup->dm_node, size_str, NULL);
log_open();
log_error_errno(errno, "Failed to execute resize2fs: %m");
_exit(EXIT_FAILURE);
@@ -2720,7 +2734,7 @@ static int prepare_resize_partition(
p = fdisk_table_get_partition(t, i);
if (!p)
- return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to read partition metadata: %m");
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to read partition metadata.");
if (fdisk_partition_is_used(p) <= 0)
continue;
@@ -3126,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;
@@ -3183,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);
@@ -3231,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);
@@ -3444,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;
}
@@ -3532,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;
}
@@ -3582,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);
@@ -3611,17 +3633,7 @@ 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)
@@ -3664,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;
@@ -3706,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);
@@ -3748,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;
}