sd-boot: stub: Obtain PE section offsets from RAM, not disk (#6250)

In a Secure Boot scenario the stub loader will have been validated
before execution. A malicious drive could then change the data returned
in future reads, resulting in the loader obtaining incorrect section
offsets and (for instance) allowing the command line to be modified.
Pull that information out of the in-RAM representation of the loader
instead in order to avoid this.

Fixes: #6230

(Lennart did some minor coding style fixes, and renamed pefile.c → pe.c,
as suggested by Kay, given that the file now contains a function whose
name doesn't match the filename as prefix anymore.)
This commit is contained in:
Matthew Garrett 2017-06-30 11:27:47 -07:00 committed by Lennart Poettering
parent 9db307820e
commit d4cbada2a9
5 changed files with 84 additions and 82 deletions

View file

@ -20,10 +20,10 @@
#include "disk.h"
#include "graphics.h"
#include "linux.h"
#include "pefile.h"
#include "util.h"
#include "measure.h"
#include "pe.h"
#include "shim.h"
#include "util.h"
#ifndef EFI_OS_INDICATIONS_BOOT_TO_FW_UI
#define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001ULL
@ -1543,7 +1543,7 @@ static VOID config_entry_add_linux( Config *config, EFI_LOADED_IMAGE *loaded_ima
continue;
/* look for .osrel and .cmdline sections in the .efi binary */
err = pefile_locate_sections(linux_dir, f->FileName, sections, addrs, offs, szs);
err = pe_file_locate_sections(linux_dir, f->FileName, sections, addrs, offs, szs);
if (EFI_ERROR(err))
continue;

View file

@ -4,7 +4,7 @@ efi_headers = files('''
graphics.h
linux.h
measure.h
pefile.h
pe.h
splash.h
util.h
shim.h
@ -14,7 +14,7 @@ common_sources = '''
disk.c
graphics.c
measure.c
pefile.c
pe.c
util.c
'''.split()

View file

@ -15,7 +15,7 @@
#include <efi.h>
#include <efilib.h>
#include "pefile.h"
#include "pe.h"
#include "util.h"
struct DosFileHeader {
@ -52,6 +52,11 @@ struct PeFileHeader {
UINT16 Characteristics;
} __attribute__((packed));
struct PeHeader {
UINT8 Magic[4];
struct PeFileHeader FileHeader;
} __attribute__((packed));
struct PeSectionHeader {
UINT8 Name[8];
UINT32 VirtualSize;
@ -65,15 +70,61 @@ struct PeSectionHeader {
UINT32 Characteristics;
} __attribute__((packed));
EFI_STATUS pe_memory_locate_sections(CHAR8 *base, CHAR8 **sections, UINTN *addrs, UINTN *offsets, UINTN *sizes) {
struct DosFileHeader *dos;
struct PeHeader *pe;
UINTN i;
UINTN offset;
EFI_STATUS pefile_locate_sections(EFI_FILE *dir, CHAR16 *path, CHAR8 **sections, UINTN *addrs, UINTN *offsets, UINTN *sizes) {
dos = (struct DosFileHeader *)base;
if (CompareMem(dos->Magic, "MZ", 2) != 0)
return EFI_LOAD_ERROR;
pe = (struct PeHeader *)&base[dos->ExeHeader];
if (CompareMem(pe->Magic, "PE\0\0", 4) != 0)
return EFI_LOAD_ERROR;
/* PE32+ Subsystem type */
if (pe->FileHeader.Machine != PE_HEADER_MACHINE_X64 &&
pe->FileHeader.Machine != PE_HEADER_MACHINE_I386)
return EFI_LOAD_ERROR;
if (pe->FileHeader.NumberOfSections > 96)
return EFI_LOAD_ERROR;
offset = dos->ExeHeader + sizeof(*pe) + pe->FileHeader.SizeOfOptionalHeader;
for (i = 0; i < pe->FileHeader.NumberOfSections; i++) {
struct PeSectionHeader *sect;
UINTN j;
sect = (struct PeSectionHeader *)&base[offset];
for (j = 0; sections[j]; j++) {
if (CompareMem(sect->Name, sections[j], strlena(sections[j])) != 0)
continue;
if (addrs)
addrs[j] = (UINTN)sect->VirtualAddress;
if (offsets)
offsets[j] = (UINTN)sect->PointerToRawData;
if (sizes)
sizes[j] = (UINTN)sect->VirtualSize;
}
offset += sizeof(*sect);
}
return EFI_SUCCESS;
}
EFI_STATUS pe_file_locate_sections(EFI_FILE *dir, CHAR16 *path, CHAR8 **sections, UINTN *addrs, UINTN *offsets, UINTN *sizes) {
EFI_FILE_HANDLE handle;
struct DosFileHeader dos;
uint8_t magic[4];
struct PeFileHeader pe;
struct PeHeader pe;
UINTN len;
UINTN i;
UINTN headerlen;
EFI_STATUS err;
CHAR8 *header = NULL;
err = uefi_call_wrapper(dir->Open, 5, dir, &handle, path, EFI_FILE_MODE_READ, 0ULL);
if (EFI_ERROR(err))
@ -89,30 +140,10 @@ EFI_STATUS pefile_locate_sections(EFI_FILE *dir, CHAR16 *path, CHAR8 **sections,
goto out;
}
if (CompareMem(dos.Magic, "MZ", 2) != 0) {
err = EFI_LOAD_ERROR;
goto out;
}
err = uefi_call_wrapper(handle->SetPosition, 2, handle, dos.ExeHeader);
if (EFI_ERROR(err))
goto out;
/* PE header */
len = sizeof(magic);
err = uefi_call_wrapper(handle->Read, 3, handle, &len, &magic);
if (EFI_ERROR(err))
goto out;
if (len != sizeof(magic)) {
err = EFI_LOAD_ERROR;
goto out;
}
if (CompareMem(magic, "PE\0\0", 2) != 0) {
err = EFI_LOAD_ERROR;
goto out;
}
len = sizeof(pe);
err = uefi_call_wrapper(handle->Read, 3, handle, &len, &pe);
if (EFI_ERROR(err))
@ -122,49 +153,30 @@ EFI_STATUS pefile_locate_sections(EFI_FILE *dir, CHAR16 *path, CHAR8 **sections,
goto out;
}
/* PE32+ Subsystem type */
if (pe.Machine != PE_HEADER_MACHINE_X64 &&
pe.Machine != PE_HEADER_MACHINE_I386) {
err = EFI_LOAD_ERROR;
headerlen = sizeof(dos) + sizeof(pe) + pe.FileHeader.SizeOfOptionalHeader + pe.FileHeader.NumberOfSections * sizeof(struct PeSectionHeader);
header = AllocatePool(headerlen);
if (!header) {
err = EFI_OUT_OF_RESOURCES;
goto out;
}
if (pe.NumberOfSections > 96) {
err = EFI_LOAD_ERROR;
goto out;
}
/* the sections start directly after the headers */
err = uefi_call_wrapper(handle->SetPosition, 2, handle, dos.ExeHeader + sizeof(magic) + sizeof(pe) + pe.SizeOfOptionalHeader);
len = headerlen;
err = uefi_call_wrapper(handle->SetPosition, 2, handle, 0);
if (EFI_ERROR(err))
goto out;
for (i = 0; i < pe.NumberOfSections; i++) {
struct PeSectionHeader sect;
UINTN j;
len = sizeof(sect);
err = uefi_call_wrapper(handle->Read, 3, handle, &len, &sect);
if (EFI_ERROR(err))
goto out;
if (len != sizeof(sect)) {
err = EFI_LOAD_ERROR;
goto out;
}
for (j = 0; sections[j]; j++) {
if (CompareMem(sect.Name, sections[j], strlena(sections[j])) != 0)
continue;
if (addrs)
addrs[j] = (UINTN)sect.VirtualAddress;
if (offsets)
offsets[j] = (UINTN)sect.PointerToRawData;
if (sizes)
sizes[j] = (UINTN)sect.VirtualSize;
}
err = uefi_call_wrapper(handle->Read, 3, handle, &len, header);
if (EFI_ERROR(err)) {
goto out;
}
if (len != headerlen) {
err = EFI_LOAD_ERROR;
goto out;
}
err = pe_memory_locate_sections(header, sections, addrs, offsets, sizes);
out:
if (header)
FreePool(header);
uefi_call_wrapper(handle->Close, 1, handle);
return err;
}

View file

@ -15,6 +15,8 @@
#ifndef __SDBOOT_PEFILE_H
#define __SDBOOT_PEFILE_H
EFI_STATUS pefile_locate_sections(EFI_FILE *dir, CHAR16 *path,
CHAR8 **sections, UINTN *addrs, UINTN *offsets, UINTN *sizes);
EFI_STATUS pe_memory_locate_sections(CHAR8 *base,
CHAR8 **sections, UINTN *addrs, UINTN *offsets, UINTN *sizes);
EFI_STATUS pe_file_locate_sections(EFI_FILE *dir, CHAR16 *path,
CHAR8 **sections, UINTN *addrs, UINTN *offsets, UINTN *sizes);
#endif

View file

@ -17,10 +17,10 @@
#include "disk.h"
#include "graphics.h"
#include "linux.h"
#include "pefile.h"
#include "measure.h"
#include "pe.h"
#include "splash.h"
#include "util.h"
#include "measure.h"
/* magic string to find in the binary image */
static const char __attribute__((used)) magic[] = "#### LoaderInfo: systemd-stub " PACKAGE_VERSION " ####";
@ -29,8 +29,6 @@ static const EFI_GUID global_guid = EFI_GLOBAL_VARIABLE;
EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
EFI_LOADED_IMAGE *loaded_image;
EFI_FILE *root_dir;
CHAR16 *loaded_image_path;
CHAR8 *b;
UINTN size;
BOOLEAN secure = FALSE;
@ -59,22 +57,12 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
return err;
}
root_dir = LibOpenRoot(loaded_image->DeviceHandle);
if (!root_dir) {
Print(L"Unable to open root directory: %r ", err);
uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);
return EFI_LOAD_ERROR;
}
loaded_image_path = DevicePathToStr(loaded_image->FilePath);
if (efivar_get_raw(&global_guid, L"SecureBoot", &b, &size) == EFI_SUCCESS) {
if (*b > 0)
secure = TRUE;
FreePool(b);
}
err = pefile_locate_sections(root_dir, loaded_image_path, sections, addrs, offs, szs);
err = pe_memory_locate_sections(loaded_image->ImageBase, sections, addrs, offs, szs);
if (EFI_ERROR(err)) {
Print(L"Unable to locate embedded .linux section: %r ", err);
uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000);