repart: make --tpm2-pcrs also configurable in repart.d/*

Add repart.d TPM2PCRs= option with the same syntax as --tpm2-pcrs.
This allows a per-partition pcr binding, and not rely on a global config
applicable to all partitions.

The global --tpm2-pcrs overrides TPM2PCRs config. If none of them
is defined, rely on default.
This commit is contained in:
Emanuele Giuseppe Esposito
2025-07-03 08:08:53 -04:00
parent d439799932
commit 49dcc89ddc
2 changed files with 59 additions and 12 deletions

View File

@@ -867,6 +867,19 @@
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
</varlistentry>
<varlistentry>
<term><varname>TPM2PCRs=</varname></term>
<listitem><para>Configures the list of PCRs to use for LUKS2 volumes configured with
the <varname>Encrypt=tpm2</varname> setting in partition files.
This option take the same parameters as the similary named options to
<citerefentry><refentrytitle>systemd-cryptenroll</refentrytitle><manvolnum>1</manvolnum></citerefentry>
and have the same effect on partitions where TPM2 enrollment is requested.
This option will be overridden by the global <varname>--tpm2-pcrs=</varname> option.</para>
<xi:include href="version-info.xml" xpointer="v259"/></listitem>
</varlistentry>
<varlistentry>
<term><varname>Compression=</varname></term>

View File

@@ -411,6 +411,8 @@ typedef struct Partition {
OrderedHashmap *subvolumes;
char *default_subvolume;
EncryptMode encrypt;
Tpm2PCRValue *tpm2_hash_pcr_values;
size_t tpm2_n_hash_pcr_values;
VerityMode verity;
char *verity_match_key;
MinimizeMode minimize;
@@ -674,6 +676,7 @@ 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);
@@ -715,6 +718,7 @@ 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);
@@ -2465,6 +2469,33 @@ 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 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);
@@ -2570,6 +2601,7 @@ 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", "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 },
@@ -4813,8 +4845,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"
@@ -4854,7 +4888,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 {
@@ -4862,8 +4896,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");
}
@@ -4871,17 +4905,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");
}
@@ -4894,8 +4928,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,
@@ -4905,8 +4939,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 */