mirror of
https://github.com/morgan9e/systemd
synced 2026-04-14 08:25:20 +09:00
systemd-repart: add encryption configs into repart.d/* (#38052)
As explained in https://github.com/systemd/systemd/issues/37892, it would be nice to define per-partition PCRs/key file to use. The global default config will be still defined as cmdline options, and `TPM2PCRs=` and `KeyFile=` will be overriden by them.
This commit is contained in:
@@ -869,6 +869,31 @@
|
||||
<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>KeyFile=</varname></term>
|
||||
|
||||
<listitem><para>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
|
||||
<varname>Encrypt=key-file</varname> setting in partition files. Please refer to the documentation of
|
||||
<varname>--key-file=</varname> for more details. This option will be overridden by the global
|
||||
<varname>--key-file=</varname> option.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v259"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>Compression=</varname></term>
|
||||
|
||||
|
||||
@@ -338,9 +338,9 @@
|
||||
volumes configured with the <varname>Encrypt=key-file</varname> setting in partition files. Should
|
||||
refer to a regular file containing the key, or an <constant>AF_UNIX</constant> 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.</para>
|
||||
is not specified, and no <varname>KeyFile=</varname> 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.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v247"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user