From bf3f4d16d6426a26638d792f887f8e3fe86def94 Mon Sep 17 00:00:00 2001 From: Adrian Vovk Date: Tue, 7 Dec 2021 21:01:46 -0500 Subject: [PATCH 1/2] stub: Properly null-terminate filenames in pack_cpio_one Previously, mangle_filename would write the null terminator, but then wouldn't increment the pointer. Thus, the pad4 call that comes immediately after mangle_filename would trample over the null terminator that mangle_filename wrote. Since the padding is all 0s, this happened to work for the filenames it was tested for However, in some cases, this would cause the pointer to be a different position than predicted. Basically, the predicted size would be one byte bigger than the actual size (the missing null terminator). Usually, this disappeared into the alignment padding at the end of the buffer, but if the buffer was already unexpectedly aligned (b/c it was a byte shorter than expected), this would cause assertion failures. Also, the c_namesize field in the cpio header was incorrect. It didn't include the null terminator, as required by the spec. --- src/boot/efi/cpio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/boot/efi/cpio.c b/src/boot/efi/cpio.c index d7dd50fc8fa..d4c870284bb 100644 --- a/src/boot/efi/cpio.c +++ b/src/boot/efi/cpio.c @@ -34,7 +34,7 @@ static CHAR8* mangle_filename(CHAR8 *p, const CHAR16 *f) { *(w++) = *f; } - *w = 0; + *(w++) = 0; return w; } @@ -138,7 +138,7 @@ static EFI_STATUS pack_cpio_one( a = write_cpio_word(a, 0); /* minor(dev) */ a = write_cpio_word(a, 0); /* major(rdev) */ a = write_cpio_word(a, 0); /* minor(rdev) */ - a = write_cpio_word(a, target_dir_prefix_size + fname_size + 1); /* fname size */ + a = write_cpio_word(a, target_dir_prefix_size + fname_size + 2); /* fname size */ a = write_cpio_word(a, 0); /* "crc" */ CopyMem(a, target_dir_prefix, target_dir_prefix_size); From f3b6f33387a15e89e9720115a90650c50848440d Mon Sep 17 00:00:00 2001 From: Adrian Vovk Date: Tue, 7 Dec 2021 21:21:40 -0500 Subject: [PATCH 2/2] stub: Load credentials from \loader\credentials\*.cred Some types of credentials that a user would want to pass into the initrd do not depend on the specific kernel/initrd version. For instance, this can include SSH keys, rootfs encryption keys, dm-integrity keys, and so on. This introduces a directory where such credentials can be placed so that any kernel image will load them --- man/systemd-stub.xml | 24 +++++++++++++++------- src/boot/efi/cpio.c | 11 ++++++----- src/boot/efi/cpio.h | 4 +++- src/boot/efi/stub.c | 47 ++++++++++++++++++++++++++++++++------------ 4 files changed, 60 insertions(+), 26 deletions(-) diff --git a/man/systemd-stub.xml b/man/systemd-stub.xml index 2a32e05bcc3..b1983b4927e 100644 --- a/man/systemd-stub.xml +++ b/man/systemd-stub.xml @@ -27,6 +27,9 @@ /usr/lib/systemd/boot/efi/linuxx64.efi.stub /usr/lib/systemd/boot/efi/linuxia32.efi.stub /usr/lib/systemd/boot/efi/linuxaa64.efi.stub + ESP/.../foo.efi.extra.d/*.cred + ESP/.../foo.efi.extra.d/*.raw + ESP/loader/credentials/*.cred @@ -78,13 +81,14 @@ Companion Files The systemd-stub UEFI boot stub automatically collects two types of auxiliary - companion files optionally placed in a drop-in directory next to the EFI binary and dynamically generates - cpio initrd archives from them, and passes them to the kernel. Specifically: + companion files optionally placed in drop-in directories on the same partition as the EFI binary, + dynamically generates cpio initrd archives from them, and passes them to the kernel. + Specifically: - For a kernel binary called foo.efi it + For a kernel binary called foo.efi, it will look for files with the .cred suffix in a directory named - foo.efi.extra.d/, next to it. A cpio + foo.efi.extra.d/ next to it. A cpio archive is generated from all files found that way, placing them in the /.extra/credentials/ directory of the initrd file hierarchy. The main initrd may then access them in this directory. This is supposed to be used to store auxiliary, encrypted, @@ -94,16 +98,22 @@ details on encrypted credentials. The generated cpio archive is measured into TPM PCR 4 (if a TPM is present) - Similar, files foo.efi.extra.d/*.raw - are packed up as cpio archive and placed in the /.extra/sysext/ + 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 systemd-sysext8 for details on system extension images. The generated cpio archive containing these system extension images is measured into TPM PCR 8 (if a TPM is present). + + Files /loader/credentials/*.cred are packed up in a + cpio archive and placed in the /.extra/global_credentials/ + directory of the initrd file hierarchy. This is supposed to be used to pass additional credentials to + the initrd, regardless of the kernel being booted. The generated cpio archive is + measured into TPM PCR 4 (if a TPM is present) - Both mechanisms may be used to parameterize and extend trusted (i.e. signed), immutable initrd + These mechanisms may be used to parameterize and extend trusted (i.e. signed), immutable initrd images in a reasonably safe way: all data they contain is measured into TPM PCRs. On access they should be further validated: in case of the credentials case by encrypting/authenticating them via TPM, as exposed by systemd-creds encrypt -T (see diff --git a/src/boot/efi/cpio.c b/src/boot/efi/cpio.c index d4c870284bb..be0708aec43 100644 --- a/src/boot/efi/cpio.c +++ b/src/boot/efi/cpio.c @@ -4,8 +4,6 @@ #include "measure.h" #include "util.h" -#define EXTRA_DIR_SUFFIX L".extra.d" - static CHAR8* write_cpio_word(CHAR8 *p, UINT32 v) { static const char hex[] = "0123456789abcdef"; @@ -308,6 +306,7 @@ static EFI_STATUS pack_cpio_trailer( EFI_STATUS pack_cpio( EFI_LOADED_IMAGE *loaded_image, + const CHAR16 *dropin_dir, const CHAR16 *match_suffix, const CHAR8 *target_dir_prefix, UINT32 dir_mode, @@ -319,7 +318,7 @@ EFI_STATUS pack_cpio( _cleanup_(FileHandleClosep) EFI_FILE_HANDLE root = NULL, extra_dir = NULL; UINTN dirent_size = 0, buffer_size = 0, n_items = 0, n_allocated = 0; - _cleanup_freepool_ CHAR16 *extra_dir_path = NULL; + _cleanup_freepool_ CHAR16 *rel_dropin_dir = NULL; _cleanup_freepool_ EFI_FILE_INFO *dirent = NULL; _cleanup_(strv_freep) CHAR16 **items = NULL; _cleanup_freepool_ void *buffer = NULL; @@ -335,8 +334,10 @@ EFI_STATUS pack_cpio( if (!root) return log_error_status_stall(EFI_LOAD_ERROR, L"Unable to open root directory."); - extra_dir_path = xpool_print(L"%D" EXTRA_DIR_SUFFIX, loaded_image->FilePath); - err = open_directory(root, extra_dir_path, &extra_dir); + if (!dropin_dir) + dropin_dir = rel_dropin_dir = xpool_print(L"%D.extra.d", loaded_image->FilePath); + + err = open_directory(root, dropin_dir, &extra_dir); if (err == EFI_NOT_FOUND) { /* No extra subdir, that's totally OK */ *ret_buffer = NULL; diff --git a/src/boot/efi/cpio.h b/src/boot/efi/cpio.h index 7a2331e0c8e..a272d289298 100644 --- a/src/boot/efi/cpio.h +++ b/src/boot/efi/cpio.h @@ -5,11 +5,13 @@ EFI_STATUS pack_cpio( EFI_LOADED_IMAGE *loaded_image, + const CHAR16 *dropin_dir, const CHAR16 *match_suffix, const CHAR8 *target_dir_prefix, UINT32 dir_mode, UINT32 access_mode, - UINTN pcr, + UINTN tpm_pcr, const CHAR16 *tpm_description, void **ret_buffer, UINTN *ret_buffer_size); + diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c index c99b3b7d6d6..0b1f276c416 100644 --- a/src/boot/efi/stub.c +++ b/src/boot/efi/stub.c @@ -20,6 +20,7 @@ _used_ _section_(".sdmagic") static const char magic[] = "#### LoaderInfo: syste static EFI_STATUS combine_initrd( EFI_PHYSICAL_ADDRESS initrd_base, UINTN initrd_size, const void *credential_initrd, UINTN credential_initrd_size, + const void *global_credential_initrd, UINTN global_credential_initrd_size, const void *sysext_initrd, UINTN sysext_initrd_size, EFI_PHYSICAL_ADDRESS *ret_initrd_base, UINTN *ret_initrd_size) { @@ -31,7 +32,7 @@ static EFI_STATUS combine_initrd( assert(ret_initrd_base); assert(ret_initrd_size); - /* Combines three initrds into one, by simple concatenation in memory */ + /* Combines four initrds into one, by simple concatenation in memory */ n = ALIGN_TO(initrd_size, 4); /* main initrd might not be padded yet */ if (credential_initrd) { @@ -40,6 +41,12 @@ static EFI_STATUS combine_initrd( n += credential_initrd_size; } + if (global_credential_initrd) { + if (n > UINTN_MAX - global_credential_initrd_size) + return EFI_OUT_OF_RESOURCES; + + n += global_credential_initrd_size; + } if (sysext_initrd) { if (n > UINTN_MAX - sysext_initrd_size) return EFI_OUT_OF_RESOURCES; @@ -76,6 +83,11 @@ static EFI_STATUS combine_initrd( p += credential_initrd_size; } + if (global_credential_initrd) { + CopyMem(p, global_credential_initrd, global_credential_initrd_size); + p += global_credential_initrd_size; + } + if (sysext_initrd) { CopyMem(p, sysext_initrd, sysext_initrd_size); p += sysext_initrd_size; @@ -156,8 +168,9 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { }; UINTN cmdline_len = 0, linux_size, initrd_size, dt_size; - UINTN credential_initrd_size = 0, sysext_initrd_size = 0; - _cleanup_freepool_ void *credential_initrd = NULL, *sysext_initrd = NULL; + UINTN credential_initrd_size = 0, global_credential_initrd_size = 0, sysext_initrd_size = 0; + _cleanup_freepool_ void *credential_initrd = NULL, *global_credential_initrd = NULL; + _cleanup_freepool_ void *sysext_initrd = NULL; EFI_PHYSICAL_ADDRESS linux_base, initrd_base, dt_base; _cleanup_(devicetree_cleanup) struct devicetree_state dt_state = {}; EFI_LOADED_IMAGE *loaded_image; @@ -213,6 +226,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { export_variables(loaded_image); (void) pack_cpio(loaded_image, + NULL, L".cred", (const CHAR8*) ".extra/credentials", /* dir_mode= */ 0500, @@ -223,6 +237,18 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { &credential_initrd_size); (void) pack_cpio(loaded_image, + L"\\loader\\credentials", + L".cred", + (const CHAR8*) ".extra/global_credentials", + /* dir_mode= */ 0500, + /* access_mode= */ 0400, + /* tpm_pcr= */ TPM_PCR_INDEX_KERNEL_PARAMETERS, + L"Global credentials initrd", + &global_credential_initrd, + &global_credential_initrd_size); + + (void) pack_cpio(loaded_image, + NULL, L".raw", (const CHAR8*) ".extra/sysext", /* dir_mode= */ 0555, @@ -241,26 +267,21 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { dt_size = szs[SECTION_DTB]; dt_base = dt_size != 0 ? POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[SECTION_DTB] : 0; - if (credential_initrd || sysext_initrd) { + if (credential_initrd || global_credential_initrd || sysext_initrd) { /* If we have generated initrds dynamically, let's combine them with the built-in initrd. */ err = combine_initrd( initrd_base, initrd_size, credential_initrd, credential_initrd_size, + global_credential_initrd, global_credential_initrd_size, sysext_initrd, sysext_initrd_size, &initrd_base, &initrd_size); if (EFI_ERROR(err)) return err; /* Given these might be large let's free them explicitly, quickly. */ - if (credential_initrd) { - FreePool(credential_initrd); - credential_initrd = NULL; - } - - if (sysext_initrd) { - FreePool(sysext_initrd); - sysext_initrd = NULL; - } + credential_initrd = mfree(credential_initrd); + global_credential_initrd = mfree(global_credential_initrd); + sysext_initrd = mfree(sysext_initrd); } if (dt_size > 0) {