From fd83c98e8a462b9b5ae07e3d7a2ace500b50c172 Mon Sep 17 00:00:00 2001 From: Aidan Dang Date: Mon, 26 Sep 2022 00:12:10 +1000 Subject: [PATCH] Implement --luks-sector-size for homed --- docs/USER_RECORD.md | 6 ++++- man/homectl.xml | 1 + shell-completion/bash/homectl | 1 + src/home/homectl.c | 46 +++++++++++++++++++++++++++++++++++ src/home/homework-luks.c | 6 ++--- src/partition/repart.c | 8 +++--- src/shared/loop-util.c | 21 +++++++++++++--- src/shared/loop-util.h | 2 +- src/shared/user-record-show.c | 2 ++ src/shared/user-record.c | 13 ++++++++++ src/shared/user-record.h | 2 ++ src/test/test-loop-block.c | 4 +-- 12 files changed, 98 insertions(+), 14 deletions(-) diff --git a/docs/USER_RECORD.md b/docs/USER_RECORD.md index e6d2b5d99f8..f330e8b13a7 100644 --- a/docs/USER_RECORD.md +++ b/docs/USER_RECORD.md @@ -500,6 +500,10 @@ memory cost for the PBKDF operation, when LUKS storage is used, in bytes. `luksPbkdfParallelThreads` → An unsigned 64bit integer, indicating the intended required parallel threads for the PBKDF operation, when LUKS storage is used. +`luksSectorSize` → An unsigned 64bit integer, indicating the sector size to +use for the LUKS storage mechanism, in bytes. Must be a power of two between +512 and 4096. + `autoResizeMode` → A string, one of `off`, `grow`, `shrink-and-grow`. Unless set to `off`, controls whether the home area shall be grown automatically to the size configured in `diskSize` automatically at login time. If set to @@ -734,7 +738,7 @@ that may be used in this section are identical to the equally named ones in the `fileSystemUuid`, `luksDiscard`, `luksOfflineDiscard`, `luksCipher`, `luksCipherMode`, `luksVolumeKeySize`, `luksPbkdfHashAlgorithm`, `luksPbkdfType`, `luksPbkdfTimeCostUSec`, `luksPbkdfMemoryCost`, -`luksPbkdfParallelThreads`, `autoResizeMode`, `rebalanceWeight`, +`luksPbkdfParallelThreads`, `luksSectorSize`, `autoResizeMode`, `rebalanceWeight`, `rateLimitIntervalUSec`, `rateLimitBurst`, `enforcePasswordPolicy`, `autoLogin`, `stopDelayUSec`, `killProcesses`, `passwordChangeMinUSec`, `passwordChangeMaxUSec`, `passwordChangeWarnUSec`, diff --git a/man/homectl.xml b/man/homectl.xml index 6fd5340370f..1064f056504 100644 --- a/man/homectl.xml +++ b/man/homectl.xml @@ -692,6 +692,7 @@ SECONDS BYTES THREADS + BYTES Configures various cryptographic parameters for the LUKS2 storage mechanism. See 4096) /* Allow up to 4K due to dm-crypt support and 4K alignment by the homed LUKS backend */ + return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Sector size not between 512 and 4096: %s", t); + if (!ISPOWEROF2(ss)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Sector size not power of 2: %s", t); + + *ret = ss; + return 0; +} + static int resize_home(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; _cleanup_(user_record_unrefp) UserRecord *secret = NULL; @@ -2291,6 +2311,8 @@ static int help(int argc, char *argv[], void *userdata) { " Memory cost for PBKDF in bytes\n" " --luks-pbkdf-parallel-threads=NUMBER\n" " Number of parallel threads for PKBDF\n" + " --luks-sector-size=BYTES\n" + " Sector size for LUKS encryption in bytes\n" " --luks-extra-mount-options=OPTIONS\n" " LUKS extra mount options\n" " --auto-resize-mode=MODE Automatically grow/shrink home on login/logout\n" @@ -2372,6 +2394,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_LUKS_PBKDF_TIME_COST, ARG_LUKS_PBKDF_MEMORY_COST, ARG_LUKS_PBKDF_PARALLEL_THREADS, + ARG_LUKS_SECTOR_SIZE, ARG_RATE_LIMIT_INTERVAL, ARG_RATE_LIMIT_BURST, ARG_STOP_DELAY, @@ -2452,6 +2475,7 @@ static int parse_argv(int argc, char *argv[]) { { "luks-pbkdf-time-cost", required_argument, NULL, ARG_LUKS_PBKDF_TIME_COST }, { "luks-pbkdf-memory-cost", required_argument, NULL, ARG_LUKS_PBKDF_MEMORY_COST }, { "luks-pbkdf-parallel-threads", required_argument, NULL, ARG_LUKS_PBKDF_PARALLEL_THREADS }, + { "luks-sector-size", required_argument, NULL, ARG_LUKS_SECTOR_SIZE }, { "nosuid", required_argument, NULL, ARG_NOSUID }, { "nodev", required_argument, NULL, ARG_NODEV }, { "noexec", required_argument, NULL, ARG_NOEXEC }, @@ -3095,6 +3119,28 @@ static int parse_argv(int argc, char *argv[]) { break; } + case ARG_LUKS_SECTOR_SIZE: { + uint64_t ss; + + if (isempty(optarg)) { + r = drop_from_identity("luksSectorSize"); + if (r < 0) + return r; + + break; + } + + r = parse_sector_size(optarg, &ss); + if (r < 0) + return r; + + r = json_variant_set_field_unsigned(&arg_identity_extra, "luksSectorSize", ss); + if (r < 0) + return log_error_errno(r, "Failed to set sector size field: %m"); + + break; + } + case ARG_UMASK: { mode_t m; diff --git a/src/home/homework-luks.c b/src/home/homework-luks.c index 43e6ee02a86..858d9dda194 100644 --- a/src/home/homework-luks.c +++ b/src/home/homework-luks.c @@ -1378,7 +1378,7 @@ int home_setup_luks( return r; } - r = loop_device_make(setup->image_fd, O_RDWR, offset, size, 0, LOCK_UN, &setup->loop); + r = loop_device_make(setup->image_fd, O_RDWR, offset, size, user_record_luks_sector_size(h), 0, LOCK_UN, &setup->loop); if (r == -ENOENT) { log_error_errno(r, "Loopback block device support is not available on this system."); return -ENOLINK; /* make recognizable */ @@ -1761,7 +1761,7 @@ static int luks_format( &(struct crypt_params_luks2) { .label = label, .subsystem = "systemd-home", - .sector_size = 512U, + .sector_size = user_record_luks_sector_size(hr), .pbkdf = &good_pbkdf, }); if (r < 0) @@ -2299,7 +2299,7 @@ int home_create_luks( log_info("Writing of partition table completed."); - r = loop_device_make(setup->image_fd, O_RDWR, partition_offset, partition_size, 0, LOCK_EX, &setup->loop); + r = loop_device_make(setup->image_fd, O_RDWR, partition_offset, partition_size, user_record_luks_sector_size(h), 0, LOCK_EX, &setup->loop); if (r < 0) { if (r == -ENOENT) { /* this means /dev/loop-control doesn't exist, i.e. we are in a container * or similar and loopback bock devices are not available, return a diff --git a/src/partition/repart.c b/src/partition/repart.c index 721a196a9a6..a0f7d416450 100644 --- a/src/partition/repart.c +++ b/src/partition/repart.c @@ -3176,7 +3176,7 @@ static int context_copy_blocks(Context *context) { assert_se((whole_fd = fdisk_get_devfd(context->fdisk_context)) >= 0); if (p->encrypt != ENCRYPT_OFF) { - r = loop_device_make(whole_fd, O_RDWR, p->offset, p->new_size, 0, LOCK_EX, &d); + r = loop_device_make(whole_fd, O_RDWR, p->offset, p->new_size, 0, 0, LOCK_EX, &d); if (r < 0) return log_error_errno(r, "Failed to make loopback device of future partition %" PRIu64 ": %m", p->partno); @@ -3477,7 +3477,7 @@ static int context_mkfs(Context *context) { /* Loopback block devices are not only useful to turn regular files into block devices, but * also to cut out sections of block devices into new block devices. */ - r = loop_device_make(fd, O_RDWR, p->offset, p->new_size, 0, LOCK_EX, &d); + r = loop_device_make(fd, O_RDWR, p->offset, p->new_size, 0, 0, LOCK_EX, &d); if (r < 0) return log_error_errno(r, "Failed to make loopback device of future partition %" PRIu64 ": %m", p->partno); @@ -3646,13 +3646,13 @@ static int context_verity_hash(Context *context) { if (fd < 0) assert_se((fd = fdisk_get_devfd(context->fdisk_context)) >= 0); - r = loop_device_make(fd, O_RDONLY, dp->offset, dp->new_size, 0, LOCK_EX, &data_device); + r = loop_device_make(fd, O_RDONLY, dp->offset, dp->new_size, 0, 0, LOCK_EX, &data_device); if (r < 0) return log_error_errno(r, "Failed to make loopback device of verity data partition %" PRIu64 ": %m", p->partno); - r = loop_device_make(fd, O_RDWR, p->offset, p->new_size, 0, LOCK_EX, &hash_device); + r = loop_device_make(fd, O_RDWR, p->offset, p->new_size, 0, 0, LOCK_EX, &hash_device); if (r < 0) return log_error_errno(r, "Failed to make loopback device of verity hash partition %" PRIu64 ": %m", diff --git a/src/shared/loop-util.c b/src/shared/loop-util.c index 84f4e79c07a..731ce291121 100644 --- a/src/shared/loop-util.c +++ b/src/shared/loop-util.c @@ -123,6 +123,17 @@ static int loop_configure_verify(int fd, const struct loop_config *c) { assert(fd >= 0); assert(c); + if (c->block_size != 0) { + int z; + + if (ioctl(fd, BLKSSZGET, &z) < 0) + return -errno; + + assert(z >= 0); + if ((uint32_t) z != c->block_size) + log_debug("LOOP_CONFIGURE didn't honour requested block size %u, got %i instead. Ignoring.", c->block_size, z); + } + if (c->info.lo_sizelimit != 0) { /* Kernel 5.8 vanilla doesn't properly propagate the size limit into the * block device. If it's used, let's immediately check if it had the desired @@ -133,7 +144,7 @@ static int loop_configure_verify(int fd, const struct loop_config *c) { return -errno; if (z != c->info.lo_sizelimit) { - log_debug("LOOP_CONFIGURE is broken, doesn't honour .lo_sizelimit. Falling back to LOOP_SET_STATUS64."); + log_debug("LOOP_CONFIGURE is broken, doesn't honour .info.lo_sizelimit. Falling back to LOOP_SET_STATUS64."); broken = true; } } @@ -171,7 +182,7 @@ static int loop_configure_fallback(int fd, const struct loop_config *c) { info_copy.lo_flags &= LOOP_SET_STATUS_SETTABLE_FLAGS; /* Since kernel commit 5db470e229e22b7eda6e23b5566e532c96fb5bc3 (kernel v5.0) the LOOP_SET_STATUS64 - * ioctl can return EAGAIN in case we change the lo_offset field, if someone else is accessing the + * ioctl can return EAGAIN in case we change the info.lo_offset field, if someone else is accessing the * block device while we try to reconfigure it. This is a pretty common case, since udev might * instantly start probing the device as soon as we attach an fd to it. Hence handle it in two ways: * first, let's take the BSD lock to ensure that udev will not step in between the point in @@ -391,6 +402,7 @@ static int loop_device_make_internal( int open_flags, uint64_t offset, uint64_t size, + uint32_t block_size, uint32_t loop_flags, int lock_op, LoopDevice **ret) { @@ -463,6 +475,7 @@ static int loop_device_make_internal( config = (struct loop_config) { .fd = fd, + .block_size = block_size, .info = { /* Use the specified flags, but configure the read-only flag from the open flags, and force autoclear */ .lo_flags = (loop_flags & ~LO_FLAGS_READ_ONLY) | ((open_flags & O_ACCMODE) == O_RDONLY ? LO_FLAGS_READ_ONLY : 0) | LO_FLAGS_AUTOCLEAR, @@ -546,6 +559,7 @@ int loop_device_make( int open_flags, uint64_t offset, uint64_t size, + uint32_t block_size, uint32_t loop_flags, int lock_op, LoopDevice **ret) { @@ -559,6 +573,7 @@ int loop_device_make( open_flags, offset, size, + block_size, loop_flags_mangle(loop_flags), lock_op, ret); @@ -622,7 +637,7 @@ int loop_device_make_by_path( direct ? "enabled" : "disabled", direct != (direct_flags != 0) ? " (O_DIRECT was requested but not supported)" : ""); - return loop_device_make_internal(path, fd, open_flags, 0, 0, loop_flags, lock_op, ret); + return loop_device_make_internal(path, fd, open_flags, 0, 0, 0, loop_flags, lock_op, ret); } static LoopDevice* loop_device_free(LoopDevice *d) { diff --git a/src/shared/loop-util.h b/src/shared/loop-util.h index 1658cd35920..e466a5abbdf 100644 --- a/src/shared/loop-util.h +++ b/src/shared/loop-util.h @@ -28,7 +28,7 @@ struct LoopDevice { /* Returns true if LoopDevice object is not actually a loopback device but some other block device we just wrap */ #define LOOP_DEVICE_IS_FOREIGN(d) ((d)->nr < 0) -int loop_device_make(int fd, int open_flags, uint64_t offset, uint64_t size, uint32_t loop_flags, int lock_op, LoopDevice **ret); +int loop_device_make(int fd, int open_flags, uint64_t offset, uint64_t size, uint32_t block_size, uint32_t loop_flags, int lock_op, LoopDevice **ret); int loop_device_make_by_path(const char *path, int open_flags, uint32_t loop_flags, int lock_op, LoopDevice **ret); int loop_device_open(sd_device *dev, int open_flags, int lock_op, LoopDevice **ret); int loop_device_open_from_fd(int fd, int open_flags, int lock_op, LoopDevice **ret); diff --git a/src/shared/user-record-show.c b/src/shared/user-record-show.c index 4e46a2fe3fb..e89c0de1207 100644 --- a/src/shared/user-record-show.c +++ b/src/shared/user-record-show.c @@ -321,6 +321,8 @@ void user_record_show(UserRecord *hr, bool show_full_group_info) { if (hr->luks_pbkdf_parallel_threads != UINT64_MAX) printf("PBKDF Thread: %" PRIu64 "\n", hr->luks_pbkdf_parallel_threads); + if (hr->luks_sector_size != UINT64_MAX) + printf(" Sector Size: %" PRIu64 "\n", hr->luks_sector_size); } else if (storage == USER_CIFS) { diff --git a/src/shared/user-record.c b/src/shared/user-record.c index dc8e3802c0b..84cbdb1d30a 100644 --- a/src/shared/user-record.c +++ b/src/shared/user-record.c @@ -58,6 +58,7 @@ UserRecord* user_record_new(void) { .luks_pbkdf_time_cost_usec = UINT64_MAX, .luks_pbkdf_memory_cost = UINT64_MAX, .luks_pbkdf_parallel_threads = UINT64_MAX, + .luks_sector_size = UINT64_MAX, .disk_usage = UINT64_MAX, .disk_free = UINT64_MAX, .disk_ceiling = UINT64_MAX, @@ -1215,6 +1216,7 @@ static int dispatch_per_machine(const char *name, JsonVariant *variant, JsonDisp { "luksPbkdfTimeCostUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_pbkdf_time_cost_usec), 0 }, { "luksPbkdfMemoryCost", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_pbkdf_memory_cost), 0 }, { "luksPbkdfParallelThreads", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_pbkdf_parallel_threads), 0 }, + { "luksSectorSize", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_sector_size), 0 }, { "luksExtraMountOptions", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, luks_extra_mount_options), 0 }, { "dropCaches", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(UserRecord, drop_caches), 0 }, { "autoResizeMode", _JSON_VARIANT_TYPE_INVALID, dispatch_auto_resize_mode, offsetof(UserRecord, auto_resize_mode), 0 }, @@ -1567,6 +1569,7 @@ int user_record_load(UserRecord *h, JsonVariant *v, UserRecordLoadFlags load_fla { "luksPbkdfTimeCostUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_pbkdf_time_cost_usec), 0 }, { "luksPbkdfMemoryCost", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_pbkdf_memory_cost), 0 }, { "luksPbkdfParallelThreads", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_pbkdf_parallel_threads), 0 }, + { "luksSectorSize", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_sector_size), 0 }, { "luksExtraMountOptions", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, luks_extra_mount_options), 0 }, { "dropCaches", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(UserRecord, drop_caches), 0 }, { "autoResizeMode", _JSON_VARIANT_TYPE_INVALID, dispatch_auto_resize_mode, offsetof(UserRecord, auto_resize_mode), 0 }, @@ -1871,6 +1874,16 @@ uint64_t user_record_luks_pbkdf_parallel_threads(UserRecord *h) { return MIN(h->luks_pbkdf_parallel_threads, UINT32_MAX); } +uint64_t user_record_luks_sector_size(UserRecord *h) { + assert(h); + + if (h->luks_sector_size == UINT64_MAX) + return 512; + + /* Allow up to 4K due to dm-crypt support and 4K alignment by the homed LUKS backend */ + return CLAMP(UINT64_C(1) << (63 - __builtin_clzl(h->luks_sector_size)), 512U, 4096U); +} + const char *user_record_luks_pbkdf_hash_algorithm(UserRecord *h) { assert(h); diff --git a/src/shared/user-record.h b/src/shared/user-record.h index 9bd77cffeb2..47f4035d453 100644 --- a/src/shared/user-record.h +++ b/src/shared/user-record.h @@ -312,6 +312,7 @@ typedef struct UserRecord { uint64_t luks_pbkdf_time_cost_usec; uint64_t luks_pbkdf_memory_cost; uint64_t luks_pbkdf_parallel_threads; + uint64_t luks_sector_size; char *luks_extra_mount_options; uint64_t disk_usage; @@ -396,6 +397,7 @@ const char* user_record_luks_pbkdf_type(UserRecord *h); usec_t user_record_luks_pbkdf_time_cost_usec(UserRecord *h); uint64_t user_record_luks_pbkdf_memory_cost(UserRecord *h); uint64_t user_record_luks_pbkdf_parallel_threads(UserRecord *h); +uint64_t user_record_luks_sector_size(UserRecord *h); const char *user_record_luks_pbkdf_hash_algorithm(UserRecord *h); gid_t user_record_gid(UserRecord *h); UserDisposition user_record_disposition(UserRecord *h); diff --git a/src/test/test-loop-block.c b/src/test/test-loop-block.c index 2fefbd2eebf..e2b97dd56ff 100644 --- a/src/test/test-loop-block.c +++ b/src/test/test-loop-block.c @@ -62,7 +62,7 @@ static void* thread_func(void *ptr) { assert_se(mkdtemp_malloc(NULL, &mounted) >= 0); - r = loop_device_make(fd, O_RDONLY, 0, UINT64_MAX, LO_FLAGS_PARTSCAN, LOCK_SH, &loop); + r = loop_device_make(fd, O_RDONLY, 0, UINT64_MAX, 0, LO_FLAGS_PARTSCAN, LOCK_SH, &loop); if (r < 0) log_error_errno(r, "Failed to allocate loopback device: %m"); assert_se(r >= 0); @@ -217,7 +217,7 @@ static int run(int argc, char *argv[]) { return 0; } - assert_se(loop_device_make(fd, O_RDWR, 0, UINT64_MAX, LO_FLAGS_PARTSCAN, LOCK_EX, &loop) >= 0); + assert_se(loop_device_make(fd, O_RDWR, 0, UINT64_MAX, 0, LO_FLAGS_PARTSCAN, LOCK_EX, &loop) >= 0); #if HAVE_BLKID assert_se(dissect_loop_device(loop, NULL, NULL, 0, &dissected) >= 0);