diff --git a/.github/workflows/mkosi.yml b/.github/workflows/mkosi.yml index d952e6b31b..84f930abd1 100644 --- a/.github/workflows/mkosi.yml +++ b/.github/workflows/mkosi.yml @@ -95,8 +95,11 @@ jobs: KernelCommandLine=${{ env.KERNEL_CMDLINE }} EOF + - name: Generate secure boot key + run: sudo mkosi genkey + - name: Build ${{ matrix.distro }} - run: sudo mkosi --idmap no + run: sudo mkosi --idmap no --secure-boot - name: Show ${{ matrix.distro }} image summary run: sudo mkosi summary diff --git a/.gitignore b/.gitignore index 8aa363eac4..844d67f0a1 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,7 @@ __pycache__/ /mkosi.output/ /mkosi.default /mkosi.installdir/ +/mkosi.secure-boot.* # Ignore any mkosi config files with "local" in the name /mkosi.default.d/**/*local*.conf /tags diff --git a/man/loader.conf.xml b/man/loader.conf.xml index 80122177e5..22f4644d9e 100644 --- a/man/loader.conf.xml +++ b/man/loader.conf.xml @@ -227,12 +227,33 @@ Danger: this feature might soft-brick your device if used improperly. - Takes one of off, manual or force. - Controls the enrollment of Secure Boot keys. If set to off, no action whatsoever - is taken. If set to manual (the default) and the UEFI firmware is in setup-mode - then entries to manually enroll Secure Boot variables are created in the boot menu. If set to - force, in addition, if a directory named /loader/keys/auto/ - exists on the ESP then the keys in that directory are enrolled automatically. + Controls enrollment of secure boot keys found on the ESP if the system is in setup mode: + + + + No action is taken. + + + + + Boot entries for found secure boot keys are created that allow manual + enrollment. + + + + + Same behavior as , but will try to automatically + enroll the key auto if it is considered to be safe. Currently, this is only + the case if the system is running inside a virtual machine. + + + + + Always enroll the auto key if found. Note that a warning + message with a timeout will still be shown if this operation is unknown to be safe. + + + The different sets of variables can be set up under /loader/keys/NAME where @@ -254,8 +275,8 @@ uuid=$(systemd-id128 new --uuid) for key in PK KEK db; do openssl req -new -x509 -subj "/CN=${key}/" -keyout "${key}.key" -out "${key}.crt" - openssl x509 -outform DER -in "${key}.crt" -out "${key}.cer" - cert-to-efi-sig-list -g "${uuid}" "${key}.crt" "${key}.esl" + openssl x509 -outform DER -in "${key}.crt" -out "${key}.der" + sbsiglist --owner "${uuid}" --type x509 --output "${key}.esl" "${key}.der" done for key in MicWinProPCA2011_2011-10-19.crt MicCorUEFCA2011_2011-06-27.crt MicCorKEKCA2011_2011-06-24.crt; do @@ -266,7 +287,7 @@ done # Optionally add Microsoft Windows Production CA 2011 (needed to boot into Windows). cat MicWinProPCA2011_2011-10-19.esl >> db.esl -# Optionally add Microsoft Corporation UEFI CA 2011 (for firmware drivers / option ROMs +# Optionally add Microsoft Corporation UEFI CA 2011 for firmware drivers / option ROMs # and third-party boot loaders (including shim). This is highly recommended on real # hardware as not including this may soft-brick your device (see next paragraph). cat MicCorUEFCA2011_2011-06-27.esl >> db.esl @@ -276,9 +297,10 @@ cat MicCorUEFCA2011_2011-06-27.esl >> db.esl # key. The revocation database can be updated with fwupdmgr1. cat MicCorKEKCA2011_2011-06-24.esl >> KEK.esl -sign-efi-sig-list -c PK.crt -k PK.key PK PK.esl PK.auth -sign-efi-sig-list -c PK.crt -k PK.key KEK KEK.esl KEK.auth -sign-efi-sig-list -c KEK.crt -k KEK.key db db.esl db.auth +attr=NON_VOLATILE,RUNTIME_ACCESS,BOOTSERVICE_ACCESS,TIME_BASED_AUTHENTICATED_WRITE_ACCESS +sbvarsign --attr ${attr} --key PK.key --cert PK.crt --output PK.auth PK PK.esl +sbvarsign --attr ${attr} --key PK.key --cert PK.crt --output KEK.auth KEK KEK.esl +sbvarsign --attr ${attr} --key KEK.key --cert KEK.crt --output db.auth db db.esl This feature is considered dangerous because even if all the required files are signed with the diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c index bb318c4e7e..03fe022ef6 100644 --- a/src/boot/efi/boot.c +++ b/src/boot/efi/boot.c @@ -526,6 +526,9 @@ static void print_status(Config *config, char16_t *loaded_image_path) { case ENROLL_MANUAL: printf(" secure-boot-enroll: manual\n"); break; + case ENROLL_IF_SAFE: + printf(" secure-boot-enroll: if-safe\n"); + break; case ENROLL_FORCE: printf(" secure-boot-enroll: force\n"); break; @@ -1259,6 +1262,8 @@ static void config_defaults_load_from_file(Config *config, char *content) { config->secure_boot_enroll = ENROLL_MANUAL; else if (streq8(value, "force")) config->secure_boot_enroll = ENROLL_FORCE; + else if (streq8(value, "if-safe")) + config->secure_boot_enroll = ENROLL_IF_SAFE; else if (streq8(value, "off")) config->secure_boot_enroll = ENROLL_OFF; else @@ -1572,7 +1577,7 @@ static void config_load_defaults(Config *config, EFI_FILE *root_dir) { .auto_entries = true, .auto_firmware = true, .reboot_for_bitlocker = false, - .secure_boot_enroll = ENROLL_MANUAL, + .secure_boot_enroll = ENROLL_IF_SAFE, .idx_default_efivar = IDX_INVALID, .console_mode = CONSOLE_MODE_KEEP, .console_mode_efivar = CONSOLE_MODE_KEEP, @@ -2511,10 +2516,11 @@ static EFI_STATUS secure_boot_discover_keys(Config *config, EFI_FILE *root_dir) }; config_add_entry(config, entry); - if (config->secure_boot_enroll == ENROLL_FORCE && strcaseeq16(dirent->FileName, u"auto")) + if (IN_SET(config->secure_boot_enroll, ENROLL_IF_SAFE, ENROLL_FORCE) && + strcaseeq16(dirent->FileName, u"auto")) /* if we auto enroll successfully this call does not return, if it fails we still * want to add other potential entries to the menu */ - secure_boot_enroll_at(root_dir, entry->path); + secure_boot_enroll_at(root_dir, entry->path, config->secure_boot_enroll == ENROLL_FORCE); } return EFI_SUCCESS; @@ -2700,7 +2706,7 @@ static EFI_STATUS run(EFI_HANDLE image) { /* if auto enrollment is activated, we try to load keys for the given entry. */ if (entry->type == LOADER_SECURE_BOOT_KEYS && config.secure_boot_enroll != ENROLL_OFF) { - err = secure_boot_enroll_at(root_dir, entry->path); + err = secure_boot_enroll_at(root_dir, entry->path, /*force=*/ true); if (err != EFI_SUCCESS) return err; continue; diff --git a/src/boot/efi/secure-boot.c b/src/boot/efi/secure-boot.c index c8a3957b1e..8b796db65f 100644 --- a/src/boot/efi/secure-boot.c +++ b/src/boot/efi/secure-boot.c @@ -36,7 +36,7 @@ SecureBootMode secure_boot_mode(void) { static const char sbat[] _used_ _section_(".sbat") = SBAT_SECTION_TEXT; #endif -EFI_STATUS secure_boot_enroll_at(EFI_FILE *root_dir, const char16_t *path) { +EFI_STATUS secure_boot_enroll_at(EFI_FILE *root_dir, const char16_t *path, bool force) { assert(root_dir); assert(path); @@ -44,11 +44,16 @@ EFI_STATUS secure_boot_enroll_at(EFI_FILE *root_dir, const char16_t *path) { clear_screen(COLOR_NORMAL); - printf("Enrolling secure boot keys from directory: %ls\n", path); - /* Enrolling secure boot keys is safe to do in virtualized environments as there is nothing * we can brick there. */ - if (!in_hypervisor()) { + bool is_safe = in_hypervisor(); + + if (!is_safe && !force) + return EFI_SUCCESS; + + printf("Enrolling secure boot keys from directory: %ls\n", path); + + if (!is_safe) { printf("Warning: Enrolling custom Secure Boot keys might soft-brick your machine!\n"); unsigned timeout_sec = 15; diff --git a/src/boot/efi/secure-boot.h b/src/boot/efi/secure-boot.h index e98de81c2a..bdc34150da 100644 --- a/src/boot/efi/secure-boot.h +++ b/src/boot/efi/secure-boot.h @@ -9,13 +9,14 @@ typedef enum { ENROLL_OFF, /* no Secure Boot key enrollment whatsoever, even manual entries are not generated */ ENROLL_MANUAL, /* Secure Boot key enrollment is strictly manual: manual entries are generated and need to be selected by the user */ + ENROLL_IF_SAFE, /* Automatically enroll if it is safe (if we are running inside a VM, for example). */ ENROLL_FORCE, /* Secure Boot key enrollment may be automatic if it is available but might not be safe */ } secure_boot_enroll; bool secure_boot_enabled(void); SecureBootMode secure_boot_mode(void); -EFI_STATUS secure_boot_enroll_at(EFI_FILE *root_dir, const char16_t *path); +EFI_STATUS secure_boot_enroll_at(EFI_FILE *root_dir, const char16_t *path, bool force); typedef bool (*security_validator_t)( const void *ctx, diff --git a/test/mkosi-check-and-shutdown.sh b/test/mkosi-check-and-shutdown.sh index ed76ef370a..b86d2d3e69 100644 --- a/test/mkosi-check-and-shutdown.sh +++ b/test/mkosi-check-and-shutdown.sh @@ -3,6 +3,12 @@ systemctl --failed --no-legend | tee /failed-services +# Check that secure boot keys were properly enrolled. +if [[ -d /sys/firmware/efi/efivars/ ]]; then + cmp /sys/firmware/efi/efivars/SecureBoot-8be4df61-93ca-11d2-aa0d-00e098032b8c <(printf '\6\0\0\0\1') + cmp /sys/firmware/efi/efivars/SetupMode-8be4df61-93ca-11d2-aa0d-00e098032b8c <(printf '\6\0\0\0\0') +fi + # Exit with non-zero EC if the /failed-services file is not empty (we have -e set) [[ ! -s /failed-services ]]