#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"
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;
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;
}
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) {
}
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;
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;
}
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;
*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;
}
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,
const char *cipher,
const char *cipher_mode,
uint64_t volume_key_size,
- char **passwords,
const PasswordCache *cache,
bool discard,
struct crypt_device **ret,
_cleanup_(erase_and_freep) void *vk = NULL;
sd_id128_t p;
size_t vks;
- char **list;
int r;
assert(h);
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)
_cleanup_(erase_and_freep) void *vk = NULL;
sd_id128_t p;
- char **list;
size_t vks;
int r;
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)
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));
offset *= 512U;
}
+
+ size = setup->loop->device_size;
} else {
#if HAVE_VALGRIND_MEMCHECK_H
VALGRIND_MAKE_MEM_DEFINED(&info, sizeof(info));
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,
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;
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) {
/* 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;
/* 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;
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;
} 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);
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. */
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);
/* → 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;
}
} 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;
}
_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);
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 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;
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);
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;
}