From 39e0c237f19be47d24c1ca4372808686e175d11a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 8 Nov 2023 19:02:03 +0100 Subject: [PATCH 1/2] stub: pick up confexts from the ESP as well MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This does what we do for system extension also for configuration extension. This is complicated by the fact that we previously looked for .d/*.raw for system extensions. We want to measure sysexts and confexts to different PCRs (13 vs. 12) hence we must distinguish them, but *.raw would match both kinds. This commit solves this via the following mechanism: we'll load confexts from *.confext.raw and sysexts from *.raw but will then enclude *.confext.raw from the latter. This preserves compatibility but allows us to somewhat reasonable distinguish both types of images. The documentation is updated not going into this detail though, and instead now claims that sysexts shall be *.sysext.raw and confexts *.confext.raw even though we actually are more lenient than this. This is simply to push people towards using the longer, more descriptive suffixes. I added an XML comment () about this to the docs, so that whenever somebody notices the difference between code and docs understands why and leaves it that way. --- man/systemd-stub.xml | 66 ++++++++++++++++++++++----- src/boot/bootctl-status.c | 17 +++---- src/boot/efi/cpio.c | 3 ++ src/boot/efi/cpio.h | 1 + src/boot/efi/stub.c | 39 ++++++++++++---- src/fundamental/efivars-fundamental.h | 1 + 6 files changed, 99 insertions(+), 28 deletions(-) diff --git a/man/systemd-stub.xml b/man/systemd-stub.xml index 5b47d752ca..e489a138d6 100644 --- a/man/systemd-stub.xml +++ b/man/systemd-stub.xml @@ -32,6 +32,8 @@ ESP/.../foo.efi.extra.d/*.addon.efi ESP/.../foo.efi.extra.d/*.cred ESP/.../foo.efi.extra.d/*.raw + ESP/.../foo.efi.extra.d/*.sysext.raw + ESP/.../foo.efi.extra.d/*.confext.raw ESP/loader/addons/*.addon.efi ESP/loader/credentials/*.cred @@ -153,14 +155,28 @@ details on encrypted credentials. The generated cpio archive is measured into TPM PCR 12 (if a TPM is present). - Similarly, files foo.efi.extra.d/*.raw - are packed up in a cpio archive and placed in the /.extra/sysext/ - directory in the initrd file hierarchy. This is supposed to be used to pass additional system extension - images to the initrd. See + Similarly, files + foo.efi.extra.d/*.sysext.raw are packed up in a + cpio archive and placed in the /.extra/sysext/ directory in the + initrd file hierarchy. This is supposed to be used to pass additional system extension images to the + initrd. See systemd-sysext8 for details on system extension images. The generated cpio archive containing these system extension images is measured into TPM PCR 13 (if a TPM is present). + + + Similarly, files + foo.efi.extra.d/*.confext.raw are packed up in a + cpio archive and placed in the /.extra/confext/ directory in + the initrd file hierarchy. This is supposed to be used to pass additional configuration extension + images to the initrd. See + systemd-confext8 for + details on configuration extension images. The generated cpio archive containing + these system extension images is measured into TPM PCR 12 (if a TPM is present). + Similarly, files foo.efi.extra.d/*.addon.efi are loaded and verified as PE binaries, and a .cmdline section is parsed from them. Addons are supposed to be @@ -218,9 +234,10 @@ Also note that the Linux kernel will measure all initrds it receives into TPM PCR 9. This means every type of initrd will be measured two or three times: the initrd embedded in the kernel image will be - measured to PCR 4, PCR 9 and PCR 11; the initrd synthesized from credentials will be measured to both PCR - 9 and PCR 12; the initrd synthesized from system extensions will be measured to both PCR 4 and PCR - 9. Let's summarize the OS resources and the PCRs they are measured to: + measured to PCR 4, PCR 9 and PCR 11; the initrd synthesized from credentials (and the one synthesized + from configuration extensions) will be measured to both PCR 9 and PCR 12; the initrd synthesized from + system extensions will be measured to both PCR 4 and PCR 9. Let's summarize the OS resources and the PCRs + they are measured to: OS Resource PCR Summary @@ -291,6 +308,11 @@ System Extensions (synthesized initrd from companion files)9 + 13 + + + Configuration Extensions (synthesized initrd from companion files) + 9 + 12 +
@@ -371,13 +393,24 @@ StubPcrInitRDSysExts - The PCR register index the systemd extensions for the initrd, which are picked up - from the file system the kernel image is located on. Formatted as decimal ASCII string (e.g. + The PCR register index the system extensions for the initrd, which are picked up from + the file system the kernel image is located on. Formatted as decimal ASCII string (e.g. 13). This variable is set if a measurement was successfully completed, and remains unset otherwise. + + + StubPcrInitRDConfExts + + The PCR register index the configuration extensions for the initrd, which are picked + up from the file system the kernel image is located on. Formatted as decimal ASCII string (e.g. + 12). This variable is set if a measurement was successfully completed, and remains + unset otherwise. + + + Note that some of the variables above may also be set by the boot loader. The stub will only set @@ -422,14 +455,23 @@ - /.extra/sysext/*.raw - System extension image files (suffix .raw) that are placed next to - the unified kernel image (as described above) are copied into the + /.extra/sysext/*.sysext.raw + System extension image files (suffix .sysext.raw) that are placed + next to the unified kernel image (as described above) are copied into the /.extra/sysext/ directory in the initrd execution environment. + + /.extra/confext/*.confext.raw + Configuration extension image files (suffix .confext.raw) that are + placed next to the unified kernel image (as described above) are copied into the + /.extra/confext/ directory in the initrd execution environment. + + + + /.extra/tpm2-pcr-signature.json The TPM2 PCR signature JSON object included in the .pcrsig PE diff --git a/src/boot/bootctl-status.c b/src/boot/bootctl-status.c index c8041c4bd1..337b3eb4b5 100644 --- a/src/boot/bootctl-status.c +++ b/src/boot/bootctl-status.c @@ -374,14 +374,15 @@ int verb_status(int argc, char *argv[], void *userdata) { uint64_t flag; const char *name; } stub_flags[] = { - { EFI_STUB_FEATURE_REPORT_BOOT_PARTITION, "Stub sets ESP information" }, - { EFI_STUB_FEATURE_PICK_UP_CREDENTIALS, "Picks up credentials from boot partition" }, - { EFI_STUB_FEATURE_PICK_UP_SYSEXTS, "Picks up system extension images from boot partition" }, - { EFI_STUB_FEATURE_THREE_PCRS, "Measures kernel+command line+sysexts" }, - { EFI_STUB_FEATURE_RANDOM_SEED, "Support for passing random seed to OS" }, - { EFI_STUB_FEATURE_CMDLINE_ADDONS, "Pick up .cmdline from addons" }, - { EFI_STUB_FEATURE_CMDLINE_SMBIOS, "Pick up .cmdline from SMBIOS Type 11" }, - { EFI_STUB_FEATURE_DEVICETREE_ADDONS, "Pick up .dtb from addons" }, + { EFI_STUB_FEATURE_REPORT_BOOT_PARTITION, "Stub sets ESP information" }, + { EFI_STUB_FEATURE_PICK_UP_CREDENTIALS, "Picks up credentials from boot partition" }, + { EFI_STUB_FEATURE_PICK_UP_SYSEXTS, "Picks up system extension images from boot partition" }, + { EFI_STUB_FEATURE_PICK_UP_CONFEXTS, "Picks up configuration extension images from boot partition" }, + { EFI_STUB_FEATURE_THREE_PCRS, "Measures kernel+command line+sysexts" }, + { EFI_STUB_FEATURE_RANDOM_SEED, "Support for passing random seed to OS" }, + { EFI_STUB_FEATURE_CMDLINE_ADDONS, "Pick up .cmdline from addons" }, + { EFI_STUB_FEATURE_CMDLINE_SMBIOS, "Pick up .cmdline from SMBIOS Type 11" }, + { EFI_STUB_FEATURE_DEVICETREE_ADDONS, "Pick up .dtb from addons" }, }; _cleanup_free_ char *fw_type = NULL, *fw_info = NULL, *loader = NULL, *loader_path = NULL, *stub = NULL; sd_id128_t loader_part_uuid = SD_ID128_NULL; diff --git a/src/boot/efi/cpio.c b/src/boot/efi/cpio.c index 5b90e17c41..1408d74bb6 100644 --- a/src/boot/efi/cpio.c +++ b/src/boot/efi/cpio.c @@ -305,6 +305,7 @@ EFI_STATUS pack_cpio( EFI_LOADED_IMAGE_PROTOCOL *loaded_image, const char16_t *dropin_dir, const char16_t *match_suffix, + const char16_t *exclude_suffix, const char *target_dir_prefix, uint32_t dir_mode, uint32_t access_mode, @@ -367,6 +368,8 @@ EFI_STATUS pack_cpio( continue; if (match_suffix && !endswith_no_case(dirent->FileName, match_suffix)) continue; + if (exclude_suffix && endswith_no_case(dirent->FileName, exclude_suffix)) + continue; if (!is_ascii(dirent->FileName)) continue; if (strlen16(dirent->FileName) > 255) /* Max filename size on Linux */ diff --git a/src/boot/efi/cpio.h b/src/boot/efi/cpio.h index 26851e3c15..9d14fa15a2 100644 --- a/src/boot/efi/cpio.h +++ b/src/boot/efi/cpio.h @@ -8,6 +8,7 @@ EFI_STATUS pack_cpio( EFI_LOADED_IMAGE_PROTOCOL *loaded_image, const char16_t *dropin_dir, const char16_t *match_suffix, + const char16_t *exclude_suffix, const char *target_dir_prefix, uint32_t dir_mode, uint32_t access_mode, diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c index 7ef3e76544..58586f942f 100644 --- a/src/boot/efi/stub.c +++ b/src/boot/efi/stub.c @@ -91,6 +91,7 @@ static void export_variables(EFI_LOADED_IMAGE_PROTOCOL *loaded_image) { EFI_STUB_FEATURE_REPORT_BOOT_PARTITION | /* We set LoaderDevicePartUUID */ EFI_STUB_FEATURE_PICK_UP_CREDENTIALS | /* We pick up credentials from the boot partition */ EFI_STUB_FEATURE_PICK_UP_SYSEXTS | /* We pick up system extensions from the boot partition */ + EFI_STUB_FEATURE_PICK_UP_CONFEXTS | /* We pick up configuration extensions from the boot partition */ EFI_STUB_FEATURE_THREE_PCRS | /* We can measure kernel image, parameters and sysext */ EFI_STUB_FEATURE_RANDOM_SEED | /* We pass a random seed to the kernel */ EFI_STUB_FEATURE_CMDLINE_ADDONS | /* We pick up .cmdline addons */ @@ -497,8 +498,8 @@ static EFI_STATUS load_addons( } static EFI_STATUS run(EFI_HANDLE image) { - _cleanup_free_ void *credential_initrd = NULL, *global_credential_initrd = NULL, *sysext_initrd = NULL, *pcrsig_initrd = NULL, *pcrpkey_initrd = NULL; - size_t credential_initrd_size = 0, global_credential_initrd_size = 0, sysext_initrd_size = 0, pcrsig_initrd_size = 0, pcrpkey_initrd_size = 0; + _cleanup_free_ void *credential_initrd = NULL, *global_credential_initrd = NULL, *sysext_initrd = NULL, *confext_initrd = NULL, *pcrsig_initrd = NULL, *pcrpkey_initrd = NULL; + size_t credential_initrd_size = 0, global_credential_initrd_size = 0, sysext_initrd_size = 0, confext_initrd_size = 0, pcrsig_initrd_size = 0, pcrpkey_initrd_size = 0; void **dt_bases_addons_global = NULL, **dt_bases_addons_uki = NULL; char16_t **dt_filenames_addons_global = NULL, **dt_filenames_addons_uki = NULL; _cleanup_free_ size_t *dt_sizes_addons_global = NULL, *dt_sizes_addons_uki = NULL; @@ -510,7 +511,7 @@ static EFI_STATUS run(EFI_HANDLE image) { _cleanup_free_ char16_t *cmdline = NULL, *cmdline_addons_global = NULL, *cmdline_addons_uki = NULL; int sections_measured = -1, parameters_measured = -1; _cleanup_free_ char *uname = NULL; - bool sysext_measured = false, m; + bool sysext_measured = false, confext_measured = false, m; uint64_t loader_features = 0; EFI_STATUS err; @@ -660,8 +661,9 @@ static EFI_STATUS run(EFI_HANDLE image) { export_variables(loaded_image); if (pack_cpio(loaded_image, - NULL, + /* dropin_dir= */ NULL, u".cred", + /* exclude_suffix= */ NULL, ".extra/credentials", /* dir_mode= */ 0500, /* access_mode= */ 0400, @@ -675,6 +677,7 @@ static EFI_STATUS run(EFI_HANDLE image) { if (pack_cpio(loaded_image, u"\\loader\\credentials", u".cred", + /* exclude_suffix= */ NULL, ".extra/global_credentials", /* dir_mode= */ 0500, /* access_mode= */ 0400, @@ -686,8 +689,9 @@ static EFI_STATUS run(EFI_HANDLE image) { parameters_measured = parameters_measured < 0 ? m : (parameters_measured && m); if (pack_cpio(loaded_image, - NULL, - u".raw", + /* dropin_dir= */ NULL, + u".raw", /* ideally we'd pick up only *.sysext.raw here, but for compat we pick up *.raw instead … */ + u".confext.raw", /* … but then exclude *.confext.raw again */ ".extra/sysext", /* dir_mode= */ 0555, /* access_mode= */ 0444, @@ -698,6 +702,20 @@ static EFI_STATUS run(EFI_HANDLE image) { &m) == EFI_SUCCESS) sysext_measured = m; + if (pack_cpio(loaded_image, + /* dropin_dir= */ NULL, + u".confext.raw", + /* exclude_suffix= */ NULL, + ".extra/confext", + /* dir_mode= */ 0555, + /* access_mode= */ 0444, + /* tpm_pcr= */ TPM2_PCR_KERNEL_CONFIG, + u"Configuration extension initrd", + &confext_initrd, + &confext_initrd_size, + &m) == EFI_SUCCESS) + confext_measured = m; + dt_size = szs[UNIFIED_SECTION_DTB]; dt_base = dt_size != 0 ? POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[UNIFIED_SECTION_DTB] : 0; @@ -728,6 +746,8 @@ static EFI_STATUS run(EFI_HANDLE image) { (void) efivar_set_uint_string(MAKE_GUID_PTR(LOADER), u"StubPcrKernelParameters", TPM2_PCR_KERNEL_CONFIG, 0); if (sysext_measured) (void) efivar_set_uint_string(MAKE_GUID_PTR(LOADER), u"StubPcrInitRDSysExts", TPM2_PCR_SYSEXTS, 0); + if (confext_measured) + (void) efivar_set_uint_string(MAKE_GUID_PTR(LOADER), u"StubPcrInitRDConfExts", TPM2_PCR_KERNEL_CONFIG, 0); /* If the PCR signature was embedded in the PE image, then let's wrap it in a cpio and also pass it * to the kernel, so that it can be read from /.extra/tpm2-pcr-signature.json. Note that this section @@ -773,7 +793,7 @@ static EFI_STATUS run(EFI_HANDLE image) { initrd_base = initrd_size != 0 ? POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[UNIFIED_SECTION_INITRD] : 0; _cleanup_pages_ Pages initrd_pages = {}; - if (credential_initrd || global_credential_initrd || sysext_initrd || pcrsig_initrd || pcrpkey_initrd) { + if (credential_initrd || global_credential_initrd || sysext_initrd || confext_initrd || pcrsig_initrd || pcrpkey_initrd) { /* If we have generated initrds dynamically, let's combine them with the built-in initrd. */ err = combine_initrd( initrd_base, initrd_size, @@ -781,6 +801,7 @@ static EFI_STATUS run(EFI_HANDLE image) { credential_initrd, global_credential_initrd, sysext_initrd, + confext_initrd, pcrsig_initrd, pcrpkey_initrd, }, @@ -788,10 +809,11 @@ static EFI_STATUS run(EFI_HANDLE image) { credential_initrd_size, global_credential_initrd_size, sysext_initrd_size, + confext_initrd_size, pcrsig_initrd_size, pcrpkey_initrd_size, }, - 5, + 6, &initrd_pages, &initrd_size); if (err != EFI_SUCCESS) return err; @@ -802,6 +824,7 @@ static EFI_STATUS run(EFI_HANDLE image) { credential_initrd = mfree(credential_initrd); global_credential_initrd = mfree(global_credential_initrd); sysext_initrd = mfree(sysext_initrd); + confext_initrd = mfree(confext_initrd); pcrsig_initrd = mfree(pcrsig_initrd); pcrpkey_initrd = mfree(pcrpkey_initrd); } diff --git a/src/fundamental/efivars-fundamental.h b/src/fundamental/efivars-fundamental.h index 2d25d2248f..01b18ec241 100644 --- a/src/fundamental/efivars-fundamental.h +++ b/src/fundamental/efivars-fundamental.h @@ -33,6 +33,7 @@ #define EFI_STUB_FEATURE_CMDLINE_ADDONS (UINT64_C(1) << 5) #define EFI_STUB_FEATURE_CMDLINE_SMBIOS (UINT64_C(1) << 6) #define EFI_STUB_FEATURE_DEVICETREE_ADDONS (UINT64_C(1) << 7) +#define EFI_STUB_FEATURE_PICK_UP_CONFEXTS (UINT64_C(1) << 8) typedef enum SecureBootMode { SECURE_BOOT_UNSUPPORTED, From d4fee8941af312ea70306dd2742aece9da9028db Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 8 Nov 2023 19:10:44 +0100 Subject: [PATCH 2/2] confext: make sure we pick up configuration extensions passed to us from the stub With fixes from Maanya Goenka. --- src/analyze/analyze-image-policy.c | 2 ++ src/shared/discover-image.c | 11 ++++++++--- src/shared/image-policy.c | 8 ++++++++ src/shared/image-policy.h | 7 ++++--- src/sysext/sysext.c | 12 ++++++++++-- src/test/test-image-policy.c | 1 + 6 files changed, 33 insertions(+), 8 deletions(-) diff --git a/src/analyze/analyze-image-policy.c b/src/analyze/analyze-image-policy.c index 0146b50c78..7d4f5498e8 100644 --- a/src/analyze/analyze-image-policy.c +++ b/src/analyze/analyze-image-policy.c @@ -94,6 +94,8 @@ int verb_image_policy(int argc, char *argv[], void *userdata) { p = &image_policy_sysext_strict; else if (streq(argv[i], "@confext")) p = &image_policy_confext; + else if (streq(argv[i], "@confext-strict")) + p = &image_policy_confext_strict; else if (streq(argv[i], "@container")) p = &image_policy_container; else if (streq(argv[i], "@service")) diff --git a/src/shared/discover-image.c b/src/shared/discover-image.c index b3f4c9ab76..3baa84c8bd 100644 --- a/src/shared/discover-image.c +++ b/src/shared/discover-image.c @@ -75,15 +75,20 @@ static const char* const image_search_path[_IMAGE_CLASS_MAX] = { "/usr/lib/confexts\0", }; -/* Inside the initrd, use a slightly different set of search path (i.e. include .extra/sysext in extension - * search dir) */ +/* Inside the initrd, use a slightly different set of search path (i.e. include .extra/sysext/ and + * .extra/confext/ in extension search dir) */ static const char* const image_search_path_initrd[_IMAGE_CLASS_MAX] = { /* (entries that aren't listed here will get the same search path as for the non initrd-case) */ [IMAGE_SYSEXT] = "/etc/extensions\0" /* only place symlinks here */ "/run/extensions\0" /* and here too */ "/var/lib/extensions\0" /* the main place for images */ - "/.extra/sysext\0" /* put sysext picked up by systemd-stub last, since not trusted */ + "/.extra/sysext\0", /* put sysext picked up by systemd-stub last, since not trusted */ + + [IMAGE_CONFEXT] = "/run/confexts\0" /* only place symlinks here */ + "/var/lib/confexts\0" /* the main place for images */ + "/usr/local/lib/confexts\0" + "/.extra/confext\0", /* put confext picked up by systemd-stub last, since not trusted */ }; static const char* image_class_suffix_table[_IMAGE_CLASS_MAX] = { diff --git a/src/shared/image-policy.c b/src/shared/image-policy.c index 3c3de50979..47ca62c731 100644 --- a/src/shared/image-policy.c +++ b/src/shared/image-policy.c @@ -726,6 +726,14 @@ const ImagePolicy image_policy_confext = { .default_flags = PARTITION_POLICY_IGNORE, }; +const ImagePolicy image_policy_confext_strict = { + .n_policies = 1, + .policies = { + { PARTITION_ROOT, PARTITION_POLICY_SIGNED|PARTITION_POLICY_ABSENT }, + }, + .default_flags = PARTITION_POLICY_IGNORE, +}; + const ImagePolicy image_policy_container = { /* For systemd-nspawn containers we use all partitions, with the exception of swap */ .n_policies = 8, diff --git a/src/shared/image-policy.h b/src/shared/image-policy.h index f59c16e173..c2a0a5ac40 100644 --- a/src/shared/image-policy.h +++ b/src/shared/image-policy.h @@ -58,9 +58,10 @@ struct ImagePolicy { extern const ImagePolicy image_policy_allow; extern const ImagePolicy image_policy_deny; extern const ImagePolicy image_policy_ignore; -extern const ImagePolicy image_policy_sysext; /* No verity required */ -extern const ImagePolicy image_policy_sysext_strict; /* Signed verity required */ -extern const ImagePolicy image_policy_confext; /* No verity required */ +extern const ImagePolicy image_policy_sysext; /* No verity required */ +extern const ImagePolicy image_policy_sysext_strict; /* Signed verity required */ +extern const ImagePolicy image_policy_confext; /* No verity required */ +extern const ImagePolicy image_policy_confext_strict; /* Signed verity required */ extern const ImagePolicy image_policy_container; extern const ImagePolicy image_policy_service; extern const ImagePolicy image_policy_host; diff --git a/src/sysext/sysext.c b/src/sysext/sysext.c index fe11fe0f07..fc79370781 100644 --- a/src/sysext/sysext.c +++ b/src/sysext/sysext.c @@ -659,8 +659,16 @@ static const ImagePolicy *pick_image_policy(const Image *img) { * picked up from an untrusted ESP. Thus, require a stricter policy by default for them. (For the * other directories we assume the appropriate level of trust was already established already. */ - if (in_initrd() && path_startswith(img->path, "/.extra/sysext/")) - return &image_policy_sysext_strict; + if (in_initrd()) { + if (path_startswith(img->path, "/.extra/sysext/")) + return &image_policy_sysext_strict; + if (path_startswith(img->path, "/.extra/confext/")) + return &image_policy_confext_strict; + + /* Better safe than sorry, refuse everything else passed in via the untrusted /.extra/ dir */ + if (path_startswith(img->path, "/.extra/")) + return &image_policy_deny; + } return image_class_info[img->class].default_image_policy; } diff --git a/src/test/test-image-policy.c b/src/test/test-image-policy.c index d9fe5562a1..1572093c9d 100644 --- a/src/test/test-image-policy.c +++ b/src/test/test-image-policy.c @@ -79,6 +79,7 @@ TEST_RET(test_image_policy_to_string) { test_policy(&image_policy_sysext, "sysext"); test_policy(&image_policy_sysext_strict, "sysext-strict"); test_policy(&image_policy_confext, "confext"); + test_policy(&image_policy_confext_strict, "confext-strict"); test_policy(&image_policy_container, "container"); test_policy(&image_policy_host, "host"); test_policy(&image_policy_service, "service");