]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
systemd-cryptenroll: add string aliases for tpm2 PCRs
authorOMOJOLA JOSHUA DAMILOLA <omojolajoshua@gmail.com>
Thu, 30 Mar 2023 07:55:41 +0000 (07:55 +0000)
committerLuca Boccassi <luca.boccassi@gmail.com>
Thu, 13 Apr 2023 11:08:32 +0000 (12:08 +0100)
Fixes #26697. RFE.

man/systemd-cryptenroll.xml
src/basic/string-table.h
src/shared/tpm2-util.c
src/shared/tpm2-util.h
src/test/test-tpm2.c
test/units/testsuite-70.sh

index 8af75d1523bcce49d134bf2dd8d89159e5a45e86..b81e6bdaab887caca8aad7719ae04ac6bca59371 100644 (file)
         <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>
 
           <!-- 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>
             <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>
 
 
               <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>
         <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
index e3a26a623ca48443b7318c8e1287ac28760ba56b..3be70dfadedf6655120fd260fca0e418378e2344 100644 (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)
index 1ffd78c7414597a748530914857406eae8a1e202..86ada60561fd9095ff575d73f19f3ebf1032199f 100644 (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);
index 6a3aea851963e39b4ca198a5dfafd255cbf53ba6..6634e2d2421e413dfd14c313459640ab77039df4 100644 (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);
index 710ce51cd0cea3825683d2f0a45c63a3c589f417..3e989251d861534f19ee2e712654068622a02e8f 100644 (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) {
index 3fbcbff8f4e1bd297b949cd426f47cdf9713fbe8..8fd23542951291b07f2ae4e255065bf068622e12 100755 (executable)
@@ -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; }