From: Vitaly Kuznetsov Date: Wed, 14 Jan 2026 08:51:33 +0000 (+0100) Subject: repart: Add fixate_volume_key crypttab option generation X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4754289ddf8bce0cee4876639d1f1c009a2c111a;p=thirdparty%2Fsystemd.git repart: Add fixate_volume_key crypttab option generation Add an option to capture the expected LUKS volume key hash and record it to the generated crypttab. --- diff --git a/man/crypttab.xml b/man/crypttab.xml index dbbeb3fd5bf..f0d0ead947c 100644 --- a/man/crypttab.xml +++ b/man/crypttab.xml @@ -1023,6 +1023,11 @@ if it has a different one. The expected hash matches the digest which is measured to the sha256 PCR bank of the TPM2 when is used. + For newly created LUKS volumes, the expected hash can be generated by + systemd-repart8. + For additional details, see the description at + repart.d5. + @@ -1136,6 +1141,8 @@ external /dev/sda3 keyfile:LABEL=keydev keyfile-timeout=10s,cipher=xchac systemd-cryptsetup@.service8 systemd-cryptsetup-generator8 systemd-cryptenroll1 + systemd-repart8 + repart.d5 fstab5 cryptsetup8 mkswap8 diff --git a/man/repart.d.xml b/man/repart.d.xml index 5d325577456..5cdf2b0f346 100644 --- a/man/repart.d.xml +++ b/man/repart.d.xml @@ -869,13 +869,18 @@ EncryptedVolume= Specifies how the encrypted partition should be set up. Takes at least one and at most - three fields separated with a colon (:). The first field specifies the encrypted + four fields separated with a colon (:). The first field specifies the encrypted volume name under /dev/mapper/. If not specified, luks-UUID will be used where UUID is the LUKS UUID. The second field specifies the keyfile to use following the same format as specified in crypttab. The third field specifies a - comma-delimited list of crypttab options. These fields correspond to the first, third and fourth + comma-delimited list of crypttab options. These three fields correspond to the first, third and fourth column of the crypttab5 format. + The fourth field contains extra options, which are not directly reflected to crypttab options. Currently, + the only supported extra option is fixate-volume-key. When specified, the volume key + hash will be taken upon LUKS volume formatting and the result will be added to the list of crypttab options, + i.e. fixate-volume-key=expected-hash will be added to the fourth column of the generated + crypttab. Note that this setting is only taken into account when diff --git a/src/repart/repart.c b/src/repart/repart.c index 7c19f102877..48e05d3769c 100644 --- a/src/repart/repart.c +++ b/src/repart/repart.c @@ -318,6 +318,7 @@ typedef struct PartitionEncryptedVolume { char *name; char *keyfile; char *options; + bool fixate_volume_key; } PartitionEncryptedVolume; static PartitionEncryptedVolume* partition_encrypted_volume_free(PartitionEncryptedVolume *c) { @@ -2547,7 +2548,8 @@ static int config_parse_encrypted_volume( void *data, void *userdata) { - _cleanup_free_ char *volume = NULL, *keyfile = NULL, *options = NULL; + _cleanup_free_ char *volume = NULL, *keyfile = NULL, *options = NULL, *extra = NULL; + bool fixate_volume_key = false; Partition *p = ASSERT_PTR(data); int r; @@ -2558,7 +2560,7 @@ static int config_parse_encrypted_volume( const char *q = rvalue; r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE|EXTRACT_DONT_COALESCE_SEPARATORS|EXTRACT_UNQUOTE, - &volume, &keyfile, &options); + &volume, &keyfile, &options, &extra); if (r == -ENOMEM) return log_oom(); if (r < 0) { @@ -2589,10 +2591,29 @@ static int config_parse_encrypted_volume( if (!p->encrypted_volume) return log_oom(); + for (const char *e = extra;;) { + _cleanup_free_ char *word = NULL; + + r = extract_first_word(&e, &word, ",", EXTRACT_DONT_COALESCE_SEPARATORS | EXTRACT_UNESCAPE_SEPARATORS); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Failed to parse extra options '%s', ignoring", word); + break; + } + if (r == 0) + break; + + if (streq(word, "fixate-volume-key")) + fixate_volume_key = true; + else + log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown extra option '%s', ignoring", word); + } + *p->encrypted_volume = (PartitionEncryptedVolume) { .name = TAKE_PTR(volume), .keyfile = TAKE_PTR(keyfile), .options = TAKE_PTR(options), + .fixate_volume_key = fixate_volume_key, }; return 0; @@ -5174,6 +5195,44 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta if (r < 0) return log_error_errno(r, "Failed to LUKS2 format future partition: %m"); + if (p->encrypted_volume && p->encrypted_volume->fixate_volume_key) { + _cleanup_free_ char *key_id = NULL, *hash_option = NULL; + + r = sym_crypt_get_volume_key_size(cd); + if (r < 0) + return log_error_errno(r, "Failed to determine volume key size: %m"); + if (r == 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Volume key has zero size and 'fixate-volume-key' is used"); + + _cleanup_(iovec_done) struct iovec vk = { + .iov_base = malloc(r), + .iov_len = r, + }; + + if (!vk.iov_base) + return log_oom(); + + r = sym_crypt_volume_key_get(cd, CRYPT_ANY_SLOT, (char *) vk.iov_base, &vk.iov_len, NULL, 0); + if (r < 0) + return log_error_errno(r, "Failed to get volume key: %m"); + + r = cryptsetup_get_volume_key_id( + cd, + /* volume_name= */ p->encrypted_volume->name, + /* volume_key= */ vk.iov_base, + /* volume_key_size= */ vk.iov_len, + /* ret= */ &key_id); + if (r < 0) + return log_error_errno(r, "Failed to get volume key hash: %m"); + + hash_option = strjoin("fixate-volume-key=", key_id); + if (!hash_option) + return log_oom(); + + if (!strextend_with_separator(&p->encrypted_volume->options, ",", hash_option)) + return log_oom(); + } + if (IN_SET(p->encrypt, ENCRYPT_KEY_FILE, ENCRYPT_KEY_FILE_TPM2)) { /* Use partition-specific key if available, otherwise fall back to global key */ struct iovec *iovec_key = arg_key.iov_base ? &arg_key : &p->key;