diff --git a/man/repart.d.xml b/man/repart.d.xml index 87d04d0e93..50bc76d225 100644 --- a/man/repart.d.xml +++ b/man/repart.d.xml @@ -869,6 +869,31 @@ + + TPM2PCRs= + + Configures the list of PCRs to use for LUKS2 volumes configured with + the Encrypt=tpm2 setting in partition files. + This option take the same parameters as the similary named options to + systemd-cryptenroll1 + and have the same effect on partitions where TPM2 enrollment is requested. + This option will be overridden by the global --tpm2-pcrs= option. + + + + + + KeyFile= + + Takes a file system path. This path must be absolute, otherwise the option is ignored. + Configures the encryption key to use when setting up LUKS2 volumes configured with the + Encrypt=key-file setting in partition files. Please refer to the documentation of + --key-file= for more details. This option will be overridden by the global + --key-file= option. + + + + Compression= diff --git a/man/systemd-repart.xml b/man/systemd-repart.xml index 7a739752eb..317ae05826 100644 --- a/man/systemd-repart.xml +++ b/man/systemd-repart.xml @@ -338,9 +338,9 @@ volumes configured with the Encrypt=key-file setting in partition files. Should refer to a regular file containing the key, or an AF_UNIX stream socket in the file system. In the latter case, a connection is made to it and the key read from it. If this switch - is not specified, the empty key (i.e. zero length key) is used. This behaviour is useful for setting - up encrypted partitions during early first boot that receive their user-supplied password only in a - later setup step. + is not specified, and no KeyFile= is specified in the partition file, the empty + key (i.e. zero length key) is used. This behaviour is useful for setting up encrypted partitions during + early first boot that receive their user-supplied password only in a later setup step. diff --git a/src/repart/repart.c b/src/repart/repart.c index 49f7d0745c..433798fa57 100644 --- a/src/repart/repart.c +++ b/src/repart/repart.c @@ -169,8 +169,7 @@ static bool arg_size_auto = false; static sd_json_format_flags_t arg_json_format_flags = SD_JSON_FORMAT_OFF; static PagerFlags arg_pager_flags = 0; static bool arg_legend = true; -static void *arg_key = NULL; -static size_t arg_key_size = 0; +static struct iovec arg_key = {}; static char *arg_private_key = NULL; static KeySourceType arg_private_key_source_type = OPENSSL_KEY_SOURCE_FILE; static char *arg_private_key_source = NULL; @@ -208,7 +207,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_node, freep); STATIC_DESTRUCTOR_REGISTER(arg_root, freep); STATIC_DESTRUCTOR_REGISTER(arg_image, freep); STATIC_DESTRUCTOR_REGISTER(arg_definitions, strv_freep); -STATIC_DESTRUCTOR_REGISTER(arg_key, erase_and_freep); +STATIC_DESTRUCTOR_REGISTER(arg_key, iovec_done_erase); STATIC_DESTRUCTOR_REGISTER(arg_private_key, freep); STATIC_DESTRUCTOR_REGISTER(arg_private_key_source, freep); STATIC_DESTRUCTOR_REGISTER(arg_certificate, freep); @@ -413,6 +412,9 @@ typedef struct Partition { OrderedHashmap *subvolumes; char *default_subvolume; EncryptMode encrypt; + struct iovec key; + Tpm2PCRValue *tpm2_hash_pcr_values; + size_t tpm2_n_hash_pcr_values; VerityMode verity; char *verity_match_key; MinimizeMode minimize; @@ -676,10 +678,13 @@ static Partition* partition_free(Partition *p) { strv_free(p->make_symlinks); ordered_hashmap_free(p->subvolumes); free(p->default_subvolume); + free(p->tpm2_hash_pcr_values); free(p->verity_match_key); free(p->compression); free(p->compression_level); + iovec_done_erase(&p->key); + copy_files_free_many(p->copy_files, p->n_copy_files); iovec_done(&p->roothash); @@ -717,10 +722,13 @@ static void partition_foreignize(Partition *p) { p->make_symlinks = strv_free(p->make_symlinks); p->subvolumes = ordered_hashmap_free(p->subvolumes); p->default_subvolume = mfree(p->default_subvolume); + p->tpm2_hash_pcr_values = mfree(p->tpm2_hash_pcr_values); p->verity_match_key = mfree(p->verity_match_key); p->compression = mfree(p->compression); p->compression_level = mfree(p->compression_level); + iovec_done_erase(&p->key); + copy_files_free_many(p->copy_files, p->n_copy_files); p->copy_files = NULL; p->n_copy_files = 0; @@ -2467,6 +2475,78 @@ static int config_parse_encrypted_volume( return 0; } +static int config_parse_tpm2_pcrs( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + Partition *partition = ASSERT_PTR(data); + + assert(rvalue); + + if (isempty(rvalue)) { + /* Clear existing PCR values if empty */ + partition->tpm2_hash_pcr_values = mfree(partition->tpm2_hash_pcr_values); + partition->tpm2_n_hash_pcr_values = 0; + return 0; + } + + return tpm2_parse_pcr_argument_append(rvalue, &partition->tpm2_hash_pcr_values, + &partition->tpm2_n_hash_pcr_values); +} + +static int parse_key_file(const char *filename, struct iovec *key) { + _cleanup_(erase_and_freep) char *k = NULL; + size_t n = 0; + int r; + + r = read_full_file_full( + AT_FDCWD, filename, + /* offset= */ UINT64_MAX, + /* size= */ SIZE_MAX, + READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE|READ_FULL_FILE_CONNECT_SOCKET, + /* bind_name= */ NULL, + &k, &n); + if (r < 0) + return log_error_errno(r, "Failed to read key file '%s': %m", filename); + + iovec_done_erase(key); + *key = IOVEC_MAKE(TAKE_PTR(k), n); + + return 0; +} + +static int config_parse_key_file( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + Partition *partition = ASSERT_PTR(userdata); + + assert(rvalue); + + if (isempty(rvalue)) { + iovec_done_erase(&partition->key); + return 0; + } + + return parse_key_file(rvalue, &partition->key); +} + static DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_verity, verity_mode, VerityMode, VERITY_OFF); static DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_minimize, minimize_mode, MinimizeMode, MINIMIZE_OFF); @@ -2572,6 +2652,8 @@ static int partition_read_definition(Partition *p, const char *path, const char { "Partition", "VerityHashBlockSizeBytes", config_parse_block_size, 0, &p->verity_hash_block_size }, { "Partition", "MountPoint", config_parse_mountpoint, 0, p }, { "Partition", "EncryptedVolume", config_parse_encrypted_volume, 0, p }, + { "Partition", "TPM2PCRs", config_parse_tpm2_pcrs, 0, p }, + { "Partition", "KeyFile", config_parse_key_file, 0, p }, { "Partition", "Compression", config_parse_string, CONFIG_PARSE_STRING_SAFE_AND_ASCII, &p->compression }, { "Partition", "CompressionLevel", config_parse_string, CONFIG_PARSE_STRING_SAFE_AND_ASCII, &p->compression_level }, { "Partition", "SupplementFor", config_parse_string, 0, &p->supplement_for_name }, @@ -4793,18 +4875,21 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta return log_error_errno(r, "Failed to LUKS2 format future partition: %m"); 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; + r = sym_crypt_keyslot_add_by_volume_key( cd, CRYPT_ANY_SLOT, NULL, VOLUME_KEY_SIZE, - strempty(arg_key), - arg_key_size); + strempty(iovec_key->iov_base), + iovec_key->iov_len); if (r < 0) return log_error_errno(r, "Failed to add LUKS2 key: %m"); - passphrase = strempty(arg_key); - passphrase_size = arg_key_size; + passphrase = strempty(iovec_key->iov_base); + passphrase_size = iovec_key->iov_len; } if (IN_SET(p->encrypt, ENCRYPT_TPM2, ENCRYPT_KEY_FILE_TPM2)) { @@ -4815,8 +4900,10 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta ssize_t base64_encoded_size; int keyslot; TPM2Flags flags = 0; + Tpm2PCRValue *pcr_values = arg_tpm2_n_hash_pcr_values > 0 ? arg_tpm2_hash_pcr_values : p->tpm2_hash_pcr_values; + size_t n_pcr_values = arg_tpm2_n_hash_pcr_values > 0 ? arg_tpm2_n_hash_pcr_values : p->tpm2_n_hash_pcr_values; - if (arg_tpm2_n_hash_pcr_values == 0 && + if (n_pcr_values == 0 && arg_tpm2_public_key_pcr_mask == 0 && !arg_tpm2_pcrlock) log_notice("Notice: encrypting future partition %" PRIu64 ", locking against TPM2 with an empty policy, i.e. without any state or access restrictions.\n" @@ -4856,7 +4943,7 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta if (r < 0) return r; - if (!tpm2_pcr_values_has_all_values(arg_tpm2_hash_pcr_values, arg_tpm2_n_hash_pcr_values)) + if (!tpm2_pcr_values_has_all_values(pcr_values, n_pcr_values)) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Must provide all PCR values when using TPM2 device key."); } else { @@ -4864,8 +4951,8 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta if (r < 0) return r; - if (!tpm2_pcr_values_has_all_values(arg_tpm2_hash_pcr_values, arg_tpm2_n_hash_pcr_values)) { - r = tpm2_pcr_read_missing_values(tpm2_context, arg_tpm2_hash_pcr_values, arg_tpm2_n_hash_pcr_values); + if (!tpm2_pcr_values_has_all_values(pcr_values, n_pcr_values)) { + r = tpm2_pcr_read_missing_values(tpm2_context, pcr_values, n_pcr_values); if (r < 0) return log_error_errno(r, "Could not read pcr values: %m"); } @@ -4873,17 +4960,17 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta uint16_t hash_pcr_bank = 0; uint32_t hash_pcr_mask = 0; - if (arg_tpm2_n_hash_pcr_values > 0) { + if (n_pcr_values > 0) { size_t hash_count; - r = tpm2_pcr_values_hash_count(arg_tpm2_hash_pcr_values, arg_tpm2_n_hash_pcr_values, &hash_count); + r = tpm2_pcr_values_hash_count(pcr_values, n_pcr_values, &hash_count); if (r < 0) return log_error_errno(r, "Could not get hash count: %m"); if (hash_count > 1) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Multiple PCR banks selected."); - hash_pcr_bank = arg_tpm2_hash_pcr_values[0].hash; - r = tpm2_pcr_values_to_mask(arg_tpm2_hash_pcr_values, arg_tpm2_n_hash_pcr_values, hash_pcr_bank, &hash_pcr_mask); + hash_pcr_bank = pcr_values[0].hash; + r = tpm2_pcr_values_to_mask(pcr_values, n_pcr_values, hash_pcr_bank, &hash_pcr_mask); if (r < 0) return log_error_errno(r, "Could not get hash mask: %m"); } @@ -4896,8 +4983,8 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta /* If both PCR public key unlock and pcrlock unlock is selected, then shard the encryption key. */ r = tpm2_calculate_sealing_policy( - arg_tpm2_hash_pcr_values, - arg_tpm2_n_hash_pcr_values, + pcr_values, + n_pcr_values, iovec_is_set(&pubkey) ? &public : NULL, /* use_pin= */ false, arg_tpm2_pcrlock && !iovec_is_set(&pubkey) ? &pcrlock_policy : NULL, @@ -4907,8 +4994,8 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta if (arg_tpm2_pcrlock && iovec_is_set(&pubkey)) { r = tpm2_calculate_sealing_policy( - arg_tpm2_hash_pcr_values, - arg_tpm2_n_hash_pcr_values, + pcr_values, + n_pcr_values, /* public= */ NULL, /* Turn this one off for the 2nd shard */ /* use_pin= */ false, &pcrlock_policy, /* But turn this one on */ @@ -8829,20 +8916,9 @@ static int parse_argv(int argc, char *argv[], X509 **ret_certificate, EVP_PKEY * break; case ARG_KEY_FILE: { - _cleanup_(erase_and_freep) char *k = NULL; - size_t n = 0; - - r = read_full_file_full( - AT_FDCWD, optarg, UINT64_MAX, SIZE_MAX, - READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE|READ_FULL_FILE_CONNECT_SOCKET, - NULL, - &k, &n); + r = parse_key_file(optarg, &arg_key); if (r < 0) - return log_error_errno(r, "Failed to read key file '%s': %m", optarg); - - erase_and_free(arg_key); - arg_key = TAKE_PTR(k); - arg_key_size = n; + return r; break; }