]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Implement --luks-sector-size for homed
authorAidan Dang <dang@aidan.gg>
Sun, 25 Sep 2022 14:12:10 +0000 (00:12 +1000)
committerLuca Boccassi <luca.boccassi@gmail.com>
Fri, 7 Oct 2022 14:36:04 +0000 (16:36 +0200)
12 files changed:
docs/USER_RECORD.md
man/homectl.xml
shell-completion/bash/homectl
src/home/homectl.c
src/home/homework-luks.c
src/partition/repart.c
src/shared/loop-util.c
src/shared/loop-util.h
src/shared/user-record-show.c
src/shared/user-record.c
src/shared/user-record.h
src/test/test-loop-block.c

index e6d2b5d99f804de5cc94493cd02771ccd50f85b3..f330e8b13a77d88ec1d7664823b0b9e4a762e4c8 100644 (file)
@@ -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`,
index 6fd5340370f8ef0e7e313e015cc09490f22f4b45..1064f056504c5153764000c34bd3e32c9e437847 100644 (file)
         <term><option>--luks-pbkdf-time-cost=</option><replaceable>SECONDS</replaceable></term>
         <term><option>--luks-pbkdf-memory-cost=</option><replaceable>BYTES</replaceable></term>
         <term><option>--luks-pbkdf-parallel-threads=</option><replaceable>THREADS</replaceable></term>
+        <term><option>--luks-sector-size=</option><replaceable>BYTES</replaceable></term>
 
         <listitem><para>Configures various cryptographic parameters for the LUKS2 storage mechanism. See
         <citerefentry
index 58a9c14c9e4668918f14fb748aa4e984071dd3ea..166a50828efc1f93fadc547e1f2afcdd16103b90 100644 (file)
@@ -96,6 +96,7 @@ _homectl() {
                         --luks-pbkdf-time-cost
                         --luks-pbkdf-memory-cost
                         --luks-pbkdf-parallel-threads
+                        --luks-sector-size
                         --nosuid
                         --nodev
                         --noexec
index 7ccee4df106dcbf7d4bf22331e7242ca26ad95d5..87a1c6883f6da2b1afe3b8ad94126a29a539eb1c 100644 (file)
@@ -1797,6 +1797,26 @@ static int parse_disk_size(const char *t, uint64_t *ret) {
         return 0;
 }
 
+static int parse_sector_size(const char *t, uint64_t *ret) {
+        int r;
+
+        assert(t);
+        assert(ret);
+
+        uint64_t ss;
+
+        r = safe_atou64(t, &ss);
+        if (r < 0)
+                return log_error_errno(r, "Failed to parse sector size parameter %s", t);
+        if (ss < 512 || ss > 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;
 
index 43e6ee02a86d178ad3cf9d9c500b2f2613439279..858d9dda194a6620a67430e1f6fe94996edfa13a 100644 (file)
@@ -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
index 721a196a9a6b26e49fa8b88118badc07a11c6aae..a0f7d4164500a6a9307e13b6bb89b40ad30809a7 100644 (file)
@@ -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",
index 84f4e79c07adb30e1e4739c28521c41624bb00ea..731ce291121edfd01f9071b06bd9958f06084644 100644 (file)
@@ -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) {
index 1658cd359208c2edb439cf004a1ac4b1b65e7301..e466a5abbdf9dd3e66bffa6799d25407c439b923 100644 (file)
@@ -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);
index 4e46a2fe3fbad1be0bd42616612ec49bf6b94097..e89c0de1207968b7b24a5d1a94dfde26b6a219de 100644 (file)
@@ -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) {
 
index dc8e3802c0ba9bbd158a733d9cc817ed7af3eeba..84cbdb1d30a998c8de6292db4f5bc4d8194c34c8 100644 (file)
@@ -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);
 
index 9bd77cffeb2ecfa9360093b96a070680c1407622..47f4035d4531e512e5cf587d6e236226cfb6a271 100644 (file)
@@ -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);
index 2fefbd2eebfce2dac33fc042dec607f3df2db042..e2b97dd56ff5c7ff3d27b112876305da2e39c7a7 100644 (file)
@@ -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);