diff --git a/.github/workflows/pullrequest.yml b/.github/workflows/pullrequest.yml index 5329496..18fe564 100644 --- a/.github/workflows/pullrequest.yml +++ b/.github/workflows/pullrequest.yml @@ -15,28 +15,24 @@ jobs: name: ${{ matrix.distro }} ${{ matrix.efiarch }} cross-build strategy: + fail-fast: false matrix: include: - arch: amd64 efiarch: aa64 gccarch: aarch64 makearch: aarch64 - distro: f35 + distro: f36 - arch: amd64 efiarch: aa64 gccarch: aarch64 makearch: aarch64 - distro: f34 + distro: f35 - arch: amd64 - efiarch: aa64 - gccarch: aarch64 - makearch: aarch64 - distro: f33 - - arch: amd64 - efiarch: aa64 - gccarch: aarch64 - makearch: aarch64 - distro: f32 + efiarch: arm + gccarch: arm + makearch: arm + distro: f36 - arch: amd64 efiarch: arm gccarch: arm @@ -48,15 +44,10 @@ jobs: makearch: arm distro: f34 - arch: amd64 - efiarch: arm - gccarch: arm - makearch: arm - distro: f33 - - arch: amd64 - efiarch: arm - gccarch: arm - makearch: arm - distro: f32 + efiarch: x64 + gccarch: x86_64 + makearch: x86_64 + distro: f36 - arch: amd64 efiarch: x64 gccarch: x86_64 @@ -68,15 +59,10 @@ jobs: makearch: x86_64 distro: f34 - arch: amd64 - efiarch: x64 + efiarch: ia32 gccarch: x86_64 - makearch: x86_64 - distro: f33 - - arch: amd64 - efiarch: x64 - gccarch: x86_64 - makearch: x86_64 - distro: f32 + makearch: ia32 + distro: f36 - arch: amd64 efiarch: ia32 gccarch: x86_64 @@ -87,16 +73,6 @@ jobs: gccarch: x86_64 makearch: ia32 distro: f34 - - arch: amd64 - efiarch: ia32 - gccarch: x86_64 - makearch: ia32 - distro: f33 - - arch: amd64 - efiarch: ia32 - gccarch: x86_64 - makearch: ia32 - distro: f32 steps: - name: Checkout @@ -139,6 +115,10 @@ jobs: strategy: matrix: include: + - arch: amd64 + efiarch: x64 + makearch: x86_64 + distro: f36 - arch: amd64 efiarch: x64 makearch: x86_64 @@ -150,11 +130,7 @@ jobs: - arch: amd64 efiarch: x64 makearch: x86_64 - distro: f33 - - arch: amd64 - efiarch: x64 - makearch: x86_64 - distro: f32 + distro: centos9 - arch: amd64 efiarch: x64 makearch: x86_64 @@ -163,6 +139,10 @@ jobs: efiarch: x64 makearch: x86_64 distro: centos7 + - arch: amd64 + efiarch: ia32 + makearch: ia32 + distro: f36 - arch: amd64 efiarch: ia32 makearch: ia32 @@ -171,14 +151,6 @@ jobs: efiarch: ia32 makearch: ia32 distro: f34 - - arch: amd64 - efiarch: ia32 - makearch: ia32 - distro: f33 - - arch: amd64 - efiarch: ia32 - makearch: ia32 - distro: f32 - arch: amd64 efiarch: ia32 makearch: ia32 diff --git a/.gitmodules b/.gitmodules index 1029752..78424fb 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "gnu-efi"] path = gnu-efi url = https://github.com/rhboot/gnu-efi.git - branch = shim-15.5 + branch = shim-15.6 diff --git a/BUILDING b/BUILDING index 2da9b5f..3b2e85d 100644 --- a/BUILDING +++ b/BUILDING @@ -35,7 +35,8 @@ Variables you could set to customize the build: If this is set, we look for SHIM_DEVEL_DEBUG instead of SHIM_DEBUG in our debugger delay hook, thus meaning you can have it pause for a debugger only on the development branch and not the OS you need to boot - to scp in a new development build. + to scp in a new development build. Likewise, we look for + SHIM_DEVEL_VERBOSE rather than SHIM_VERBOSE. - DISABLE_EBS_PROTECTION On systems where a second stage bootloader is not used, and the Linux Kernel is embedded in the same EFI image as shim and booted directly diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..59a2c94 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,134 @@ + +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, caste, color, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +rharwood AT redhat DOT com. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.1, available at +[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. + +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. + +For answers to common questions about this code of conduct, see the FAQ at +[https://www.contributor-covenant.org/faq][FAQ]. Translations are available +at [https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[Mozilla CoC]: https://github.com/mozilla/diversity +[FAQ]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations + diff --git a/Make.defaults b/Make.defaults index 18677da..dfed9c4 100644 --- a/Make.defaults +++ b/Make.defaults @@ -84,9 +84,7 @@ ifeq ($(ARCH),aarch64) ARCH_GNUEFI ?= aarch64 ARCH_SUFFIX ?= aa64 ARCH_SUFFIX_UPPER ?= AA64 - FORMAT := -O binary - SUBSYSTEM := 0xa - ARCH_LDFLAGS += --defsym=EFI_SUBSYSTEM=$(SUBSYSTEM) + ARCH_LDFLAGS ?= ARCH_CFLAGS ?= endif ifeq ($(ARCH),arm) diff --git a/Make.rules b/Make.rules index 532aab6..7c6ec6c 100644 --- a/Make.rules +++ b/Make.rules @@ -35,4 +35,7 @@ $(strip $(foreach x,$(DEFAULT_$(1)), $(eval override $(1)+=$(x))))) endef +%.o : %.S + $(CC) $(CFLAGS) -c -o $@ $< + # vim:filetype=make diff --git a/Makefile b/Makefile index 6b50b8f..24ac314 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ default : all NAME = shim -VERSION = 15.5 +VERSION = 15.6 ifneq ($(origin RELEASE),undefined) DASHRELEASE ?= -$(RELEASE) else @@ -40,7 +40,7 @@ TARGETS += $(MMNAME) $(FBNAME) endif OBJS = shim.o globals.o mok.o netboot.o cert.o replacements.o tpm.o version.o errlog.o sbat.o sbat_data.o pe.o httpboot.o csv.o load-options.o KEYS = shim_cert.h ocsp.* ca.* shim.crt shim.csr shim.p12 shim.pem shim.key shim.cer -ORIG_SOURCES = shim.c globals.c mok.c netboot.c replacements.c tpm.c errlog.c sbat.c pe.c httpboot.c shim.h version.h $(wildcard include/*.h) +ORIG_SOURCES = shim.c globals.c mok.c netboot.c replacements.c tpm.c errlog.c sbat.c pe.c httpboot.c shim.h version.h $(wildcard include/*.h) cert.S MOK_OBJS = MokManager.o PasswordCrypt.o crypt_blowfish.o errlog.o sbat_data.o globals.o ORIG_MOK_SOURCES = MokManager.c PasswordCrypt.c crypt_blowfish.c shim.h $(wildcard include/*.h) FALLBACK_OBJS = fallback.o tpm.o errlog.o sbat_data.o globals.o @@ -108,9 +108,6 @@ shim.o: shim_cert.h endif shim.o: $(wildcard $(TOPDIR)/*.h) -cert.o : $(TOPDIR)/cert.S - $(CC) $(CFLAGS) -c -o $@ $< - sbat.%.csv : data/sbat.%.csv $(DOS2UNIX) $(D2UFLAGS) $< $@ tail -c1 $@ | read -r _ || echo >> $@ # ensure a trailing newline @@ -154,6 +151,7 @@ gnu-efi/$(ARCH_GNUEFI)/gnuefi/libgnuefi.a gnu-efi/$(ARCH_GNUEFI)/lib/libefi.a: mkdir -p gnu-efi/lib gnu-efi/gnuefi $(MAKE) -C gnu-efi \ COMPILER="$(COMPILER)" \ + CCC_CC="$(COMPILER)" \ CC="$(CC)" \ ARCH=$(ARCH_GNUEFI) \ TOPDIR=$(TOPDIR)/gnu-efi \ diff --git a/MokManager.c b/MokManager.c index 1359af8..ffcd6a6 100644 --- a/MokManager.c +++ b/MokManager.c @@ -1776,17 +1776,7 @@ static EFI_STATUS mok_tml_prompt(void *MokTML, UINTN MokTMLSize) LibDeleteVariable(L"MokListTrustedNew", &SHIM_LOCK_GUID); return EFI_ABORTED; } - if (var->MokTMLState == 0) { - efi_status = RT->SetVariable(L"MokListTrusted", &SHIM_LOCK_GUID, - EFI_VARIABLE_NON_VOLATILE | - EFI_VARIABLE_BOOTSERVICE_ACCESS, - 1, &dbval); - if (EFI_ERROR(efi_status)) { - console_notify(L"Failed to set MokListTrusted state"); - return efi_status; - } - } else { efi_status = RT->SetVariable(L"MokListTrusted", &SHIM_LOCK_GUID, EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, @@ -1795,8 +1785,16 @@ static EFI_STATUS mok_tml_prompt(void *MokTML, UINTN MokTMLSize) console_notify(L"Failed to delete MokListTrusted state"); return efi_status; } + } else { + efi_status = RT->SetVariable(L"MokListTrusted", &SHIM_LOCK_GUID, + EFI_VARIABLE_NON_VOLATILE | + EFI_VARIABLE_BOOTSERVICE_ACCESS, + 1, &dbval); + if (EFI_ERROR(efi_status)) { + console_notify(L"Failed to set MokListTrusted state"); + return efi_status; + } } - return EFI_SUCCESS; } diff --git a/commit b/commit index ef99049..4605976 100644 --- a/commit +++ b/commit @@ -1 +1 @@ -f2c598bb2218da966872ba3e0c6e7e830dca6ef0 \ No newline at end of file +505cdb678b319fcf9a7fdee77c0f091b4147cbe5 \ No newline at end of file diff --git a/data/sbat.csv b/data/sbat.csv index ad838f2..7a5169f 100755 --- a/data/sbat.csv +++ b/data/sbat.csv @@ -1,2 +1,2 @@ sbat,1,SBAT Version,sbat,1,https://github.com/rhboot/shim/blob/main/SBAT.md -shim,1,UEFI shim,shim,1,https://github.com/rhboot/shim +shim,2,UEFI shim,shim,1,https://github.com/rhboot/shim diff --git a/elf_aarch64_efi.lds b/elf_aarch64_efi.lds index 42825fd..60c55ba 100644 --- a/elf_aarch64_efi.lds +++ b/elf_aarch64_efi.lds @@ -3,109 +3,94 @@ OUTPUT_ARCH(aarch64) ENTRY(_start) SECTIONS { - .text 0x0 : { - _text = .; - *(.text.head) - *(.text) - *(.text.*) - *(.gnu.linkonce.t.*) - _evtext = .; - . = ALIGN(4096); + . = 0; + ImageBase = .; + .hash : { *(.hash) } /* this MUST come first! */ + . = ALIGN(4096); + .eh_frame : + { + *(.eh_frame) + } + . = ALIGN(4096); + .text : + { + _text = .; + *(.text) + *(.text.*) + *(.gnu.linkonce.t.*) + _etext = .; + } + . = ALIGN(4096); + .reloc : + { + *(.reloc) + } + . = ALIGN(4096); + .note.gnu.build-id : { + *(.note.gnu.build-id) + } + + . = ALIGN(4096); + .data.ident : { + *(.data.ident) } - _etext = .; - _text_size = . - _text; - _text_vsize = _evtext - _text; . = ALIGN(4096); .data : { _data = .; - *(.sdata) - *(.data) - *(.data1) - *(.data.*) + *(.rodata*) *(.got.plt) *(.got) - - *(.dynamic) - + *(.data*) + *(.sdata) /* the EFI loader doesn't seem to like a .bss section, so we stick it all into .data: */ - . = ALIGN(16); - _bss = .; *(.sbss) *(.scommon) *(.dynbss) *(.bss) *(COMMON) - _evdata = .; - . = ALIGN(4096); - _bss_end = .; + *(.rel.local) + } + + . = ALIGN(4096); + .vendor_cert : + { + *(.vendor_cert) + } + . = ALIGN(4096); + .dynamic : { *(.dynamic) } + . = ALIGN(4096); + .rela : + { + *(.rela.data*) + *(.rela.got*) + *(.rela.stab*) } _edata = .; - _data_vsize = _evdata - _data; _data_size = . - _data; - - /* - * Note that _sbat must be the beginning of the data, and _esbat must be the - * end and must be before any section padding. The sbat self-check uses - * _esbat to find the bounds of the data, and if the padding is included, the - * CSV parser (correctly) rejects the data as having NUL values in one of the - * required columns. - */ . = ALIGN(4096); .sbat : { _sbat = .; *(.sbat) *(.sbat.*) - _esbat = .; - . = ALIGN(4096); - _epsbat = .; } - _sbat_size = _epsbat - _sbat; - _sbat_vsize = _esbat - _sbat; + _esbat = .; + _sbat_size = . - _sbat; . = ALIGN(4096); - .rodata : - { - _rodata = .; - *(.rodata*) - *(.srodata) - . = ALIGN(16); - *(.note.gnu.build-id) - . = ALIGN(4096); - *(.vendor_cert) - *(.data.ident) - . = ALIGN(4096); - } + .dynsym : { *(.dynsym) } . = ALIGN(4096); - .rela : - { - *(.rela.dyn) - *(.rela.plt) - *(.rela.got) - *(.rela.data) - *(.rela.data*) - } + .dynstr : { *(.dynstr) } . = ALIGN(4096); - .dyn : + .ignored.reloc : { - *(.dynsym) - *(.dynstr) - _evrodata = .; - . = ALIGN(4096); - } - _erodata = .; - _rodata_size = . - _rodata; - _rodata_vsize = _evrodata - _rodata; - _alldata_size = . - _data; - - /DISCARD/ : - { - *(.rel.reloc) + *(.rela.reloc) *(.eh_frame) *(.note.GNU-stack) } .comment 0 : { *(.comment) } + .note.gnu.build-id : { *(.note.gnu.build-id) } } diff --git a/fallback.c b/fallback.c index 8e6327b..da4b25c 100644 --- a/fallback.c +++ b/fallback.c @@ -24,7 +24,7 @@ get_fallback_verbose(void) if (state != -1) return state; - efi_status = get_variable(L"FALLBACK_VERBOSE", + efi_status = get_variable(FALLBACK_VERBOSE_VAR_NAME, &data, &dataSize, SHIM_LOCK_GUID); if (EFI_ERROR(efi_status)) { state = 0; @@ -1130,7 +1130,7 @@ debug_hook(void) register volatile int x = 0; extern char _etext, _edata; - efi_status = get_variable(L"SHIM_DEBUG", &data, &dataSize, + efi_status = get_variable(DEBUG_VAR_NAME, &data, &dataSize, SHIM_LOCK_GUID); if (EFI_ERROR(efi_status)) { return; diff --git a/globals.c b/globals.c index 4a1f432..b4e80dd 100644 --- a/globals.c +++ b/globals.c @@ -12,6 +12,9 @@ UINT8 *vendor_authorized = NULL; UINT32 vendor_deauthorized_size = 0; UINT8 *vendor_deauthorized = NULL; +UINT32 user_cert_size; +UINT8 *user_cert; + #if defined(ENABLE_SHIM_CERT) UINT32 build_cert_size; UINT8 *build_cert; @@ -26,6 +29,7 @@ int loader_is_participating; UINT8 user_insecure_mode; UINT8 ignore_db; UINT8 trust_mok_list; +UINT8 mok_policy = 0; UINT32 verbose = 0; diff --git a/gnu-efi/Make.defaults b/gnu-efi/Make.defaults index 5ce8f7c..3b56150 100755 --- a/gnu-efi/Make.defaults +++ b/gnu-efi/Make.defaults @@ -153,13 +153,11 @@ endif # Set HAVE_EFI_OBJCOPY if objcopy understands --target efi-[app|bsdrv|rtdrv], # otherwise we need to compose the PE/COFF header using the assembler # -ifneq ($(ARCH),aarch64) ifneq ($(ARCH),arm) ifneq ($(ARCH),mips64el) export HAVE_EFI_OBJCOPY=y endif endif -endif ifeq ($(ARCH),arm) CFLAGS += -marm diff --git a/gnu-efi/gnuefi/crt0-efi-aarch64.S b/gnu-efi/gnuefi/crt0-efi-aarch64.S index a96b5eb..0fefec0 100644 --- a/gnu-efi/gnuefi/crt0-efi-aarch64.S +++ b/gnu-efi/gnuefi/crt0-efi-aarch64.S @@ -16,136 +16,11 @@ * either version 2 of the License, or (at your option) any later version. */ - .section .text.head - - /* - * Magic "MZ" signature for PE/COFF - */ - .globl ImageBase -ImageBase: - .ascii "MZ" - .skip 58 // 'MZ' + pad + offset == 64 - .long pe_header - ImageBase // Offset to the PE header. -pe_header: - .ascii "PE" - .short 0 -coff_header: - .short 0xaa64 // AArch64 - .short 4 // nr_sections - .long 0 // TimeDateStamp - .long 0 // PointerToSymbolTable - .long 1 // NumberOfSymbols - .short section_table - optional_header // SizeOfOptionalHeader - .short 0x206 // Characteristics. - // IMAGE_FILE_DEBUG_STRIPPED | - // IMAGE_FILE_EXECUTABLE_IMAGE | - // IMAGE_FILE_LINE_NUMS_STRIPPED -optional_header: - .short 0x20b // PE32+ format - .byte 0x02 // MajorLinkerVersion - .byte 0x14 // MinorLinkerVersion - .long _text_size // SizeOfCode - .long _alldata_size // SizeOfInitializedData - .long 0 // SizeOfUninitializedData - .long _start - ImageBase // AddressOfEntryPoint - .long _start - ImageBase // BaseOfCode - -extra_header_fields: - .quad 0 // ImageBase - .long 0x1000 // SectionAlignment - .long 0x200 // FileAlignment - .short 0 // MajorOperatingSystemVersion - .short 0 // MinorOperatingSystemVersion - .short 0 // MajorImageVersion - .short 0 // MinorImageVersion - .short 0 // MajorSubsystemVersion - .short 0 // MinorSubsystemVersion - .long 0 // Win32VersionValue - - .long _erodata - ImageBase // SizeOfImage - - // Everything before the kernel image is considered part of the header - .long _start - ImageBase // SizeOfHeaders - .long 0 // CheckSum - .short EFI_SUBSYSTEM // Subsystem - .short 0 // DllCharacteristics - .quad 0 // SizeOfStackReserve - .quad 0 // SizeOfStackCommit - .quad 0 // SizeOfHeapReserve - .quad 0 // SizeOfHeapCommit - .long 0 // LoaderFlags - .long 0x6 // NumberOfRvaAndSizes - - .quad 0 // ExportTable - .quad 0 // ImportTable - .quad 0 // ResourceTable - .quad 0 // ExceptionTable - .quad 0 // CertificationTable - .quad 0 // BaseRelocationTable - - // Section table -section_table: - .ascii ".text\0\0\0" - .long _evtext - _start // VirtualSize - .long _start - ImageBase // VirtualAddress - .long _etext - _start // SizeOfRawData - .long _start - ImageBase // PointerToRawData - - .long 0 // PointerToRelocations (0 for executables) - .long 0 // PointerToLineNumbers (0 for executables) - .short 0 // NumberOfRelocations (0 for executables) - .short 0 // NumberOfLineNumbers (0 for executables) - /* - * EFI_IMAGE_SCN_MEM_READ | EFI_IMAGE_SCN_MEM_EXECUTE | EFI_IMAGE_SCN_CNT_CODE - */ - .long 0x60000020 // Characteristics (section flags) - - .ascii ".data\0\0\0" - .long _data_vsize // VirtualSize - .long _data - ImageBase // VirtualAddress - .long _data_size // SizeOfRawData - .long _data - ImageBase // PointerToRawData - - .long 0 // PointerToRelocations (0 for executables) - .long 0 // PointerToLineNumbers (0 for executables) - .short 0 // NumberOfRelocations (0 for executables) - .short 0 // NumberOfLineNumbers (0 for executables) - /* - * EFI_IMAGE_SCN_MEM_WRITE | EFI_IMAGE_SCN_MEM_READ | EFI_IMAGE_SCN_CNT_INITIALIZED_DATA - */ - .long 0xc0000040 // Characteristics (section flags) - - .ascii ".sbat\0\0\0" - .long _sbat_vsize // VirtualSize - .long _sbat - ImageBase // VirtualAddress - .long _sbat_size // SizeOfRawData - .long _sbat - ImageBase // PointerToRawData - - .long 0 // PointerToRelocations (0 for executables) - .long 0 // PointerToLineNumbers (0 for executables) - .short 0 // NumberOfRelocations (0 for executables) - .short 0 // NumberOfLineNumbers (0 for executables) - /* - * EFI_IMAGE_SCN_MEM_READ | EFI_IMAGE_SCN_ALIGN_8BYTES | EFI_IMAGE_SCN_CNT_INITIALIZED_DATA - */ - .long 0x40400040 // Characteristics (section flags) - - .ascii ".rodata\0" - .long _rodata_vsize // VirtualSize - .long _rodata - ImageBase // VirtualAddress - .long _rodata_size // SizeOfRawData - .long _rodata - ImageBase // PointerToRawData - - .long 0 // PointerToRelocations (0 for executables) - .long 0 // PointerToLineNumbers (0 for executables) - .short 0 // NumberOfRelocations (0 for executables) - .short 0 // NumberOfLineNumbers (0 for executables) - /* - * EFI_IMAGE_SCN_MEM_READ | EFI_IMAGE_SCN_ALIGN_8BYTES | EFI_IMAGE_SCN_CNT_INITIALIZED_DATA - */ - .long 0x40400040 // Characteristics (section flags) + .text .align 12 + + .globl _start _start: stp x29, x30, [sp, #-32]! mov x29, sp @@ -164,3 +39,15 @@ _start: 0: ldp x29, x30, [sp], #32 ret + + // hand-craft a dummy .reloc section so EFI knows it's a relocatable executable: + .data +.dummy0: +.dummy1: + .4byte 0 + +#define IMAGE_REL_ABSOLUTE 0 + .section .reloc, "a" + .4byte .dummy1-.dummy0 // Page RVA + .4byte 10 // Block Size (2*4+2) + .2byte (IMAGE_REL_ABSOLUTE<<12) + 0 // reloc for dummy diff --git a/gnu-efi/gnuefi/crt0-efi-ia32.S b/gnu-efi/gnuefi/crt0-efi-ia32.S index 2c56746..c9b3ea6 100644 --- a/gnu-efi/gnuefi/crt0-efi-ia32.S +++ b/gnu-efi/gnuefi/crt0-efi-ia32.S @@ -68,10 +68,10 @@ _start: .data .dummy0: .dummy1: - .long 0 + .4byte 0 #define IMAGE_REL_ABSOLUTE 0 .section .reloc, "a" - .long .dummy1-.dummy0 // Page RVA - .long 10 // Block Size (2*4+2) - .word (IMAGE_REL_ABSOLUTE<<12) + 0 // reloc for dummy + .4byte .dummy1-.dummy0 // Page RVA + .4byte 10 // Block Size (2*4+2) + .2byte (IMAGE_REL_ABSOLUTE<<12) + 0 // reloc for dummy diff --git a/gnu-efi/gnuefi/crt0-efi-x86_64.S b/gnu-efi/gnuefi/crt0-efi-x86_64.S index 1a87dbd..0d99c15 100644 --- a/gnu-efi/gnuefi/crt0-efi-x86_64.S +++ b/gnu-efi/gnuefi/crt0-efi-x86_64.S @@ -59,18 +59,16 @@ _start: call efi_main addq $8, %rsp -.exit: ret // hand-craft a dummy .reloc section so EFI knows it's a relocatable executable: .data .dummy0: .dummy1: - .long 0 + .4byte 0 #define IMAGE_REL_ABSOLUTE 0 .section .reloc, "a" - .long .dummy1-.dummy0 // Page RVA - .long 10 // Block Size (2*4+2) - .word (IMAGE_REL_ABSOLUTE<<12) + 0 // reloc for dummy - + .4byte .dummy1-.dummy0 // Page RVA + .4byte 10 // Block Size (2*4+2) + .2byte (IMAGE_REL_ABSOLUTE<<12) + 0 // reloc for dummy diff --git a/gnu-efi/inc/efidef.h b/gnu-efi/inc/efidef.h index a552c7d..841cc9a 100644 --- a/gnu-efi/inc/efidef.h +++ b/gnu-efi/inc/efidef.h @@ -169,12 +169,13 @@ typedef enum { #define EFI_MEMORY_WC 0x0000000000000002 #define EFI_MEMORY_WT 0x0000000000000004 #define EFI_MEMORY_WB 0x0000000000000008 -#define EFI_MEMORY_UCE 0x0000000000000010 - -// physical memory protection on range +#define EFI_MEMORY_UCE 0x0000000000000010 #define EFI_MEMORY_WP 0x0000000000001000 + +// physical memory protection on range #define EFI_MEMORY_RP 0x0000000000002000 #define EFI_MEMORY_XP 0x0000000000004000 +#define EFI_MEMORY_RO 0x0000000000020000 // range requires a runtime mapping #define EFI_MEMORY_RUNTIME 0x8000000000000000 diff --git a/gnu-efi/inc/efiprot.h b/gnu-efi/inc/efiprot.h index c83a574..4013ab2 100644 --- a/gnu-efi/inc/efiprot.h +++ b/gnu-efi/inc/efiprot.h @@ -1422,4 +1422,39 @@ typedef struct _EFI_EBC_PROTOCOL { EFI_EBC_GET_VERSION GetVersion; } EFI_EBC_PROTOCOL; +INTERFACE_DECL(_EFI_MEMORY_ATTRIBUTE_PROTOCOL); + +typedef +EFI_STATUS +(EFIAPI *EFI_GET_MEMORY_ATTRIBUTES)( + IN struct _EFI_MEMORY_ATTRIBUTE_PROTOCOL *This, + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + OUT UINT64 *Attributes + ); + +typedef +EFI_STATUS +(EFIAPI *EFI_SET_MEMORY_ATTRIBUTES)( + IN struct _EFI_MEMORY_ATTRIBUTE_PROTOCOL *This, + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + IN UINT64 Attributes + ); + +typedef +EFI_STATUS +(EFIAPI *EFI_CLEAR_MEMORY_ATTRIBUTES)( + IN struct _EFI_MEMORY_ATTRIBUTE_PROTOCOL *This, + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + IN UINT64 Attributes + ); + +typedef struct _EFI_MEMORY_ATTRIBUTE_PROTOCOL { + EFI_GET_MEMORY_ATTRIBUTES GetMemoryAttributes; + EFI_SET_MEMORY_ATTRIBUTES SetMemoryAttributes; + EFI_CLEAR_MEMORY_ATTRIBUTES ClearMemoryAttributes; +} EFI_MEMORY_ATTRIBUTE_PROTOCOL; + #endif diff --git a/include/compiler.h b/include/compiler.h index 1857672..b4bf103 100644 --- a/include/compiler.h +++ b/include/compiler.h @@ -47,8 +47,12 @@ #define ALIAS(x) __attribute__((weak, alias (#x))) #endif #ifndef ALLOCFUNC +#if defined(__COVERITY__) +#define ALLOCFUNC(a, b) +#else #define ALLOCFUNC(dealloc, dealloc_arg) __attribute__((__malloc__(dealloc, dealloc_arg))) #endif +#endif #ifndef PRINTF #define PRINTF(first, args...) __attribute__((__format__(printf, first, ## args))) #endif diff --git a/include/guid.h b/include/guid.h index 07a19a9..d9910ff 100644 --- a/include/guid.h +++ b/include/guid.h @@ -33,8 +33,8 @@ extern EFI_GUID EFI_SECURE_BOOT_DB_GUID; extern EFI_GUID EFI_SIMPLE_FILE_SYSTEM_GUID; extern EFI_GUID SECURITY_PROTOCOL_GUID; extern EFI_GUID SECURITY2_PROTOCOL_GUID; +extern EFI_GUID EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID; extern EFI_GUID SHIM_LOCK_GUID; - extern EFI_GUID MOK_VARIABLE_STORE; #endif /* SHIM_GUID_H */ diff --git a/include/mok.h b/include/mok.h index 96da397..fb19423 100644 --- a/include/mok.h +++ b/include/mok.h @@ -59,6 +59,9 @@ struct mok_state_variable { UINT8 **addend; UINT32 *addend_size; + UINT8 **user_cert; + UINT32 *user_cert_size; + /* * build_cert is our build-time cert. Like addend, this is added * to the input variable, as part of the runtime variable, so that @@ -97,5 +100,10 @@ struct mok_variable_config_entry { UINT8 data[]; }; +/* + * bit definitions for MokPolicy + */ +#define MOK_POLICY_REQUIRE_NX 1 + #endif /* !SHIM_MOK_H_ */ // vim:fenc=utf-8:tw=75:noet diff --git a/include/pe.h b/include/pe.h index 43727f5..ccc8798 100644 --- a/include/pe.h +++ b/include/pe.h @@ -14,8 +14,12 @@ EFI_STATUS read_header(void *data, unsigned int datasize, PE_COFF_LOADER_IMAGE_CONTEXT *context); +EFI_STATUS verify_image(void *data, unsigned int datasize, + EFI_LOADED_IMAGE *li, + PE_COFF_LOADER_IMAGE_CONTEXT *context); + EFI_STATUS -handle_sbat(char *SBATBase, size_t SBATSize); +verify_sbat_section(char *SBATBase, size_t SBATSize); EFI_STATUS handle_image (void *data, unsigned int datasize, diff --git a/include/peimage.h b/include/peimage.h index 3b3f01a..e97b29c 100644 --- a/include/peimage.h +++ b/include/peimage.h @@ -17,10 +17,14 @@ #include "wincert.h" -#define SIGNATURE_16(A, B) ((A) | (B << 8)) -#define SIGNATURE_32(A, B, C, D) (SIGNATURE_16 (A, B) | (SIGNATURE_16 (C, D) << 16)) -#define SIGNATURE_64(A, B, C, D, E, F, G, H) \ - (SIGNATURE_32 (A, B, C, D) | ((UINT64) (SIGNATURE_32 (E, F, G, H)) << 32)) +#define SIGNATURE_16(A, B) \ + ((UINT16)(((UINT16)(A)) | (((UINT16)(B)) << ((UINT16)8)))) +#define SIGNATURE_32(A, B, C, D) \ + ((UINT32)(((UINT32)SIGNATURE_16(A, B)) | \ + (((UINT32)SIGNATURE_16(C, D)) << (UINT32)16))) +#define SIGNATURE_64(A, B, C, D, E, F, G, H) \ + ((UINT64)((UINT64)SIGNATURE_32(A, B, C, D) | \ + ((UINT64)(SIGNATURE_32(E, F, G, H)) << (UINT64)32))) #define ALIGN_VALUE(Value, Alignment) ((Value) + (((Alignment) - (Value)) & ((Alignment) - 1))) #define ALIGN_POINTER(Pointer, Alignment) ((VOID *) (ALIGN_VALUE ((UINTN)(Pointer), (Alignment)))) @@ -236,6 +240,24 @@ typedef struct { EFI_IMAGE_DATA_DIRECTORY DataDirectory[EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES]; } EFI_IMAGE_OPTIONAL_HEADER64; +#define EFI_IMAGE_DLLCHARACTERISTICS_RESERVED_0001 0x0001 +#define EFI_IMAGE_DLLCHARACTERISTICS_RESERVED_0002 0x0002 +#define EFI_IMAGE_DLLCHARACTERISTICS_RESERVED_0004 0x0004 +#define EFI_IMAGE_DLLCHARACTERISTICS_RESERVED_0008 0x0008 +#if 0 /* This is not in the PE spec. */ +#define EFI_IMAGE_DLLCHARACTERISTICS_RESERVED_0010 0x0010 +#endif +#define EFI_IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA 0x0020 +#define EFI_IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE 0x0040 +#define EFI_IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY 0x0080 +#define EFI_IMAGE_DLLCHARACTERISTICS_NX_COMPAT 0x0100 +#define EFI_IMAGE_DLLCHARACTERISTICS_NO_ISOLATION 0x0200 +#define EFI_IMAGE_DLLCHARACTERISTICS_NO_SEH 0x0400 +#define EFI_IMAGE_DLLCHARACTERISTICS_NO_BIND 0x0800 +#define EFI_IMAGE_DLLCHARACTERISTICS_APPCONTAINER 0x1000 +#define EFI_IMAGE_DLLCHARACTERISTICS_WDM_DRIVER 0x2000 +#define EFI_IMAGE_DLLCHARACTERISTICS_GUARD_CF 0x4000 +#define EFI_IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE 0x8000 /// /// @attention @@ -303,16 +325,31 @@ typedef struct { // // Section Flags Values // -#define EFI_IMAGE_SCN_TYPE_NO_PAD 0x00000008 ///< Reserved. +#define EFI_IMAGE_SCN_RESERVED_00000000 0x00000000 +#define EFI_IMAGE_SCN_RESERVED_00000001 0x00000001 +#define EFI_IMAGE_SCN_RESERVED_00000002 0x00000002 +#define EFI_IMAGE_SCN_RESERVED_00000004 0x00000004 +#define EFI_IMAGE_SCN_TYPE_NO_PAD 0x00000008 +#define EFI_IMAGE_SCN_RESERVED_00000010 0x00000010 #define EFI_IMAGE_SCN_CNT_CODE 0x00000020 #define EFI_IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040 #define EFI_IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080 - -#define EFI_IMAGE_SCN_LNK_OTHER 0x00000100 ///< Reserved. -#define EFI_IMAGE_SCN_LNK_INFO 0x00000200 ///< Section contains comments or some other type of information. -#define EFI_IMAGE_SCN_LNK_REMOVE 0x00000800 ///< Section contents will not become part of image. +#define EFI_IMAGE_SCN_LNK_OTHER 0x00000100 +#define EFI_IMAGE_SCN_LNK_INFO 0x00000200 +#define EFI_IMAGE_SCN_RESERVED_00000400 0x00000400 +#define EFI_IMAGE_SCN_LNK_REMOVE 0x00000800 #define EFI_IMAGE_SCN_LNK_COMDAT 0x00001000 - +#define EFI_IMAGE_SCN_RESERVED_00002000 0x00002000 +#define EFI_IMAGE_SCN_RESERVED_00004000 0x00004000 +#define EFI_IMAGE_SCN_GPREL 0x00008000 +/* + * PE 9.3 says both IMAGE_SCN_MEM_PURGEABLE and IMAGE_SCN_MEM_16BIT are + * 0x00020000, but I think it's wrong. --pjones + */ +#define EFI_IMAGE_SCN_MEM_PURGEABLE 0x00010000 // "Reserved for future use." +#define EFI_IMAGE_SCN_MEM_16BIT 0x00020000 // "Reserved for future use." +#define EFI_IMAGE_SCN_MEM_LOCKED 0x00040000 // "Reserved for future use." +#define EFI_IMAGE_SCN_MEM_PRELOAD 0x00080000 // "Reserved for future use." #define EFI_IMAGE_SCN_ALIGN_1BYTES 0x00100000 #define EFI_IMAGE_SCN_ALIGN_2BYTES 0x00200000 #define EFI_IMAGE_SCN_ALIGN_4BYTES 0x00300000 @@ -320,7 +357,14 @@ typedef struct { #define EFI_IMAGE_SCN_ALIGN_16BYTES 0x00500000 #define EFI_IMAGE_SCN_ALIGN_32BYTES 0x00600000 #define EFI_IMAGE_SCN_ALIGN_64BYTES 0x00700000 - +#define EFI_IMAGE_SCN_ALIGN_128BYTES 0x00800000 +#define EFI_IMAGE_SCN_ALIGN_256BYTES 0x00900000 +#define EFI_IMAGE_SCN_ALIGN_512BYTES 0x00a00000 +#define EFI_IMAGE_SCN_ALIGN_1024BYTES 0x00b00000 +#define EFI_IMAGE_SCN_ALIGN_2048BYTES 0x00c00000 +#define EFI_IMAGE_SCN_ALIGN_4096BYTES 0x00d00000 +#define EFI_IMAGE_SCN_ALIGN_8192BYTES 0x00e00000 +#define EFI_IMAGE_SCN_LNK_NRELOC_OVFL 0x01000000 #define EFI_IMAGE_SCN_MEM_DISCARDABLE 0x02000000 #define EFI_IMAGE_SCN_MEM_NOT_CACHED 0x04000000 #define EFI_IMAGE_SCN_MEM_NOT_PAGED 0x08000000 diff --git a/include/sbat.h b/include/sbat.h index 8551b74..aca4359 100644 --- a/include/sbat.h +++ b/include/sbat.h @@ -8,8 +8,35 @@ #define SBAT_VAR_SIG "sbat," #define SBAT_VAR_VERSION "1," -#define SBAT_VAR_DATE "2021030218" -#define SBAT_VAR SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_DATE "\n" +#define SBAT_VAR_ORIGINAL_DATE "2021030218" +#define SBAT_VAR_ORIGINAL \ + SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_ORIGINAL_DATE "\n" + +#if defined(ENABLE_SHIM_DEVEL) +#define SBAT_VAR_PREVIOUS_DATE "2022020101" +#define SBAT_VAR_PREVIOUS_REVOCATIONS "component,2\n" +#define SBAT_VAR_PREVIOUS \ + SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_PREVIOUS_DATE "\n" \ + SBAT_VAR_PREVIOUS_REVOCATIONS + +#define SBAT_VAR_LATEST_DATE "2022050100" +#define SBAT_VAR_LATEST_REVOCATIONS "component,2\nothercomponent,2\n" +#define SBAT_VAR_LATEST \ + SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_LATEST_DATE "\n" \ + SBAT_VAR_LATEST_REVOCATIONS +#else /* !ENABLE_SHIM_DEVEL */ +#define SBAT_VAR_PREVIOUS_DATE SBAT_VAR_ORIGINAL_DATE +#define SBAT_VAR_PREVIOUS_REVOCATIONS +#define SBAT_VAR_PREVIOUS \ + SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_PREVIOUS_DATE "\n" \ + SBAT_VAR_PREVIOUS_REVOCATIONS + +#define SBAT_VAR_LATEST_DATE "2022052400" +#define SBAT_VAR_LATEST_REVOCATIONS "shim,2\ngrub,2\n" +#define SBAT_VAR_LATEST \ + SBAT_VAR_SIG SBAT_VAR_VERSION SBAT_VAR_LATEST_DATE "\n" \ + SBAT_VAR_LATEST_REVOCATIONS +#endif /* ENABLE_SHIM_DEVEL */ #define UEFI_VAR_NV_BS \ (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS) @@ -33,6 +60,13 @@ #define SBAT_VAR_ATTRS UEFI_VAR_NV_BS #endif +#define SBAT_POLICY L"SbatPolicy" +#define SBAT_POLICY8 "SbatPolicy" + +#define SBAT_POLICY_LATEST 1 +#define SBAT_POLICY_PREVIOUS 2 +#define SBAT_POLICY_RESET 3 + extern UINTN _sbat, _esbat; struct sbat_var_entry { @@ -51,7 +85,8 @@ extern list_t sbat_var; EFI_STATUS parse_sbat_var(list_t *entries); void cleanup_sbat_var(list_t *entries); EFI_STATUS set_sbat_uefi_variable(void); -bool preserve_sbat_uefi_variable(UINT8 *sbat, UINTN sbatsize, UINT32 attributes); +bool preserve_sbat_uefi_variable(UINT8 *sbat, UINTN sbatsize, + UINT32 attributes, char *sbar_var); struct sbat_section_entry { const CHAR8 *component_name; diff --git a/include/test-data-efivars-1.h b/include/test-data-efivars-1.h index 55090ed..2831bd2 100644 --- a/include/test-data-efivars-1.h +++ b/include/test-data-efivars-1.h @@ -102,5 +102,9 @@ static const unsigned char test_data_efivars_1_SbatLevelRT[] = { 0x32, 0x31, 0x30, 0x33, 0x30, 0x32, 0x31, 0x38, 0x0a }; +static const unsigned char test_data_efivars_1_MokListTrustedRT[] ={ + 0x01 +}; + #endif /* !TEST_DATA_EFIVARS_1_H_ */ // vim:fenc=utf-8:tw=75:noet diff --git a/include/test.mk b/include/test.mk index 1a4fc22..e965c60 100644 --- a/include/test.mk +++ b/include/test.mk @@ -50,6 +50,9 @@ CFLAGS = $(OPTIMIZATIONS) -std=gnu11 \ # of the "include" directory CFLAGS += -isystem $(shell $(CC) $(ARCH_CFLAGS) -print-file-name=include-fixed) +# And on Debian also check the multi-arch include path +CFLAGS += -isystem /usr/include/$(shell $(CC) $(ARCH_CFLAGS) -print-multiarch) + export CFLAGS_LTO CFLAGS_GCOV libefi-test.a : diff --git a/lib/Makefile b/lib/Makefile index a4a4855..f81c5c9 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -41,6 +41,10 @@ CFLAGS = $(FEATUREFLAGS) \ $(INCLUDES) \ $(DEFINES) +ifneq ($(origin ENABLE_SHIM_DEVEL),undefined) +CFLAGS += -DENABLE_SHIM_DEVEL +endif + lib.a: $(LIBFILES) $(AR) rcs lib.a $(LIBFILES) diff --git a/lib/console.c b/lib/console.c index 7be5d54..6bbb8a7 100644 --- a/lib/console.c +++ b/lib/console.c @@ -122,6 +122,30 @@ console_print_at(UINTN col, UINTN row, const CHAR16 *fmt, ...) return ret; } +static struct { + CHAR16 up_left; + CHAR16 up_right; + CHAR16 down_left; + CHAR16 down_right; + CHAR16 horizontal; + CHAR16 vertical; +} boxdraw[2] = { + { + BOXDRAW_UP_LEFT, + BOXDRAW_UP_RIGHT, + BOXDRAW_DOWN_LEFT, + BOXDRAW_DOWN_RIGHT, + BOXDRAW_HORIZONTAL, + BOXDRAW_VERTICAL + }, { + '+', + '+', + '+', + '+', + '-', + '|' + } +}; void console_print_box_at(CHAR16 *str_arr[], int highlight, @@ -133,6 +157,7 @@ console_print_box_at(CHAR16 *str_arr[], int highlight, SIMPLE_TEXT_OUTPUT_INTERFACE *co = ST->ConOut; UINTN rows, cols; CHAR16 *Line; + bool char_set; if (lines == 0) return; @@ -181,10 +206,16 @@ console_print_box_at(CHAR16 *str_arr[], int highlight, return; } - SetMem16 (Line, size_cols * 2, BOXDRAW_HORIZONTAL); + /* test if boxdraw characters work */ + co->SetCursorPosition(co, start_col, start_row); + Line[0] = boxdraw[0].up_left; + Line[1] = L'\0'; + char_set = co->OutputString(co, Line) == 0 ? 0 : 1; - Line[0] = BOXDRAW_DOWN_RIGHT; - Line[size_cols - 1] = BOXDRAW_DOWN_LEFT; + SetMem16 (Line, size_cols * 2, boxdraw[char_set].horizontal); + + Line[0] = boxdraw[char_set].down_right; + Line[size_cols - 1] = boxdraw[char_set].down_left; Line[size_cols] = L'\0'; co->SetCursorPosition(co, start_col, start_row); co->OutputString(co, Line); @@ -204,8 +235,8 @@ console_print_box_at(CHAR16 *str_arr[], int highlight, int line = i - start; SetMem16 (Line, size_cols*2, L' '); - Line[0] = BOXDRAW_VERTICAL; - Line[size_cols - 1] = BOXDRAW_VERTICAL; + Line[0] = boxdraw[char_set].vertical; + Line[size_cols - 1] = boxdraw[char_set].vertical; Line[size_cols] = L'\0'; if (line >= 0 && line < lines) { CHAR16 *s = str_arr[line]; @@ -227,9 +258,9 @@ console_print_box_at(CHAR16 *str_arr[], int highlight, EFI_BACKGROUND_BLUE); } - SetMem16 (Line, size_cols * 2, BOXDRAW_HORIZONTAL); - Line[0] = BOXDRAW_UP_RIGHT; - Line[size_cols - 1] = BOXDRAW_UP_LEFT; + SetMem16 (Line, size_cols * 2, boxdraw[char_set].horizontal); + Line[0] = boxdraw[char_set].up_right; + Line[size_cols - 1] = boxdraw[char_set].up_left; Line[size_cols] = L'\0'; co->SetCursorPosition(co, start_col, i); co->OutputString(co, Line); @@ -522,7 +553,6 @@ console_mode_handle(VOID) efi_status = BS->LocateProtocol(&gop_guid, NULL, (void **)&gop); if (EFI_ERROR(efi_status)) { - console_error(L"Locate graphic output protocol fail", efi_status); return; } @@ -702,7 +732,7 @@ setup_verbosity(VOID) UINTN verbose_check_size; verbose_check_size = sizeof(verbose); - efi_status = get_variable(L"SHIM_VERBOSE", &verbose_check_ptr, + efi_status = get_variable(VERBOSE_VAR_NAME, &verbose_check_ptr, &verbose_check_size, SHIM_LOCK_GUID); if (!EFI_ERROR(efi_status)) { verbose = *(__typeof__(verbose) *)verbose_check_ptr; diff --git a/lib/guid.c b/lib/guid.c index 143e0bb..e100c92 100644 --- a/lib/guid.c +++ b/lib/guid.c @@ -32,6 +32,6 @@ EFI_GUID EFI_SECURE_BOOT_DB_GUID = { 0xd719b2cb, 0x3d3a, 0x4596, { 0xa3, 0xbc, EFI_GUID EFI_SIMPLE_FILE_SYSTEM_GUID = SIMPLE_FILE_SYSTEM_PROTOCOL; EFI_GUID SECURITY_PROTOCOL_GUID = { 0xA46423E3, 0x4617, 0x49f1, {0xB9, 0xFF, 0xD1, 0xBF, 0xA9, 0x11, 0x58, 0x39 } }; EFI_GUID SECURITY2_PROTOCOL_GUID = { 0x94ab2f58, 0x1438, 0x4ef1, {0x91, 0x52, 0x18, 0x94, 0x1a, 0x3a, 0x0e, 0x68 } }; - +EFI_GUID EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID = { 0xf4560cf6, 0x40ec, 0x4b4a, {0xa1, 0x92, 0xbf, 0x1d, 0x57, 0xd0, 0xb1, 0x89} }; EFI_GUID SHIM_LOCK_GUID = {0x605dab50, 0xe046, 0x4300, {0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23 } }; EFI_GUID MOK_VARIABLE_STORE = {0xc451ed2b, 0x9694, 0x45d3, {0xba, 0xba, 0xed, 0x9f, 0x89, 0x88, 0xa3, 0x89} }; diff --git a/mock-variables.c b/mock-variables.c index e9bce54..0304454 100644 --- a/mock-variables.c +++ b/mock-variables.c @@ -445,7 +445,7 @@ free_var(struct mock_variable *var) static bool mock_sv_attrs_match(UINT32 old, UINT32 new) { - UINT32 mask = ~EFI_VARIABLE_APPEND_WRITE; + UINT32 mask = ~((UINT32)EFI_VARIABLE_APPEND_WRITE); return (old & mask) == (new & mask); } diff --git a/mok.c b/mok.c index 2896285..63ddfca 100644 --- a/mok.c +++ b/mok.c @@ -84,6 +84,7 @@ categorize_deauthorized(struct mok_state_variable *v) #define MOK_MIRROR_DELETE_FIRST 0x02 #define MOK_VARIABLE_MEASURE 0x04 #define MOK_VARIABLE_LOG 0x08 +#define MOK_VARIABLE_INVERSE 0x10 struct mok_state_variable mok_state_variable_data[] = { {.name = L"MokList", @@ -97,6 +98,8 @@ struct mok_state_variable mok_state_variable_data[] = { .categorize_addend = categorize_authorized, .addend = &vendor_authorized, .addend_size = &vendor_authorized_size, + .user_cert = &user_cert, + .user_cert_size = &user_cert_size, #if defined(ENABLE_SHIM_CERT) .build_cert = &build_cert, .build_cert_size = &build_cert_size, @@ -176,10 +179,24 @@ struct mok_state_variable mok_state_variable_data[] = { .no_attr = EFI_VARIABLE_RUNTIME_ACCESS, .flags = MOK_MIRROR_DELETE_FIRST | MOK_VARIABLE_MEASURE | + MOK_VARIABLE_INVERSE | MOK_VARIABLE_LOG, .pcr = 14, .state = &trust_mok_list, }, + {.name = L"MokPolicy", + .name8 = "MokPolicy", + .rtname = L"MokPolicyRT", + .rtname8 = "MokPolicyRT", + .guid = &SHIM_LOCK_GUID, + .yes_attr = EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_NON_VOLATILE, + .no_attr = EFI_VARIABLE_RUNTIME_ACCESS, + .flags = MOK_MIRROR_DELETE_FIRST | + MOK_VARIABLE_LOG, + .pcr = 14, + .state = &mok_policy, + }, { NULL, } }; size_t n_mok_state_variables = sizeof(mok_state_variable_data) / sizeof(mok_state_variable_data[0]); @@ -586,7 +603,8 @@ mirror_one_mok_variable(struct mok_state_variable *v, dprint(L"FullDataSize:0x%lx FullData:0x%llx\n", FullDataSize, FullData); } - + if (v->user_cert_size) + FullDataSize += *v->user_cert_size; } /* @@ -700,6 +718,10 @@ mirror_one_mok_variable(struct mok_state_variable *v, dprint(L"FullDataSize:%lu FullData:0x%llx p:0x%llx pos:%lld\n", FullDataSize, FullData, p, p-(uintptr_t)FullData); } + if (v->user_cert_size) { + CopyMem(p, *v->user_cert, *v->user_cert_size); + p += *v->user_cert_size; + } } /* @@ -846,7 +868,16 @@ EFI_STATUS import_one_mok_state(struct mok_state_variable *v, efi_status = get_variable_attr(v->name, &v->data, &v->data_size, *v->guid, &attrs); - if (efi_status == EFI_NOT_FOUND) { + if (efi_status == EFI_NOT_FOUND && + v->flags & MOK_VARIABLE_INVERSE) { + v->data = AllocateZeroPool(4); + if (!v->data) { + perror(L"Out of memory\n"); + return EFI_OUT_OF_RESOURCES; + } + v->data[0] = 0x01; + v->data_size = 1; + } else if (efi_status == EFI_NOT_FOUND) { v->data = NULL; v->data_size = 0; } else if (EFI_ERROR(efi_status)) { @@ -868,6 +899,11 @@ EFI_STATUS import_one_mok_state(struct mok_state_variable *v, attrs, v->no_attr); delete = TRUE; } + if (v->flags & MOK_VARIABLE_INVERSE) { + FreePool(v->data); + v->data = NULL; + v->data_size = 0; + } } } if (delete == TRUE) { diff --git a/pe.c b/pe.c index 92c2804..ba3e2bb 100644 --- a/pe.c +++ b/pe.c @@ -696,6 +696,7 @@ read_header(void *data, unsigned int datasize, EFI_IMAGE_OPTIONAL_HEADER_UNION *PEHdr = data; unsigned long HeaderWithoutDataDir, SectionHeaderOffset, OptHeaderSize; unsigned long FileAlignment = 0; + UINT16 DllFlags; if (datasize < sizeof (PEHdr->Pe32)) { perror(L"Invalid image\n"); @@ -790,13 +791,21 @@ read_header(void *data, unsigned int datasize, context->EntryPoint = PEHdr->Pe32Plus.OptionalHeader.AddressOfEntryPoint; context->RelocDir = &PEHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC]; context->SecDir = &PEHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]; + DllFlags = PEHdr->Pe32Plus.OptionalHeader.DllCharacteristics; } else { context->ImageAddress = PEHdr->Pe32.OptionalHeader.ImageBase; context->EntryPoint = PEHdr->Pe32.OptionalHeader.AddressOfEntryPoint; context->RelocDir = &PEHdr->Pe32.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC]; context->SecDir = &PEHdr->Pe32.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]; + DllFlags = PEHdr->Pe32.OptionalHeader.DllCharacteristics; } + if ((mok_policy & MOK_POLICY_REQUIRE_NX) && + !(DllFlags & EFI_IMAGE_DLLCHARACTERISTICS_NX_COMPAT)) { + perror(L"Policy requires NX, but image does not support NX\n"); + return EFI_UNSUPPORTED; + } + context->FirstSection = (EFI_IMAGE_SECTION_HEADER *)((char *)PEHdr + PEHdr->Pe32.FileHeader.SizeOfOptionalHeader + sizeof(UINT32) + sizeof(EFI_IMAGE_FILE_HEADER)); if (context->ImageSize < context->SizeOfHeaders) { @@ -820,7 +829,7 @@ read_header(void *data, unsigned int datasize, } EFI_STATUS -handle_sbat(char *SBATBase, size_t SBATSize) +verify_sbat_section(char *SBATBase, size_t SBATSize) { unsigned int i; EFI_STATUS efi_status; @@ -834,7 +843,12 @@ handle_sbat(char *SBATBase, size_t SBATSize) if (SBATBase == NULL || SBATSize == 0) { dprint(L"No .sbat section data\n"); - return EFI_SECURITY_VIOLATION; + /* + * SBAT is mandatory for binaries loaded by shim, but optional + * for binaries loaded outside of shim but verified via the + * protocol. + */ + return in_protocol ? EFI_SUCCESS : EFI_SECURITY_VIOLATION; } sbat_size = SBATSize + 1; @@ -873,6 +887,201 @@ err: return efi_status; } +static inline uint64_t +shim_mem_attrs_to_uefi_mem_attrs (uint64_t attrs) +{ + uint64_t ret = EFI_MEMORY_RP | + EFI_MEMORY_RO | + EFI_MEMORY_XP; + + if (attrs & MEM_ATTR_R) + ret &= ~EFI_MEMORY_RP; + + if (attrs & MEM_ATTR_W) + ret &= ~EFI_MEMORY_RO; + + if (attrs & MEM_ATTR_X) + ret &= ~EFI_MEMORY_XP; + + return ret; +} + +static inline uint64_t +uefi_mem_attrs_to_shim_mem_attrs (uint64_t attrs) +{ + uint64_t ret = MEM_ATTR_R | + MEM_ATTR_W | + MEM_ATTR_X; + + if (attrs & EFI_MEMORY_RP) + ret &= ~MEM_ATTR_R; + + if (attrs & EFI_MEMORY_RO) + ret &= ~MEM_ATTR_W; + + if (attrs & EFI_MEMORY_XP) + ret &= ~MEM_ATTR_X; + + return ret; +} + +static EFI_STATUS +get_mem_attrs (uintptr_t addr, size_t size, uint64_t *attrs) +{ + EFI_MEMORY_ATTRIBUTE_PROTOCOL *proto = NULL; + EFI_PHYSICAL_ADDRESS physaddr = addr; + EFI_STATUS efi_status; + + efi_status = LibLocateProtocol(&EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID, + (VOID **)&proto); + if (EFI_ERROR(efi_status) || !proto) + return efi_status; + + if (physaddr & 0xfff || size & 0xfff || size == 0 || attrs == NULL) { + dprint(L"%a called on 0x%llx-0x%llx and attrs 0x%llx\n", + __func__, (unsigned long long)physaddr, + (unsigned long long)(physaddr+size-1), + attrs); + return EFI_SUCCESS; + } + + efi_status = proto->GetMemoryAttributes(proto, physaddr, size, attrs); + *attrs = uefi_mem_attrs_to_shim_mem_attrs (*attrs); + + return efi_status; +} + +static EFI_STATUS +update_mem_attrs(uintptr_t addr, uint64_t size, + uint64_t set_attrs, uint64_t clear_attrs) +{ + EFI_MEMORY_ATTRIBUTE_PROTOCOL *proto = NULL; + EFI_PHYSICAL_ADDRESS physaddr = addr; + EFI_STATUS efi_status, ret; + uint64_t before = 0, after = 0, uefi_set_attrs, uefi_clear_attrs; + + efi_status = LibLocateProtocol(&EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID, + (VOID **)&proto); + if (EFI_ERROR(efi_status) || !proto) + return efi_status; + + efi_status = get_mem_attrs (addr, size, &before); + if (EFI_ERROR(efi_status)) + dprint(L"get_mem_attrs(0x%llx, 0x%llx, 0x%llx) -> 0x%lx\n", + (unsigned long long)addr, (unsigned long long)size, + &before, efi_status); + + if (physaddr & 0xfff || size & 0xfff || size == 0) { + dprint(L"%a called on 0x%llx-0x%llx (size 0x%llx) +%a%a%a -%a%a%a\n", + __func__, (unsigned long long)physaddr, + (unsigned long long)(physaddr + size - 1), + (unsigned long long)size, + (set_attrs & MEM_ATTR_R) ? "r" : "", + (set_attrs & MEM_ATTR_W) ? "w" : "", + (set_attrs & MEM_ATTR_X) ? "x" : "", + (clear_attrs & MEM_ATTR_R) ? "r" : "", + (clear_attrs & MEM_ATTR_W) ? "w" : "", + (clear_attrs & MEM_ATTR_X) ? "x" : ""); + return 0; + } + + uefi_set_attrs = shim_mem_attrs_to_uefi_mem_attrs (set_attrs); + dprint("translating set_attrs from 0x%lx to 0x%lx\n", set_attrs, uefi_set_attrs); + uefi_clear_attrs = shim_mem_attrs_to_uefi_mem_attrs (clear_attrs); + dprint("translating clear_attrs from 0x%lx to 0x%lx\n", clear_attrs, uefi_clear_attrs); + efi_status = EFI_SUCCESS; + if (uefi_set_attrs) + efi_status = proto->SetMemoryAttributes(proto, physaddr, size, uefi_set_attrs); + if (!EFI_ERROR(efi_status) && uefi_clear_attrs) + efi_status = proto->ClearMemoryAttributes(proto, physaddr, size, uefi_clear_attrs); + ret = efi_status; + + efi_status = get_mem_attrs (addr, size, &after); + if (EFI_ERROR(efi_status)) + dprint(L"get_mem_attrs(0x%llx, %llu, 0x%llx) -> 0x%lx\n", + (unsigned long long)addr, (unsigned long long)size, + &after, efi_status); + + dprint(L"set +%a%a%a -%a%a%a on 0x%llx-0x%llx before:%c%c%c after:%c%c%c\n", + (set_attrs & MEM_ATTR_R) ? "r" : "", + (set_attrs & MEM_ATTR_W) ? "w" : "", + (set_attrs & MEM_ATTR_X) ? "x" : "", + (clear_attrs & MEM_ATTR_R) ? "r" : "", + (clear_attrs & MEM_ATTR_W) ? "w" : "", + (clear_attrs & MEM_ATTR_X) ? "x" : "", + (unsigned long long)addr, (unsigned long long)(addr + size - 1), + (before & MEM_ATTR_R) ? 'r' : '-', + (before & MEM_ATTR_W) ? 'w' : '-', + (before & MEM_ATTR_X) ? 'x' : '-', + (after & MEM_ATTR_R) ? 'r' : '-', + (after & MEM_ATTR_W) ? 'w' : '-', + (after & MEM_ATTR_X) ? 'x' : '-'); + + return ret; +} + +EFI_STATUS verify_image(void *data, unsigned int datasize, + EFI_LOADED_IMAGE *li, + PE_COFF_LOADER_IMAGE_CONTEXT *context) +{ + EFI_STATUS efi_status; + UINT8 sha1hash[SHA1_DIGEST_SIZE]; + UINT8 sha256hash[SHA256_DIGEST_SIZE]; + + /* + * The binary header contains relevant context and section pointers + */ + efi_status = read_header(data, datasize, context); + if (EFI_ERROR(efi_status)) { + perror(L"Failed to read header: %r\n", efi_status); + return efi_status; + } + + /* + * Perform the image verification before we start copying data around + * in order to load it. + */ + if (secure_mode()) { + efi_status = verify_buffer(data, datasize, + context, sha256hash, sha1hash); + if (EFI_ERROR(efi_status)) { + if (verbose) + console_print(L"Verification failed: %r\n", efi_status); + else + console_error(L"Verification failed", efi_status); + return efi_status; + } else if (verbose) + console_print(L"Verification succeeded\n"); + } + + /* + * Calculate the hash for the TPM measurement. + * XXX: We're computing these twice in secure boot mode when the + * buffers already contain the previously computed hashes. Also, + * this is only useful for the TPM1.2 case. We should try to fix + * this in a follow-up. + */ + efi_status = generate_hash(data, datasize, context, sha256hash, + sha1hash); + if (EFI_ERROR(efi_status)) + return efi_status; + + /* Measure the binary into the TPM */ +#ifdef REQUIRE_TPM + efi_status = +#endif + tpm_log_pe((EFI_PHYSICAL_ADDRESS)(UINTN)data, datasize, + (EFI_PHYSICAL_ADDRESS)(UINTN)context->ImageAddress, + li->FilePath, sha1hash, 4); +#ifdef REQUIRE_TPM + if (efi_status != EFI_SUCCESS) { + return efi_status; + } +#endif + + return EFI_SUCCESS; +} + /* * Once the image has been loaded it needs to be validated and relocated */ @@ -888,6 +1097,7 @@ handle_image (void *data, unsigned int datasize, int i; EFI_IMAGE_SECTION_HEADER *Section; char *base, *end; + UINT32 size; PE_COFF_LOADER_IMAGE_CONTEXT context; unsigned int alignment, alloc_size; int found_entry_point = 0; @@ -904,7 +1114,31 @@ handle_image (void *data, unsigned int datasize, } /* - * We only need to verify the binary if we're in secure mode + * Perform the image verification before we start copying data around + * in order to load it. + */ + if (secure_mode ()) { + efi_status = verify_buffer(data, datasize, &context, sha256hash, + sha1hash); + + if (EFI_ERROR(efi_status)) { + if (verbose) + console_print(L"Verification failed: %r\n", efi_status); + else + console_error(L"Verification failed", efi_status); + return efi_status; + } else { + if (verbose) + console_print(L"Verification succeeded\n"); + } + } + + /* + * Calculate the hash for the TPM measurement. + * XXX: We're computing these twice in secure boot mode when the + * buffers already contain the previously computed hashes. Also, + * this is only useful for the TPM1.2 case. We should try to fix + * this in a follow-up. */ efi_status = generate_hash(data, datasize, &context, sha256hash, sha1hash); @@ -954,6 +1188,11 @@ handle_image (void *data, unsigned int datasize, } buffer = (void *)ALIGN_VALUE((unsigned long)*alloc_address, alignment); + dprint(L"Loading 0x%llx bytes at 0x%llx\n", + (unsigned long long)context.ImageSize, + (unsigned long long)(uintptr_t)buffer); + update_mem_attrs((uintptr_t)buffer, alloc_size, MEM_ATTR_R|MEM_ATTR_W, + MEM_ATTR_X); CopyMem(buffer, data, context.SizeOfHeaders); @@ -980,9 +1219,6 @@ handle_image (void *data, unsigned int datasize, EFI_IMAGE_SECTION_HEADER *RelocSection = NULL; - char *SBATBase = NULL; - size_t SBATSize = 0; - /* * Copy the executable's sections to their desired offsets */ @@ -993,6 +1229,20 @@ handle_image (void *data, unsigned int datasize, !Section->Misc.VirtualSize) continue; + /* + * Skip sections that aren't marked readable. + */ + if (!(Section->Characteristics & EFI_IMAGE_SCN_MEM_READ)) + continue; + + if (!(Section->Characteristics & EFI_IMAGE_SCN_MEM_DISCARDABLE) && + (Section->Characteristics & EFI_IMAGE_SCN_MEM_WRITE) && + (Section->Characteristics & EFI_IMAGE_SCN_MEM_EXECUTE) && + (mok_policy & MOK_POLICY_REQUIRE_NX)) { + perror(L"Section %d is writable and executable\n", i); + return EFI_UNSUPPORTED; + } + base = ImageAddress (buffer, context.ImageSize, Section->VirtualAddress); end = ImageAddress (buffer, context.ImageSize, @@ -1027,33 +1277,6 @@ handle_image (void *data, unsigned int datasize, RelocBaseEnd == end) { RelocSection = Section; } - } else if (CompareMem(Section->Name, ".sbat\0\0\0", 8) == 0) { - if (SBATBase || SBATSize) { - perror(L"Image has multiple SBAT sections\n"); - return EFI_UNSUPPORTED; - } - - if (Section->NumberOfRelocations != 0 || - Section->PointerToRelocations != 0) { - perror(L"SBAT section has relocations\n"); - return EFI_UNSUPPORTED; - } - - /* The virtual size corresponds to the size of the SBAT - * metadata and isn't necessarily a multiple of the file - * alignment. The on-disk size is a multiple of the file - * alignment and is zero padded. Make sure that the - * on-disk size is at least as large as virtual size, - * and ignore the section if it isn't. */ - if (Section->SizeOfRawData && - Section->SizeOfRawData >= Section->Misc.VirtualSize && - base && end) { - SBATBase = base; - /* +1 because of size vs last byte location */ - SBATSize = end - base + 1; - dprint(L"sbat section base:0x%lx size:0x%lx\n", - SBATBase, SBATSize); - } } if (Section->Characteristics & EFI_IMAGE_SCN_MEM_DISCARDABLE) { @@ -1084,32 +1307,15 @@ handle_image (void *data, unsigned int datasize, return EFI_UNSUPPORTED; } - if (Section->SizeOfRawData > 0) - CopyMem(base, data + Section->PointerToRawData, - Section->SizeOfRawData); + size = Section->Misc.VirtualSize; + if (size > Section->SizeOfRawData) + size = Section->SizeOfRawData; - if (Section->SizeOfRawData < Section->Misc.VirtualSize) - ZeroMem(base + Section->SizeOfRawData, - Section->Misc.VirtualSize - Section->SizeOfRawData); - } - } + if (size > 0) + CopyMem(base, data + Section->PointerToRawData, size); - if (secure_mode ()) { - efi_status = handle_sbat(SBATBase, SBATSize); - - if (!EFI_ERROR(efi_status)) - efi_status = verify_buffer(data, datasize, - &context, sha256hash, sha1hash); - - if (EFI_ERROR(efi_status)) { - if (verbose) - console_print(L"Verification failed: %r\n", efi_status); - else - console_error(L"Verification failed", efi_status); - return efi_status; - } else { - if (verbose) - console_print(L"Verification succeeded\n"); + if (size < Section->Misc.VirtualSize) + ZeroMem(base + size, Section->Misc.VirtualSize - size); } } @@ -1133,6 +1339,50 @@ handle_image (void *data, unsigned int datasize, } } + /* + * Now set the page permissions appropriately. + */ + Section = context.FirstSection; + for (i = 0; i < context.NumberOfSections; i++, Section++) { + uint64_t set_attrs = MEM_ATTR_R; + uint64_t clear_attrs = MEM_ATTR_W|MEM_ATTR_X; + uintptr_t addr; + uint64_t length; + + /* + * Skip discardable sections with zero size + */ + if ((Section->Characteristics & EFI_IMAGE_SCN_MEM_DISCARDABLE) && + !Section->Misc.VirtualSize) + continue; + + /* + * Skip sections that aren't marked readable. + */ + if (!(Section->Characteristics & EFI_IMAGE_SCN_MEM_READ)) + continue; + + base = ImageAddress (buffer, context.ImageSize, + Section->VirtualAddress); + end = ImageAddress (buffer, context.ImageSize, + Section->VirtualAddress + + Section->Misc.VirtualSize - 1); + + addr = (uintptr_t)base; + length = (uintptr_t)end - (uintptr_t)base + 1; + + if (Section->Characteristics & EFI_IMAGE_SCN_MEM_WRITE) { + set_attrs |= MEM_ATTR_W; + clear_attrs &= ~MEM_ATTR_W; + } + if (Section->Characteristics & EFI_IMAGE_SCN_MEM_EXECUTE) { + set_attrs |= MEM_ATTR_X; + clear_attrs &= ~MEM_ATTR_X; + } + update_mem_attrs(addr, length, set_attrs, clear_attrs); + } + + /* * grub needs to know its location and size in memory, so fix up * the loaded image protocol values diff --git a/post-process-pe.c b/post-process-pe.c index 8414a5f..de8f4a3 100644 --- a/post-process-pe.c +++ b/post-process-pe.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -41,6 +42,8 @@ static int verbosity; 0; \ }) +static bool set_nx_compat = false; + typedef uint8_t UINT8; typedef uint16_t UINT16; typedef uint32_t UINT32; @@ -174,7 +177,7 @@ load_pe(const char *const file, void *const data, const size_t datasize, } if (FileAlignment % 2 != 0) - errx(1, "%s: Invalid file alignment %ld", file, FileAlignment); + errx(1, "%s: Invalid file alignment %zu", file, FileAlignment); if (FileAlignment == 0) FileAlignment = 0x200; @@ -190,7 +193,7 @@ load_pe(const char *const file, void *const data, const size_t datasize, ctx->NumberOfRvaAndSizes, EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES); if (EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES < ctx->NumberOfRvaAndSizes) errx(1, "%s: invalid number of RVAs (%lu entries, max is %d)", - file, ctx->NumberOfRvaAndSizes, + file, (unsigned long)ctx->NumberOfRvaAndSizes, EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES); if (mul(sizeof(EFI_IMAGE_DATA_DIRECTORY), @@ -233,12 +236,12 @@ load_pe(const char *const file, void *const data, const size_t datasize, if (mul(ctx->NumberOfRvaAndSizes, sizeof(EFI_IMAGE_DATA_DIRECTORY), &sz1)) debug(ERROR, - "ctx->NumberOfRvaAndSizes (%zu) * sizeof(EFI_IMAGE_DATA_DIRECTORY) overflows\n", - ctx->NumberOfRvaAndSizes); + "ctx->NumberOfRvaAndSizes (%ld) * sizeof(EFI_IMAGE_DATA_DIRECTORY) overflows\n", + (unsigned long)ctx->NumberOfRvaAndSizes); else debug(ERROR, - "ctx->NumberOfRvaAndSizes (%zu) * sizeof(EFI_IMAGE_DATA_DIRECTORY) = %zu\n", - ctx->NumberOfRvaAndSizes, sz1); + "ctx->NumberOfRvaAndSizes (%ld) * sizeof(EFI_IMAGE_DATA_DIRECTORY) = %zu\n", + (unsigned long)ctx->NumberOfRvaAndSizes, sz1); debug(ERROR, "space after image header:%zu data directory size:%zu\n", sz0, sz1); @@ -271,7 +274,7 @@ load_pe(const char *const file, void *const data, const size_t datasize, if (sub(ctx->SizeOfHeaders, SectionHeaderOffset, &sz0) || div(sz0, EFI_IMAGE_SIZEOF_SECTION_HEADER, &sz0) || (sz0 < ctx->NumberOfSections)) { - debug(ERROR, "(%zu - %zu) / %d >= %d\n", ctx->SizeOfHeaders, + debug(ERROR, "(%zu - %zu) / %d >= %d\n", (size_t)ctx->SizeOfHeaders, SectionHeaderOffset, EFI_IMAGE_SIZEOF_SECTION_HEADER, ctx->NumberOfSections); errx(1, "%s: image sections overflow section headers", file); @@ -330,6 +333,33 @@ load_pe(const char *const file, void *const data, const size_t datasize, errx(1, "%s: Security directory extends past end", file); } +static void +set_dll_characteristics(PE_COFF_LOADER_IMAGE_CONTEXT *ctx) +{ + uint16_t oldflags, newflags; + + if (image_is_64_bit(ctx->PEHdr)) { + oldflags = ctx->PEHdr->Pe32Plus.OptionalHeader.DllCharacteristics; + } else { + oldflags = ctx->PEHdr->Pe32.OptionalHeader.DllCharacteristics; + } + + if (set_nx_compat) + newflags = oldflags | EFI_IMAGE_DLLCHARACTERISTICS_NX_COMPAT; + else + newflags = oldflags & ~(uint16_t)EFI_IMAGE_DLLCHARACTERISTICS_NX_COMPAT; + if (oldflags == newflags) + return; + + debug(INFO, "Updating DLL Characteristics from 0x%04hx to 0x%04hx\n", + oldflags, newflags); + if (image_is_64_bit(ctx->PEHdr)) { + ctx->PEHdr->Pe32Plus.OptionalHeader.DllCharacteristics = newflags; + } else { + ctx->PEHdr->Pe32.OptionalHeader.DllCharacteristics = newflags; + } +} + static void fix_timestamp(PE_COFF_LOADER_IMAGE_CONTEXT *ctx) { @@ -417,6 +447,8 @@ handle_one(char *f) load_pe(f, map, sz, &ctx); + set_dll_characteristics(&ctx); + fix_timestamp(&ctx); fix_checksum(&ctx, map, sz); @@ -426,7 +458,7 @@ handle_one(char *f) warn("msync(%p, %zu, MS_SYNC) failed", map, sz); failed = 1; } - munmap(map, sz); + rc = munmap(map, sz); if (rc < 0) { warn("munmap(%p, %zu) failed", map, sz); failed = 1; @@ -449,6 +481,8 @@ static void __attribute__((__noreturn__)) usage(int status) fprintf(out, "Options:\n"); fprintf(out, " -q Be more quiet\n"); fprintf(out, " -v Be more verbose\n"); + fprintf(out, " -N Disable the NX compatibility flag\n"); + fprintf(out, " -n Enable the NX compatibility flag\n"); fprintf(out, " -h Print this help text and exit\n"); exit(status); @@ -464,6 +498,12 @@ int main(int argc, char **argv) {.name = "usage", .val = '?', }, + {.name = "disable-nx-compat", + .val = 'N', + }, + {.name = "enable-nx-compat", + .val = 'n', + }, {.name = "quiet", .val = 'q', }, @@ -474,12 +514,18 @@ int main(int argc, char **argv) }; int longindex = -1; - while ((i = getopt_long(argc, argv, "hqsv", options, &longindex)) != -1) { + while ((i = getopt_long(argc, argv, "hNnqv", options, &longindex)) != -1) { switch (i) { case 'h': case '?': usage(longindex == -1 ? 1 : 0); break; + case 'N': + set_nx_compat = false; + break; + case 'n': + set_nx_compat = true; + break; case 'q': verbosity = MAX(verbosity - 1, MIN_VERBOSITY); break; diff --git a/sbat.c b/sbat.c index 6b7eb20..f1d6e98 100644 --- a/sbat.c +++ b/sbat.c @@ -113,13 +113,14 @@ cleanup_sbat_section_entries(size_t n, struct sbat_section_entry **entries) } EFI_STATUS -verify_single_entry(struct sbat_section_entry *entry, struct sbat_var_entry *sbat_var_entry) +verify_single_entry(struct sbat_section_entry *entry, struct sbat_var_entry *sbat_var_entry, bool *found) { UINT16 sbat_gen, sbat_var_gen; if (strcmp((const char *)entry->component_name, (const char *)sbat_var_entry->component_name) == 0) { dprint(L"component %a has a matching SBAT variable entry, verifying\n", entry->component_name); + *found = true; /* * atoi returns zero for failed conversion, so essentially @@ -172,10 +173,13 @@ verify_sbat_helper(list_t *local_sbat_var, size_t n, struct sbat_section_entry * for (i = 0; i < n; i++) { list_for_each(pos, local_sbat_var) { + bool found = false; sbat_var_entry = list_entry(pos, struct sbat_var_entry, list); - efi_status = verify_single_entry(entries[i], sbat_var_entry); + efi_status = verify_single_entry(entries[i], sbat_var_entry, &found); if (EFI_ERROR(efi_status)) goto out; + if (found) + break; } } @@ -285,6 +289,7 @@ parse_sbat_var(list_t *entries) UINT8 *data = 0; UINTN datasize; EFI_STATUS efi_status; + list_t *pos = NULL; if (!entries) { dprint(L"entries is NULL\n"); @@ -301,7 +306,20 @@ parse_sbat_var(list_t *entries) * We've intentionally made sure there's a NUL byte on all variable * allocations, so use that here. */ - return parse_sbat_var_data(entries, data, datasize+1); + efi_status = parse_sbat_var_data(entries, data, datasize+1); + if (EFI_ERROR(efi_status)) + return efi_status; + + dprint(L"SBAT variable entries:\n"); + list_for_each(pos, entries) { + struct sbat_var_entry *entry; + + entry = list_entry(pos, struct sbat_var_entry, list); + dprint(L"%a, %a, %a\n", entry->component_name, + entry->component_generation, entry->sbat_datestamp); + } + + return efi_status; } static bool @@ -315,12 +333,64 @@ check_sbat_var_attributes(UINT32 attributes) #endif } -bool -preserve_sbat_uefi_variable(UINT8 *sbat, UINTN sbatsize, UINT32 attributes) +static char * +nth_sbat_field(char *str, size_t limit, int n) { - return check_sbat_var_attributes(attributes) && - sbatsize >= strlen(SBAT_VAR_SIG "1") && - !strncmp((const char *)sbat, SBAT_VAR_SIG, strlen(SBAT_VAR_SIG)); + size_t i; + for (i = 0; i < limit && str[i] != '\0'; i++) { + if (n == 0) + return &str[i]; + if (str[i] == ',') + n--; + } + return &str[i]; +} + +bool +preserve_sbat_uefi_variable(UINT8 *sbat, UINTN sbatsize, UINT32 attributes, + char *sbat_var) +{ + char *sbatc = (char *)sbat; + char *current_version, *new_version, + *current_datestamp, *new_datestamp; + int current_version_len, new_version_len; + + /* current metadata is not currupt somehow */ + if (!check_sbat_var_attributes(attributes) || + sbatsize < strlen(SBAT_VAR_ORIGINAL) || + strncmp(sbatc, SBAT_VAR_SIG, strlen(SBAT_VAR_SIG))) + return false; + + /* current metadata version not newer */ + current_version = nth_sbat_field(sbatc, sbatsize, 1); + new_version = nth_sbat_field(sbat_var, strlen(sbat_var)+1, 1); + current_datestamp = nth_sbat_field(sbatc, sbatsize, 2); + new_datestamp = nth_sbat_field(sbat_var, strlen(sbat_var)+1, 2); + + current_version_len = current_datestamp - current_version - 1; + new_version_len = new_datestamp - new_version - 1; + + if (current_version_len > new_version_len || + (current_version_len == new_version_len && + strncmp(current_version, new_version, new_version_len) > 0)) + return true; + + /* current datestamp is not newer or idential */ + if (strncmp(current_datestamp, new_datestamp, + strlen(SBAT_VAR_ORIGINAL_DATE)) >= 0) + return true; + + return false; +} + +static void +clear_sbat_policy() +{ + EFI_STATUS efi_status = EFI_SUCCESS; + + efi_status = del_variable(SBAT_POLICY, SHIM_LOCK_GUID); + if (EFI_ERROR(efi_status)) + console_error(L"Could not reset SBAT Policy", efi_status); } EFI_STATUS @@ -330,7 +400,49 @@ set_sbat_uefi_variable(void) UINT32 attributes = 0; UINT8 *sbat = NULL; + UINT8 *sbat_policy = NULL; UINTN sbatsize = 0; + UINTN sbat_policysize = 0; + + char *sbat_var = NULL; + bool reset_sbat = false; + + efi_status = get_variable_attr(SBAT_POLICY, &sbat_policy, + &sbat_policysize, SHIM_LOCK_GUID, + &attributes); + if (EFI_ERROR(efi_status)) { + dprint("Default sbat policy: previous\n"); + sbat_var = SBAT_VAR_PREVIOUS; + } else { + switch (*sbat_policy) { + case SBAT_POLICY_LATEST: + dprint("Custom sbat policy: latest\n"); + sbat_var = SBAT_VAR_LATEST; + clear_sbat_policy(); + break; + case SBAT_POLICY_PREVIOUS: + dprint("Custom sbat policy: previous\n"); + sbat_var = SBAT_VAR_PREVIOUS; + break; + case SBAT_POLICY_RESET: + if (secure_mode()) { + console_print(L"Cannot reset SBAT policy: Secure Boot is enabled.\n"); + sbat_var = SBAT_VAR_PREVIOUS; + } else { + dprint(L"Custom SBAT policy: reset OK\n"); + reset_sbat = true; + sbat_var = SBAT_VAR_ORIGINAL; + } + clear_sbat_policy(); + break; + default: + console_error(L"SBAT policy state %llu is invalid", + EFI_INVALID_PARAMETER); + sbat_var = SBAT_VAR_PREVIOUS; + clear_sbat_policy(); + break; + } + } efi_status = get_variable_attr(SBAT_VAR_NAME, &sbat, &sbatsize, SHIM_LOCK_GUID, &attributes); @@ -342,8 +454,9 @@ set_sbat_uefi_variable(void) */ if (EFI_ERROR(efi_status)) { dprint(L"SBAT read failed %r\n", efi_status); - } else if (preserve_sbat_uefi_variable(sbat, sbatsize, attributes)) { - dprint(L"%s variable is %d bytes, attributes are 0x%08x\n", + } else if (preserve_sbat_uefi_variable(sbat, sbatsize, attributes, sbat_var) + && !reset_sbat) { + dprint(L"preserving %s variable it is %d bytes, attributes are 0x%08x\n", SBAT_VAR_NAME, sbatsize, attributes); FreePool(sbat); return EFI_SUCCESS; @@ -365,7 +478,7 @@ set_sbat_uefi_variable(void) /* set variable */ efi_status = set_variable(SBAT_VAR_NAME, SHIM_LOCK_GUID, SBAT_VAR_ATTRS, - sizeof(SBAT_VAR)-1, SBAT_VAR); + strlen(sbat_var), sbat_var); if (EFI_ERROR(efi_status)) { dprint(L"%s variable writing failed %r\n", SBAT_VAR_NAME, efi_status); @@ -380,10 +493,10 @@ set_sbat_uefi_variable(void) return efi_status; } - if (sbatsize != strlen(SBAT_VAR) || - strncmp((const char *)sbat, SBAT_VAR, strlen(SBAT_VAR)) != 0) { + if (sbatsize != strlen(sbat_var) || + strncmp((const char *)sbat, sbat_var, strlen(sbat_var)) != 0) { dprint("new sbatsize is %d, expected %d\n", sbatsize, - strlen(SBAT_VAR)); + strlen(sbat_var)); efi_status = EFI_INVALID_PARAMETER; } else { dprint(L"%s variable initialization succeeded\n", SBAT_VAR_NAME); diff --git a/shim.c b/shim.c index 604c0db..fdd205e 100644 --- a/shim.c +++ b/shim.c @@ -559,9 +559,9 @@ verify_one_signature(WIN_CERTIFICATE_EFI_PKCS *sig, * Check that the signature is valid and matches the binary */ EFI_STATUS -verify_buffer (char *data, int datasize, - PE_COFF_LOADER_IMAGE_CONTEXT *context, - UINT8 *sha256hash, UINT8 *sha1hash) +verify_buffer_authenticode (char *data, int datasize, + PE_COFF_LOADER_IMAGE_CONTEXT *context, + UINT8 *sha256hash, UINT8 *sha1hash) { EFI_STATUS ret_efi_status; size_t size = datasize; @@ -695,6 +695,71 @@ verify_buffer (char *data, int datasize, return ret_efi_status; } +/* + * Check that the binary is permitted to load by SBAT. + */ +EFI_STATUS +verify_buffer_sbat (char *data, int datasize, + PE_COFF_LOADER_IMAGE_CONTEXT *context) +{ + int i; + EFI_IMAGE_SECTION_HEADER *Section; + char *SBATBase = NULL; + size_t SBATSize = 0; + + Section = context->FirstSection; + for (i = 0; i < context->NumberOfSections; i++, Section++) { + if (CompareMem(Section->Name, ".sbat\0\0\0", 8) != 0) + continue; + + if (SBATBase || SBATSize) { + perror(L"Image has multiple SBAT sections\n"); + return EFI_UNSUPPORTED; + } + + if (Section->NumberOfRelocations != 0 || + Section->PointerToRelocations != 0) { + perror(L"SBAT section has relocations\n"); + return EFI_UNSUPPORTED; + } + + /* The virtual size corresponds to the size of the SBAT + * metadata and isn't necessarily a multiple of the file + * alignment. The on-disk size is a multiple of the file + * alignment and is zero padded. Make sure that the + * on-disk size is at least as large as virtual size, + * and ignore the section if it isn't. */ + if (Section->SizeOfRawData && + Section->SizeOfRawData >= Section->Misc.VirtualSize) { + SBATBase = ImageAddress(data, datasize, + Section->PointerToRawData); + SBATSize = Section->SizeOfRawData; + dprint(L"sbat section base:0x%lx size:0x%lx\n", + SBATBase, SBATSize); + } + } + + return verify_sbat_section(SBATBase, SBATSize); +} + +/* + * Check that the signature is valid and matches the binary and that + * the binary is permitted to load by SBAT. + */ +EFI_STATUS +verify_buffer (char *data, int datasize, + PE_COFF_LOADER_IMAGE_CONTEXT *context, + UINT8 *sha256hash, UINT8 *sha1hash) +{ + EFI_STATUS efi_status; + + efi_status = verify_buffer_sbat(data, datasize, context); + if (EFI_ERROR(efi_status)) + return efi_status; + + return verify_buffer_authenticode(data, datasize, context, sha256hash, sha1hash); +} + static int is_removable_media_path(EFI_LOADED_IMAGE *li) { @@ -988,17 +1053,12 @@ restore_loaded_image(VOID) /* * Load and run an EFI executable */ -EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath) +EFI_STATUS read_image(EFI_HANDLE image_handle, CHAR16 *ImagePath, + CHAR16 **PathName, void **data, int *datasize) { EFI_STATUS efi_status; - EFI_IMAGE_ENTRY_POINT entry_point; - EFI_PHYSICAL_ADDRESS alloc_address; - UINTN alloc_pages; - CHAR16 *PathName = NULL; void *sourcebuffer = NULL; UINT64 sourcesize = 0; - void *data = NULL; - int datasize = 0; /* * We need to refer to the loaded image protocol on the running @@ -1014,11 +1074,11 @@ EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath) /* * Build a new path from the existing one plus the executable name */ - efi_status = generate_path_from_image_path(shim_li, ImagePath, &PathName); + efi_status = generate_path_from_image_path(shim_li, ImagePath, PathName); if (EFI_ERROR(efi_status)) { perror(L"Unable to generate path %s: %r\n", ImagePath, efi_status); - goto done; + return efi_status; } if (findNetboot(shim_li->DeviceHandle)) { @@ -1034,8 +1094,8 @@ EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath) efi_status); return efi_status; } - data = sourcebuffer; - datasize = sourcesize; + *data = sourcebuffer; + *datasize = sourcesize; } else if (find_httpboot(shim_li->DeviceHandle)) { efi_status = httpboot_fetch_buffer (image_handle, &sourcebuffer, @@ -1045,26 +1105,45 @@ EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath) efi_status); return efi_status; } - data = sourcebuffer; - datasize = sourcesize; + *data = sourcebuffer; + *datasize = sourcesize; } else { /* * Read the new executable off disk */ - efi_status = load_image(shim_li, &data, &datasize, PathName); + efi_status = load_image(shim_li, data, datasize, *PathName); if (EFI_ERROR(efi_status)) { perror(L"Failed to load image %s: %r\n", PathName, efi_status); PrintErrors(); ClearErrors(); - goto done; + return efi_status; } } - if (datasize < 0) { + if (*datasize < 0) efi_status = EFI_INVALID_PARAMETER; + + return efi_status; +} + +/* + * Load and run an EFI executable + */ +EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath) +{ + EFI_STATUS efi_status; + EFI_IMAGE_ENTRY_POINT entry_point; + EFI_PHYSICAL_ADDRESS alloc_address; + UINTN alloc_pages; + CHAR16 *PathName = NULL; + void *data = NULL; + int datasize = 0; + + efi_status = read_image(image_handle, ImagePath, &PathName, &data, + &datasize); + if (EFI_ERROR(efi_status)) goto done; - } /* * We need to modify the loaded image protocol entry before running @@ -1312,6 +1391,127 @@ uninstall_shim_protocols(void) #endif } +EFI_STATUS +load_cert_file(EFI_HANDLE image_handle, CHAR16 *filename, CHAR16 *PathName) +{ + EFI_STATUS efi_status; + EFI_LOADED_IMAGE li; + PE_COFF_LOADER_IMAGE_CONTEXT context; + EFI_IMAGE_SECTION_HEADER *Section; + EFI_SIGNATURE_LIST *certlist; + void *pointer; + UINT32 original; + int datasize = 0; + void *data = NULL; + int i; + + efi_status = read_image(image_handle, filename, &PathName, + &data, &datasize); + if (EFI_ERROR(efi_status)) + return efi_status; + + memset(&li, 0, sizeof(li)); + memcpy(&li.FilePath[0], filename, MIN(StrSize(filename), sizeof(li.FilePath))); + + efi_status = verify_image(data, datasize, &li, &context); + if (EFI_ERROR(efi_status)) + return efi_status; + + Section = context.FirstSection; + for (i = 0; i < context.NumberOfSections; i++, Section++) { + if (CompareMem(Section->Name, ".db\0\0\0\0\0", 8) == 0) { + original = user_cert_size; + if (Section->SizeOfRawData < sizeof(EFI_SIGNATURE_LIST)) { + continue; + } + pointer = ImageAddress(data, datasize, + Section->PointerToRawData); + if (!pointer) { + continue; + } + certlist = pointer; + user_cert_size += certlist->SignatureListSize;; + user_cert = ReallocatePool(user_cert, original, + user_cert_size); + memcpy(user_cert + original, pointer, + certlist->SignatureListSize); + } + } + FreePool(data); + return EFI_SUCCESS; +} + +/* Read additional certificates from files (after verifying signatures) */ +EFI_STATUS +load_certs(EFI_HANDLE image_handle) +{ + EFI_STATUS efi_status; + EFI_LOADED_IMAGE *li = NULL; + CHAR16 *PathName = NULL; + EFI_FILE *root, *dir; + EFI_FILE_INFO *info; + EFI_HANDLE device; + EFI_FILE_IO_INTERFACE *drive; + UINTN buffersize = 0; + void *buffer = NULL; + + efi_status = gBS->HandleProtocol(image_handle, &EFI_LOADED_IMAGE_GUID, + (void **)&li); + if (EFI_ERROR(efi_status)) { + perror(L"Unable to init protocol\n"); + return efi_status; + } + + efi_status = generate_path_from_image_path(li, L"", &PathName); + if (EFI_ERROR(efi_status)) + goto done; + + device = li->DeviceHandle; + efi_status = gBS->HandleProtocol(device, &EFI_SIMPLE_FILE_SYSTEM_GUID, + (void **)&drive); + if (EFI_ERROR(efi_status)) { + perror(L"Failed to find fs: %r\n", efi_status); + goto done; + } + + efi_status = drive->OpenVolume(drive, &root); + if (EFI_ERROR(efi_status)) { + perror(L"Failed to open fs: %r\n", efi_status); + goto done; + } + + efi_status = root->Open(root, &dir, PathName, EFI_FILE_MODE_READ, 0); + if (EFI_ERROR(efi_status)) { + perror(L"Failed to open %s - %r\n", PathName, efi_status); + goto done; + } + + while (1) { + int old = buffersize; + efi_status = dir->Read(dir, &buffersize, buffer); + if (efi_status == EFI_BUFFER_TOO_SMALL) { + buffer = ReallocatePool(buffer, old, buffersize); + continue; + } else if (EFI_ERROR(efi_status)) { + perror(L"Failed to read directory %s - %r\n", PathName, + efi_status); + goto done; + } + + info = (EFI_FILE_INFO *)buffer; + if (buffersize == 0 || !info) + goto done; + + if (StrnCaseCmp(info->FileName, L"shim_certificate", 16) == 0) { + load_cert_file(image_handle, info->FileName, PathName); + } + } +done: + FreePool(buffer); + FreePool(PathName); + return efi_status; +} + EFI_STATUS shim_init(void) { @@ -1385,17 +1585,10 @@ debug_hook(void) register volatile UINTN x = 0; extern char _text, _data; - const CHAR16 * const debug_var_name = -#ifdef ENABLE_SHIM_DEVEL - L"SHIM_DEVEL_DEBUG"; -#else - L"SHIM_DEBUG"; -#endif - if (x) return; - efi_status = get_variable(debug_var_name, &data, &dataSize, + efi_status = get_variable(DEBUG_VAR_NAME, &data, &dataSize, SHIM_LOCK_GUID); if (EFI_ERROR(efi_status)) { return; @@ -1409,7 +1602,7 @@ debug_hook(void) console_print(L"Pausing for debugger attachment.\n"); console_print(L"To disable this, remove the EFI variable %s-%g .\n", - debug_var_name, &SHIM_LOCK_GUID); + DEBUG_VAR_NAME, &SHIM_LOCK_GUID); x = 1; while (x++) { /* Make this so it can't /totally/ DoS us. */ @@ -1542,7 +1735,7 @@ efi_main (EFI_HANDLE passed_image_handle, EFI_SYSTEM_TABLE *passed_systab) goto die; } - efi_status = handle_sbat(sbat_start, sbat_end - sbat_start - 1); + efi_status = verify_sbat_section(sbat_start, sbat_end - sbat_start - 1); if (EFI_ERROR(efi_status)) { perror(L"Verifiying shim SBAT data failed: %r\n", efi_status); @@ -1554,6 +1747,13 @@ efi_main (EFI_HANDLE passed_image_handle, EFI_SYSTEM_TABLE *passed_systab) init_openssl(); + if (secure_mode()) { + efi_status = load_certs(global_image_handle); + if (EFI_ERROR(efi_status)) { + LogError(L"Failed to load addon certificates\n"); + } + } + /* * Before we do anything else, validate our non-volatile, * boot-services-only state variables are what we think they are. diff --git a/shim.h b/shim.h index 69442da..b5272b9 100644 --- a/shim.h +++ b/shim.h @@ -195,6 +195,10 @@ #include "Cryptlib/Include/OpenSslSupport.h" #endif +#define MEM_ATTR_R 4 +#define MEM_ATTR_W 2 +#define MEM_ATTR_X 1 + INTERFACE_DECL(_SHIM_LOCK); typedef @@ -248,6 +252,9 @@ extern UINT8 *vendor_authorized; extern UINT32 vendor_deauthorized_size; extern UINT8 *vendor_deauthorized; +extern UINT32 user_cert_size; +extern UINT8 *user_cert; + #if defined(ENABLE_SHIM_CERT) extern UINT32 build_cert_size; extern UINT8 *build_cert; @@ -256,6 +263,8 @@ extern UINT8 *build_cert; extern UINT8 user_insecure_mode; extern UINT8 ignore_db; extern UINT8 trust_mok_list; +extern UINT8 mok_policy; + extern UINT8 in_protocol; extern void *load_options; extern UINT32 load_options_size; @@ -284,6 +293,16 @@ verify_buffer (char *data, int datasize, #define LogError(fmt, ...) #endif +#ifdef ENABLE_SHIM_DEVEL +#define FALLBACK_VERBOSE_VAR_NAME L"FALLBACK_DEVEL_VERBOSE" +#define VERBOSE_VAR_NAME L"SHIM_DEVEL_VERBOSE" +#define DEBUG_VAR_NAME L"SHIM_DEVEL_DEBUG" +#else +#define FALLBACK_VERBOSE_VAR_NAME L"FALLBACK_VERBOSE" +#define VERBOSE_VAR_NAME L"SHIM_VERBOSE" +#define DEBUG_VAR_NAME L"SHIM_DEBUG" +#endif + char *translate_slashes(char *out, const char *str); #endif /* SHIM_H_ */ diff --git a/test-mok-mirror.c b/test-mok-mirror.c index 3479ddf..f34f62a 100644 --- a/test-mok-mirror.c +++ b/test-mok-mirror.c @@ -184,6 +184,10 @@ test_mok_mirror_0(void) .data_size = sizeof(test_data_efivars_1_SbatLevelRT), .data = test_data_efivars_1_SbatLevelRT }, + {.name = "MokListTrustedRT", + .data_size = sizeof(test_data_efivars_1_MokListTrustedRT), + .data = test_data_efivars_1_MokListTrustedRT + }, {.name = { 0, }, .data_size = 0, .data = NULL, diff --git a/test-sbat.c b/test-sbat.c index b64aa1e..72bebe7 100644 --- a/test-sbat.c +++ b/test-sbat.c @@ -14,6 +14,11 @@ list_t sbat_var; +BOOLEAN +secure_mode() { + return 1; +} + #if 0 /* * Mock test helpers @@ -965,11 +970,96 @@ err: int test_preserve_sbat_uefi_variable_good(void) { - char sbat[] = "sbat,1,\ncomponent,2,\n"; + char sbat[] = "sbat,1,2021030218\ncomponent,2,\n"; + char sbatvar[] = "sbat,1,2021030218\ncomponent,2,\n"; size_t sbat_size = sizeof(sbat); UINT32 attributes = SBAT_VAR_ATTRS; - if (preserve_sbat_uefi_variable(sbat, sbat_size, attributes)) + if (preserve_sbat_uefi_variable(sbat, sbat_size, attributes, sbatvar)) + return 0; + else + return -1; +} + +int +test_preserve_sbat_uefi_variable_version_newer(void) +{ + char sbat[] = "sbat,2,2022030218\ncomponent,2,\n"; + char sbatvar[] = "sbat,1,2021030218\ncomponent,2,\n"; + size_t sbat_size = sizeof(sbat); + UINT32 attributes = SBAT_VAR_ATTRS; + + if (preserve_sbat_uefi_variable(sbat, sbat_size, attributes, sbatvar)) + return 0; + else + return -1; +} + +int +test_preserve_sbat_uefi_variable_version_newerlonger(void) +{ + char sbat[] = "sbat,10,2022030218\ncomponent,2,\n"; + char sbatvar[] = "sbat,2,2021030218\ncomponent,2,\n"; + size_t sbat_size = sizeof(sbat); + UINT32 attributes = SBAT_VAR_ATTRS; + + if (preserve_sbat_uefi_variable(sbat, sbat_size, attributes, sbatvar)) + return 0; + else + return -1; +} + +int +test_preserve_sbat_uefi_variable_version_older(void) +{ + char sbat[] = "sbat,1,2021030218\ncomponent,2,\n"; + char sbatvar[] = "sbat,2,2022030218\ncomponent,2,\n"; + size_t sbat_size = sizeof(sbat); + UINT32 attributes = SBAT_VAR_ATTRS; + + if (preserve_sbat_uefi_variable(sbat, sbat_size, attributes, sbatvar)) + return -1; + else + return 0; +} + +int +test_preserve_sbat_uefi_variable_version_olderlonger(void) +{ + char sbat[] = "sbat,2,2021030218\ncomponent,2,\n"; + char sbatvar[] = "sbat,10,2022030218\ncomponent,2,\n"; + size_t sbat_size = sizeof(sbat); + UINT32 attributes = SBAT_VAR_ATTRS; + + if (preserve_sbat_uefi_variable(sbat, sbat_size, attributes, sbatvar)) + return -1; + else + return 0; +} + + +int +test_preserve_sbat_uefi_variable_newer(void) +{ + char sbat[] = "sbat,1,2021030218\ncomponent,2,\n"; + char sbatvar[] = "sbat,1,2025030218\ncomponent,5,\ncomponent,3"; + size_t sbat_size = sizeof(sbat); + UINT32 attributes = SBAT_VAR_ATTRS; + + if (preserve_sbat_uefi_variable(sbat, sbat_size, attributes, sbatvar)) + return -1; + else + return 0; +} +int +test_preserve_sbat_uefi_variable_older(void) +{ + char sbat[] = "sbat,1,2025030218\ncomponent,2,\ncomponent,3"; + char sbatvar[] = "sbat,1,2020030218\ncomponent,1,\n"; + size_t sbat_size = sizeof(sbat); + UINT32 attributes = SBAT_VAR_ATTRS; + + if (preserve_sbat_uefi_variable(sbat, sbat_size, attributes, sbatvar)) return 0; else return -1; @@ -978,11 +1068,12 @@ test_preserve_sbat_uefi_variable_good(void) int test_preserve_sbat_uefi_variable_bad_sig(void) { - char sbat[] = "bad_sig,1,\ncomponent,2,\n"; + char sbat[] = "bad_sig,1,2021030218\ncomponent,2,\n"; + char sbatvar[] = "sbat,1,2021030218\n"; size_t sbat_size = sizeof(sbat); UINT32 attributes = SBAT_VAR_ATTRS; - if (preserve_sbat_uefi_variable(sbat, sbat_size, attributes)) + if (preserve_sbat_uefi_variable(sbat, sbat_size, attributes, sbatvar)) return -1; else return 0; @@ -991,11 +1082,12 @@ test_preserve_sbat_uefi_variable_bad_sig(void) int test_preserve_sbat_uefi_variable_bad_attr(void) { - char sbat[] = "sbat,1,\ncomponent,2,\n"; + char sbat[] = "sbat,1,2021030218\ncomponent,2,\n"; + char sbatvar[] = "sbat,1,2021030218\n"; size_t sbat_size = sizeof(sbat); UINT32 attributes = 0; - if (preserve_sbat_uefi_variable(sbat, sbat_size, attributes)) + if (preserve_sbat_uefi_variable(sbat, sbat_size, attributes, sbatvar)) return -1; else return 0; @@ -1005,10 +1097,11 @@ int test_preserve_sbat_uefi_variable_bad_short(void) { char sbat[] = "sba"; + char sbatvar[] = "sbat,1,2021030218\n"; size_t sbat_size = sizeof(sbat); UINT32 attributes = SBAT_VAR_ATTRS; - if (preserve_sbat_uefi_variable(sbat, sbat_size, attributes)) + if (preserve_sbat_uefi_variable(sbat, sbat_size, attributes, sbatvar)) return -1; else return 0; @@ -1052,9 +1145,15 @@ main(void) test(test_parse_and_verify); test(test_preserve_sbat_uefi_variable_good); + test(test_preserve_sbat_uefi_variable_newer); + test(test_preserve_sbat_uefi_variable_older); test(test_preserve_sbat_uefi_variable_bad_sig); test(test_preserve_sbat_uefi_variable_bad_attr); test(test_preserve_sbat_uefi_variable_bad_short); + test(test_preserve_sbat_uefi_variable_version_newer); + test(test_preserve_sbat_uefi_variable_version_newerlonger); + test(test_preserve_sbat_uefi_variable_version_older); + test(test_preserve_sbat_uefi_variable_version_olderlonger); return 0; } diff --git a/test-str.c b/test-str.c index 9cb831d..30f8dd0 100644 --- a/test-str.c +++ b/test-str.c @@ -926,12 +926,15 @@ static int test_strncpy(void) { char s[] = "0123456789abcdef\0000"; - char s0[4096+4096]; - char *s1 = &s0[4096]; + char s0[4096]; + char s1[4096]; memset(s0, 0, sizeof(s0)); memcpy(s0, s, sizeof(s)); - +#if __GNUC_PREREQ(8, 1) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wstringop-truncation" +#endif memset(s1, 0, 4096); assert_equal_return(strncpy(s1, s0, 0), s1, -1, "got %p expected %p\n"); assert_equal_return(strlen(s1), 0, -1, "got %d expected %d\n"); @@ -1030,7 +1033,9 @@ test_strncpy(void) assert_equal_return(s1[16], '\000', -1, "got %#02hhx expected %02hhx\n"); assert_equal_return(s1[17], '0', -1, "got %#02hhx expected %02hhx\n"); assert_equal_return(s1[18], '1', -1, "got %#02hhx expected %02hhx\n"); - +#if __GNUC_PREREQ(8, 1) +# pragma GCC diagnostic pop +#endif return 0; } @@ -1038,12 +1043,12 @@ static int test_strcat(void) { char s[] = "0123456789abcdef\0000"; - char s0[8192]; - char *s1 = &s0[4096]; + char s0[4096]; + char s1[4096]; char *s2; char s3[] = "0123456789abcdef0123456789abcdef\000\000\000\000\000"; - memset(s0, 0, 8192); + memset(s0, 0, sizeof(s0)); memcpy(s0, s, sizeof(s)); memset(s1, 0, 4096); diff --git a/test.c b/test.c index 46cab53..c7978fa 100644 --- a/test.c +++ b/test.c @@ -259,6 +259,12 @@ console_print(const CHAR16 *fmt, ...) return 0; } +void +console_error(CHAR16 *err, EFI_STATUS efi_status) +{ + return; +} + #ifndef HAVE_START_IMAGE EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath)