systemd-cryptenroll: add string aliases for tpm2 PCRs

Fixes #26697. RFE.
This commit is contained in:
OMOJOLA JOSHUA DAMILOLA 2023-03-30 07:55:41 +00:00 committed by Luca Boccassi
parent 85ba4ca8f6
commit 96ead603b8
6 changed files with 146 additions and 15 deletions

View file

@ -237,7 +237,12 @@
<listitem><para>Configures the TPM2 PCRs (Platform Configuration Registers) to bind the enrollment
requested via <option>--tpm2-device=</option> to. Takes a <literal>+</literal> separated list of
numeric PCR indexes in the range 0…23. If not used, defaults to PCR 7 only. If an empty string is
specified, binds the enrollment to no PCRs at all. PCRs allow binding the enrollment to specific
specified, binds the enrollment to no PCRs at all.
Registers may also be specified using string aliases.</para>
<para>For instance <option>--tpm2-pcrs=boot-loader-code+platform-config+boot-loader-config</option> to bind to the registers
4, 1, and 5. Check the PCR definitions table below for a full list
of available string aliases.
PCRs allow binding the enrollment to specific
software versions and system state, so that the enrolled unlocking key is only accessible (may be
"unsealed") if specific trusted software and/or configuration is used.</para>
@ -251,13 +256,15 @@
<!-- See: https://github.com/tianocore-docs/edk2-TrustedBootChain/blob/main/4_Other_Trusted_Boot_Chains.md -->
<!-- See: https://wiki.archlinux.org/title/Trusted_Platform_Module#Accessing_PCR_registers -->
<tgroup cols='2' align='left' colsep='1' rowsep='1'>
<tgroup cols='3' align='left' colsep='1' rowsep='1'>
<colspec colname="pcr" />
<colspec colname="string_alias" />
<colspec colname="definition" />
<thead>
<row>
<entry>PCR</entry>
<entry>alias</entry>
<entry>Explanation</entry>
</row>
</thead>
@ -265,41 +272,43 @@
<tbody>
<row>
<entry>0</entry>
<entry>platform-code</entry>
<entry>Core system firmware executable code; changes on firmware updates</entry>
</row>
<row>
<entry>1</entry>
<entry>platform-config</entry>
<entry>Core system firmware data/host platform configuration; typically contains serial and model numbers, changes on basic hardware/CPU/RAM replacements</entry>
</row>
<row>
<entry>2</entry>
<entry>external-code</entry>
<entry>Extended or pluggable executable code; includes option ROMs on pluggable hardware</entry>
</row>
<row>
<entry>3</entry>
<entry>external-config</entry>
<entry>Extended or pluggable firmware data; includes information about pluggable hardware</entry>
</row>
<row>
<entry>4</entry>
<entry>boot-loader-code</entry>
<entry>Boot loader and additional drivers; changes on boot loader updates. The shim project will measure the PE binary it chain loads into this PCR. If the Linux kernel is invoked as UEFI PE binary, it is measured here, too. <citerefentry><refentrytitle>sd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry> measures system extension images read from the ESP here too (see <citerefentry><refentrytitle>systemd-sysext</refentrytitle><manvolnum>8</manvolnum></citerefentry>).</entry>
</row>
<row>
<entry>5</entry>
<entry>boot-loader-config</entry>
<entry>GPT/Partition table; changes when the partitions are added, modified or removed</entry>
</row>
<row>
<entry>6</entry>
<entry>Power state events; changes on system suspend/sleep</entry>
</row>
<row>
<entry>7</entry>
<entry>secure-boot-policy</entry>
<entry>Secure Boot state; changes when UEFI SecureBoot mode is enabled/disabled, or firmware certificates (PK, KEK, db, dbx, …) changes. The shim project will measure most of its (non-MOK) certificates and SBAT data into this PCR.</entry>
</row>
@ -308,39 +317,58 @@
<row>
<entry>9</entry>
<entry>kernel-initrd</entry>
<entry>The Linux kernel measures all initrds it receives into this PCR.</entry>
<!-- Strictly speaking only Linux >= 5.17 using the LOAD_FILE2 protocol, see https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=f046fff8bc4c4d8f8a478022e76e40b818f692df -->
</row>
<row>
<entry>10</entry>
<entry>ima</entry>
<entry>The IMA project measures its runtime state into this PCR.</entry>
</row>
<row>
<entry>11</entry>
<entry>kernel-boot</entry>
<entry><citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry> measures the ELF kernel image, embedded initrd and other payload of the PE image it is placed in into this PCR. Unlike PCR 4 (where the same data should be measured into), this PCR value should be easy to pre-calculate, as this only contains static parts of the PE binary. Use this PCR to bind TPM policies to a specific kernel image, possibly with an embedded initrd. <citerefentry><refentrytitle>systemd-pcrphase.service</refentrytitle><manvolnum>8</manvolnum></citerefentry> measures boot phase strings into this PCR at various milestones of the boot process.</entry>
</row>
<row>
<entry>12</entry>
<entry>kernel-config</entry>
<entry><citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry> measures the kernel command line into this PCR. <citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry> measures any manually specified kernel command line (i.e. a kernel command line that overrides the one embedded in the unified PE image) and loaded credentials into this PCR. (Note that if <command>systemd-boot</command> and <command>systemd-stub</command> are used in combination the command line might be measured twice!)</entry>
</row>
<row>
<entry>13</entry>
<entry>sysexts</entry>
<entry><citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry> measures any <citerefentry><refentrytitle>systemd-sysext</refentrytitle><manvolnum>8</manvolnum></citerefentry> images it loads and passed to the booted kernel into this PCR.</entry>
</row>
<row>
<entry>14</entry>
<entry>shim-policy</entry>
<entry>The shim project measures its "MOK" certificates and hashes into this PCR.</entry>
</row>
<row>
<entry>15</entry>
<entry>system-identity</entry>
<entry><citerefentry><refentrytitle>systemd-cryptsetup</refentrytitle><manvolnum>7</manvolnum></citerefentry> optionally measures the volume key of activated LUKS volumes into this PCR.</entry>
</row>
<row>
<entry>16</entry>
<entry>debug</entry>
<entry>Debug</entry>
</row>
<row>
<entry>23</entry>
<entry>application-support</entry>
<entry>Application Support</entry>
</row>
</tbody>
</tgroup>
</table>
@ -394,7 +422,9 @@
<option>--tpm2-public-key-pcrs=</option>: the former binds decryption to the current, specific PCR
values; the latter binds decryption to any set of PCR values for which a signature by the specified
public key can be provided. The latter is hence more useful in scenarios where software updates shell
be possible without losing access to all previously encrypted LUKS2 volumes.</para>
be possible without losing access to all previously encrypted LUKS2 volumes.
Like with <option>--tpm2-pcrs=</option>, string aliases as defined in the table above can also be used
to specify the registers, for instance <option>--tpm2-public-key-pcrs=boot-loader-code+system-identity</option>.</para>
<para>The <option>--tpm2-signature=</option> option takes a path to a TPM2 PCR signature file
as generated by the

View file

@ -95,6 +95,7 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *k
#define DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(name,type,max) \
_DEFINE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(name,type,max,) \
_DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(name,type,max,)
#define DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_FALLBACK(name,type,max) _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(name,type,max,)
#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(name,type,max) \
_DEFINE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(name,type,max,static)

View file

@ -20,6 +20,7 @@
#include "random-util.h"
#include "sha256.h"
#include "stat-util.h"
#include "string-table.h"
#include "time-util.h"
#include "tpm2-util.h"
#include "virt.h"
@ -2610,14 +2611,10 @@ int tpm2_pcr_mask_from_string(const char *arg, uint32_t *ret_mask) {
if (r < 0)
return log_error_errno(r, "Failed to parse PCR list: %s", arg);
r = safe_atou(pcr, &n);
r = pcr_index_from_string(pcr);
if (r < 0)
return log_error_errno(r, "Failed to parse PCR number: %s", pcr);
if (n >= TPM2_PCRS_MAX)
return log_error_errno(SYNTHETIC_ERRNO(ERANGE),
"PCR number out of range (valid range 0…%u): %u",
TPM2_PCRS_MAX - 1, n);
return log_error_errno(r, "Failed to parse specified PCR or specified PCR is out of range: %s", pcr);
n = r;
SET_BIT(mask, n);;
}
@ -3115,3 +3112,24 @@ int tpm2_util_pbkdf2_hmac_sha256(const void *pass,
return 0;
}
static const char* const pcr_index_table[_PCR_INDEX_MAX_DEFINED] = {
[PCR_PLATFORM_CODE] = "platform-code",
[PCR_PLATFORM_CONFIG] = "platform-config",
[PCR_EXTERNAL_CODE] = "external-code",
[PCR_EXTERNAL_CONFIG] = "external-config",
[PCR_BOOT_LOADER_CODE] = "boot-loader-code",
[PCR_BOOT_LOADER_CONFIG] = "boot-loader-config",
[PCR_SECURE_BOOT_POLICY] = "secure-boot-policy",
[PCR_KERNEL_INITRD] = "kernel-initrd",
[PCR_IMA] = "ima",
[PCR_KERNEL_BOOT] = "kernel-boot",
[PCR_KERNEL_CONFIG] = "kernel-config",
[PCR_SYSEXTS] = "sysexts",
[PCR_SHIM_POLICY] = "shim-policy",
[PCR_SYSTEM_IDENTITY] = "system-identity",
[PCR_DEBUG] = "debug",
[PCR_APPLICATION_SUPPORT] = "application-support",
};
DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_FALLBACK(pcr_index, int, TPM2_PCRS_MAX);

View file

@ -202,6 +202,31 @@ typedef enum Tpm2Support {
TPM2_SUPPORT_FULL = TPM2_SUPPORT_FIRMWARE|TPM2_SUPPORT_DRIVER|TPM2_SUPPORT_SYSTEM|TPM2_SUPPORT_SUBSYSTEM,
} Tpm2Support;
typedef enum PcrIndex {
/* The following names for PCRs 0…7 are based on the names in the "TCG PC Client Specific Platform Firmware Profile Specification" (https://trustedcomputinggroup.org/resource/pc-client-specific-platform-firmware-profile-specification/) */
PCR_PLATFORM_CODE = 0,
PCR_PLATFORM_CONFIG = 1,
PCR_EXTERNAL_CODE = 2,
PCR_EXTERNAL_CONFIG = 3,
PCR_BOOT_LOADER_CODE = 4,
PCR_BOOT_LOADER_CONFIG = 5,
PCR_SECURE_BOOT_POLICY = 7,
/* The following names for PCRs 9…15 are based on the "Linux TPM PCR Registry"
(https://uapi-group.org/specifications/specs/linux_tpm_pcr_registry/) */
PCR_KERNEL_INITRD = 9,
PCR_IMA = 10,
PCR_KERNEL_BOOT = 11,
PCR_KERNEL_CONFIG = 12,
PCR_SYSEXTS = 13,
PCR_SHIM_POLICY = 14,
PCR_SYSTEM_IDENTITY = 15,
/* As per "TCG PC Client Specific Platform Firmware Profile Specification" again, see above */
PCR_DEBUG = 16,
PCR_APPLICATION_SUPPORT = 23,
_PCR_INDEX_MAX_DEFINED = TPM2_PCRS_MAX,
_PCR_INDEX_INVALID = -EINVAL,
} PcrIndex;
Tpm2Support tpm2_support(void);
int tpm2_parse_pcr_argument(const char *arg, uint32_t *mask);
@ -214,3 +239,5 @@ int tpm2_util_pbkdf2_hmac_sha256(const void *pass,
const void *salt,
size_t saltlen,
uint8_t res[static SHA256_DIGEST_SIZE]);
int pcr_index_from_string(const char *s);

View file

@ -26,6 +26,53 @@ TEST(tpm2_mask_from_string) {
test_tpm2_pcr_mask_from_string_one("0,2", 5, 0);
test_tpm2_pcr_mask_from_string_one("0+2", 5, 0);
test_tpm2_pcr_mask_from_string_one("foo", 0, -EINVAL);
test_tpm2_pcr_mask_from_string_one("7+application-support", 8388736, 0);
test_tpm2_pcr_mask_from_string_one("8+boot-loader-code", 272, 0);
test_tpm2_pcr_mask_from_string_one("6+boot-loader-code,44", 0, -EINVAL);
test_tpm2_pcr_mask_from_string_one("7,shim-policy,4", 16528, 0);
test_tpm2_pcr_mask_from_string_one("sysexts,shim-policy+kernel-boot", 26624, 0);
test_tpm2_pcr_mask_from_string_one("sysexts,shim+kernel-boot", 0, -EINVAL);
test_tpm2_pcr_mask_from_string_one("sysexts+17+23", 8527872, 0);
test_tpm2_pcr_mask_from_string_one("debug+24", 16842752, 0);
}
TEST(pcr_index_from_string) {
assert_se(pcr_index_from_string("platform-code") == 0);
assert_se(pcr_index_from_string("0") == 0);
assert_se(pcr_index_from_string("platform-config") == 1);
assert_se(pcr_index_from_string("1") == 1);
assert_se(pcr_index_from_string("external-code") == 2);
assert_se(pcr_index_from_string("2") == 2);
assert_se(pcr_index_from_string("external-config") == 3);
assert_se(pcr_index_from_string("3") == 3);
assert_se(pcr_index_from_string("boot-loader-code") == 4);
assert_se(pcr_index_from_string("4") == 4);
assert_se(pcr_index_from_string("boot-loader-config") == 5);
assert_se(pcr_index_from_string("5") == 5);
assert_se(pcr_index_from_string("secure-boot-policy") == 7);
assert_se(pcr_index_from_string("7") == 7);
assert_se(pcr_index_from_string("kernel-initrd") == 9);
assert_se(pcr_index_from_string("9") == 9);
assert_se(pcr_index_from_string("ima") == 10);
assert_se(pcr_index_from_string("10") == 10);
assert_se(pcr_index_from_string("kernel-boot") == 11);
assert_se(pcr_index_from_string("11") == 11);
assert_se(pcr_index_from_string("kernel-config") == 12);
assert_se(pcr_index_from_string("12") == 12);
assert_se(pcr_index_from_string("sysexts") == 13);
assert_se(pcr_index_from_string("13") == 13);
assert_se(pcr_index_from_string("shim-policy") == 14);
assert_se(pcr_index_from_string("14") == 14);
assert_se(pcr_index_from_string("system-identity") == 15);
assert_se(pcr_index_from_string("15") == 15);
assert_se(pcr_index_from_string("debug") == 16);
assert_se(pcr_index_from_string("16") == 16);
assert_se(pcr_index_from_string("application-support") == 23);
assert_se(pcr_index_from_string("23") == 23);
assert_se(pcr_index_from_string("hello") == -EINVAL);
assert_se(pcr_index_from_string("8") == 8);
assert_se(pcr_index_from_string("44") == -EINVAL);
assert_se(pcr_index_from_string("-5") == -EINVAL);
}
TEST(tpm2_util_pbkdf2_hmac_sha256) {

View file

@ -268,6 +268,14 @@ systemd-cryptenroll --tpm2-public-key-pcrs=key $img_2 && { echo 'unexpected succ
systemd-cryptenroll --tpm2-pcrs=key $img_2 && { echo 'unexpected success'; exit 1; }
systemd-cryptenroll --tpm2-pcrs=44+8 $img_2 && { echo 'unexpected success'; exit 1; }
systemd-cryptenroll --tpm2-pcrs=8 $img_2
systemd-cryptenroll --tpm2-pcrs=hello $img_2 && { echo 'unexpected success'; exit 1; }
systemd-cryptenroll --tpm2-pcrs=boot-loader-code+boot-loader-config $img_2
#wipe_slots
systemd-cryptenroll --wipe-slot $img_2 && { echo 'unexpected success'; exit 1; }