3 * Based mostly on shim.c by Matthew J. Garrett/Red Hat (see below
6 * Code to perform Secure Boot verification of boot loader programs
7 * using the Shim program and its Machine Owner Keys (MOKs), to
8 * supplement standard Secure Boot checks performed by the firmware.
13 * shim - trivial UEFI first-stage bootloader
15 * Copyright 2012 Red Hat, Inc <mjg@redhat.com>
17 * Redistribution and use in source and binary forms, with or without
18 * modification, are permitted provided that the following conditions
21 * Redistributions of source code must retain the above copyright
22 * notice, this list of conditions and the following disclaimer.
24 * Redistributions in binary form must reproduce the above copyright
25 * notice, this list of conditions and the following disclaimer in the
26 * documentation and/or other materials provided with the
29 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
30 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
31 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
32 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
33 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
34 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
35 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
36 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
37 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
38 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
39 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
40 * OF THE POSSIBILITY OF SUCH DAMAGE.
42 * Significant portions of this code are derived from Tianocore
43 * (http://tianocore.sf.net) and are Copyright 2009-2012 Intel
52 #include "../include/refit_call_wrapper.h"
53 #include "../include/PeImage.h"
55 #define SECOND_STAGE L"\\refind.efi"
56 #define MOK_MANAGER L"\\MokManager.efi"
58 static EFI_STATUS (EFIAPI
*entry_point
) (EFI_HANDLE image_handle
, EFI_SYSTEM_TABLE
*system_table
);
61 // * The vendor certificate used for validating the second stage loader
63 // extern UINT8 vendor_cert[];
64 // extern UINT32 vendor_cert_size;
65 // extern UINT32 vendor_dbx_size;
67 #define EFI_IMAGE_SECURITY_DATABASE_GUID { 0xd719b2cb, 0x3d3a, 0x4596, { 0xa3, 0xbc, 0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f }}
69 //static UINT8 insecure_mode;
83 static EFI_STATUS
get_variable (CHAR16
*name
, EFI_GUID guid
, UINT32
*attributes
, UINTN
*size
, VOID
**buffer
)
85 EFI_STATUS efi_status
;
86 char allocate
= !(*size
);
88 efi_status
= uefi_call_wrapper(RT
->GetVariable
, 5, name
, &guid
, attributes
, size
, buffer
);
90 if (efi_status
!= EFI_BUFFER_TOO_SMALL
|| !allocate
) {
94 *buffer
= AllocatePool(*size
);
97 Print(L
"Unable to allocate variable buffer\n");
98 return EFI_OUT_OF_RESOURCES
;
101 efi_status
= uefi_call_wrapper(RT
->GetVariable
, 5, name
, &guid
, attributes
, size
, *buffer
);
107 * Check whether we're in Secure Boot and user mode
109 BOOLEAN
secure_mode (VOID
)
112 EFI_GUID global_var
= EFI_GLOBAL_VARIABLE
;
113 UINTN charsize
= sizeof(char);
117 status
= get_variable(L
"SecureBoot", global_var
, &attributes
, &charsize
, (VOID
*)&sb
);
119 /* FIXME - more paranoia here? */
120 if (status
!= EFI_SUCCESS
|| sb
!= 1) {
124 status
= get_variable(L
"SetupMode", global_var
, &attributes
, &charsize
, (VOID
*)&setupmode
);
126 if (status
== EFI_SUCCESS
&& setupmode
== 1) {
133 // Returns TRUE if the shim program is available to verify binaries,
135 BOOLEAN
ShimLoaded(void) {
136 SHIM_LOCK
*shim_lock
;
137 EFI_GUID ShimLockGuid
= SHIM_LOCK_GUID
;
139 return (BS
->LocateProtocol(&ShimLockGuid
, NULL
, (VOID
**) &shim_lock
) == EFI_SUCCESS
);
143 * Currently, shim/MOK only works on x86-64 (X64) systems, and some of this code
144 * generates warnings on x86 (IA32) builds, so don't bother compiling it at all
152 * Perform basic bounds checking of the intra-image pointers
154 static void *ImageAddress (void *image
, int size
, unsigned int address
)
159 return image
+ address
;
163 * Perform the actual relocation
165 static EFI_STATUS
relocate_coff (GNUEFI_PE_COFF_LOADER_IMAGE_CONTEXT
*context
, void *data
)
167 EFI_IMAGE_BASE_RELOCATION
*RelocBase
, *RelocBaseEnd
;
169 UINT16
*Reloc
, *RelocEnd
;
170 char *Fixup
, *FixupBase
, *FixupData
= NULL
;
174 int size
= context
->ImageSize
;
175 void *ImageEnd
= (char *)data
+ size
;
177 context
->PEHdr
->Pe32Plus
.OptionalHeader
.ImageBase
= (UINT64
)data
;
179 // Linux kernels with EFI stub support don't have relocation information, so
180 // we can skip all this stuff....
181 if (((context
->RelocDir
->VirtualAddress
== 0) && (context
->RelocDir
->Size
== 0)) ||
182 ((context
->PEHdr
->Pe32
.FileHeader
.Characteristics
& EFI_IMAGE_FILE_RELOCS_STRIPPED
) != 0)) {
186 if (context
->NumberOfRvaAndSizes
<= EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC
) {
187 Print(L
"Image has no relocation entry\n");
188 return EFI_UNSUPPORTED
;
191 RelocBase
= ImageAddress(data
, size
, context
->RelocDir
->VirtualAddress
);
192 RelocBaseEnd
= ImageAddress(data
, size
, context
->RelocDir
->VirtualAddress
+ context
->RelocDir
->Size
- 1);
194 if (!RelocBase
|| !RelocBaseEnd
) {
195 Print(L
"Reloc table overflows binary\n");
196 return EFI_UNSUPPORTED
;
199 Adjust
= (UINT64
)data
- context
->ImageAddress
;
201 while (RelocBase
< RelocBaseEnd
) {
202 Reloc
= (UINT16
*) ((char *) RelocBase
+ sizeof (EFI_IMAGE_BASE_RELOCATION
));
203 RelocEnd
= (UINT16
*) ((char *) RelocBase
+ RelocBase
->SizeOfBlock
);
205 if ((void *)RelocEnd
< data
|| (void *)RelocEnd
> ImageEnd
) {
206 Print(L
"Reloc entry overflows binary\n");
207 return EFI_UNSUPPORTED
;
210 FixupBase
= ImageAddress(data
, size
, RelocBase
->VirtualAddress
);
212 Print(L
"Invalid fixupbase\n");
213 return EFI_UNSUPPORTED
;
216 while (Reloc
< RelocEnd
) {
217 Fixup
= FixupBase
+ (*Reloc
& 0xFFF);
218 switch ((*Reloc
) >> 12) {
219 case EFI_IMAGE_REL_BASED_ABSOLUTE
:
222 case EFI_IMAGE_REL_BASED_HIGH
:
223 Fixup16
= (UINT16
*) Fixup
;
224 *Fixup16
= (UINT16
) (*Fixup16
+ ((UINT16
) ((UINT32
) Adjust
>> 16)));
225 if (FixupData
!= NULL
) {
226 *(UINT16
*) FixupData
= *Fixup16
;
227 FixupData
= FixupData
+ sizeof (UINT16
);
231 case EFI_IMAGE_REL_BASED_LOW
:
232 Fixup16
= (UINT16
*) Fixup
;
233 *Fixup16
= (UINT16
) (*Fixup16
+ (UINT16
) Adjust
);
234 if (FixupData
!= NULL
) {
235 *(UINT16
*) FixupData
= *Fixup16
;
236 FixupData
= FixupData
+ sizeof (UINT16
);
240 case EFI_IMAGE_REL_BASED_HIGHLOW
:
241 Fixup32
= (UINT32
*) Fixup
;
242 *Fixup32
= *Fixup32
+ (UINT32
) Adjust
;
243 if (FixupData
!= NULL
) {
244 FixupData
= ALIGN_POINTER (FixupData
, sizeof (UINT32
));
245 *(UINT32
*)FixupData
= *Fixup32
;
246 FixupData
= FixupData
+ sizeof (UINT32
);
250 case EFI_IMAGE_REL_BASED_DIR64
:
251 Fixup64
= (UINT64
*) Fixup
;
252 *Fixup64
= *Fixup64
+ (UINT64
) Adjust
;
253 if (FixupData
!= NULL
) {
254 FixupData
= ALIGN_POINTER (FixupData
, sizeof(UINT64
));
255 *(UINT64
*)(FixupData
) = *Fixup64
;
256 FixupData
= FixupData
+ sizeof(UINT64
);
261 Print(L
"Unknown relocation\n");
262 return EFI_UNSUPPORTED
;
266 RelocBase
= (EFI_IMAGE_BASE_RELOCATION
*) RelocEnd
;
270 } /* relocate_coff() */
273 * Read the binary header and grab appropriate information from it
275 static EFI_STATUS
read_header(void *data
, unsigned int datasize
,
276 GNUEFI_PE_COFF_LOADER_IMAGE_CONTEXT
*context
)
278 EFI_IMAGE_DOS_HEADER
*DosHdr
= data
;
279 EFI_IMAGE_OPTIONAL_HEADER_UNION
*PEHdr
= data
;
281 if (datasize
< sizeof(EFI_IMAGE_DOS_HEADER
)) {
282 Print(L
"Invalid image\n");
283 return EFI_UNSUPPORTED
;
286 if (DosHdr
->e_magic
== EFI_IMAGE_DOS_SIGNATURE
)
287 PEHdr
= (EFI_IMAGE_OPTIONAL_HEADER_UNION
*)((char *)data
+ DosHdr
->e_lfanew
);
289 if ((((UINT8
*)PEHdr
- (UINT8
*)data
) + sizeof(EFI_IMAGE_OPTIONAL_HEADER_UNION
)) > datasize
) {
290 Print(L
"Invalid image\n");
291 return EFI_UNSUPPORTED
;
294 if (PEHdr
->Te
.Signature
!= EFI_IMAGE_NT_SIGNATURE
) {
295 Print(L
"Unsupported image type\n");
296 return EFI_UNSUPPORTED
;
299 if (PEHdr
->Pe32
.FileHeader
.Characteristics
& EFI_IMAGE_FILE_RELOCS_STRIPPED
) {
300 Print(L
"Unsupported image - Relocations have been stripped\n");
301 return EFI_UNSUPPORTED
;
304 if (PEHdr
->Pe32
.OptionalHeader
.Magic
!= EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC
) {
305 Print(L
"Only 64-bit images supported\n");
306 return EFI_UNSUPPORTED
;
309 context
->PEHdr
= PEHdr
;
310 context
->ImageAddress
= PEHdr
->Pe32Plus
.OptionalHeader
.ImageBase
;
311 context
->ImageSize
= (UINT64
)PEHdr
->Pe32Plus
.OptionalHeader
.SizeOfImage
;
312 context
->SizeOfHeaders
= PEHdr
->Pe32Plus
.OptionalHeader
.SizeOfHeaders
;
313 context
->EntryPoint
= PEHdr
->Pe32Plus
.OptionalHeader
.AddressOfEntryPoint
;
314 context
->RelocDir
= &PEHdr
->Pe32Plus
.OptionalHeader
.DataDirectory
[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC
];
315 context
->NumberOfRvaAndSizes
= PEHdr
->Pe32Plus
.OptionalHeader
.NumberOfRvaAndSizes
;
316 context
->NumberOfSections
= PEHdr
->Pe32
.FileHeader
.NumberOfSections
;
317 context
->FirstSection
= (EFI_IMAGE_SECTION_HEADER
*)((char *)PEHdr
+ PEHdr
->Pe32
.FileHeader
.SizeOfOptionalHeader
+ sizeof(UINT32
) + sizeof(EFI_IMAGE_FILE_HEADER
));
318 context
->SecDir
= (EFI_IMAGE_DATA_DIRECTORY
*) &PEHdr
->Pe32Plus
.OptionalHeader
.DataDirectory
[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY
];
320 if (context
->ImageSize
< context
->SizeOfHeaders
) {
321 Print(L
"Invalid image\n");
322 return EFI_UNSUPPORTED
;
325 if (((UINT8
*)context
->SecDir
- (UINT8
*)data
) > (datasize
- sizeof(EFI_IMAGE_DATA_DIRECTORY
))) {
326 Print(L
"Invalid image\n");
327 return EFI_UNSUPPORTED
;
330 if (context
->SecDir
->VirtualAddress
>= datasize
) {
331 Print(L
"Malformed security header\n");
332 return EFI_INVALID_PARAMETER
;
337 // The following is based on the grub_linuxefi_secure_validate() function in Fedora's
338 // version of GRUB 2.
339 // Returns TRUE if the specified data is validated by Shim's MOK, FALSE otherwise
340 static BOOLEAN
ShimValidate (VOID
*data
, UINT32 size
)
342 SHIM_LOCK
*shim_lock
;
343 EFI_GUID ShimLockGuid
= SHIM_LOCK_GUID
;
345 if (BS
->LocateProtocol(&ShimLockGuid
, NULL
, (VOID
**) &shim_lock
) == EFI_SUCCESS
) {
349 if (shim_lock
->shim_verify(data
, size
) == EFI_SUCCESS
)
354 } // BOOLEAN ShimValidate()
357 * Once the image has been loaded it needs to be validated and relocated
359 static EFI_STATUS
handle_image (void *data
, unsigned int datasize
, EFI_LOADED_IMAGE
*li
,
360 CHAR16
*Options
, REFIT_VOLUME
*DeviceVolume
, IN CHAR16
*FileName
)
362 EFI_STATUS efi_status
;
365 EFI_IMAGE_SECTION_HEADER
*Section
;
367 GNUEFI_PE_COFF_LOADER_IMAGE_CONTEXT context
;
368 EFI_DEVICE_PATH
*DevicePath
;
371 * The binary header contains relevant context and section pointers
373 efi_status
= read_header(data
, datasize
, &context
);
374 if (efi_status
!= EFI_SUCCESS
) {
375 Print(L
"Failed to read header\n");
380 * Validate the image; if this fails, return EFI_ACCESS_DENIED
382 if (!ShimValidate(data
, datasize
)) {
383 return EFI_ACCESS_DENIED
;
386 buffer
= AllocatePool(context
.ImageSize
);
389 Print(L
"Failed to allocate image buffer\n");
390 return EFI_OUT_OF_RESOURCES
;
393 CopyMem(buffer
, data
, context
.SizeOfHeaders
);
396 * Copy the executable's sections to their desired offsets
398 Section
= context
.FirstSection
;
399 for (i
= 0; i
< context
.NumberOfSections
; i
++) {
400 size
= Section
->Misc
.VirtualSize
;
402 if (size
> Section
->SizeOfRawData
)
403 size
= Section
->SizeOfRawData
;
405 base
= ImageAddress (buffer
, context
.ImageSize
, Section
->VirtualAddress
);
406 end
= ImageAddress (buffer
, context
.ImageSize
, Section
->VirtualAddress
+ size
- 1);
409 Print(L
"Invalid section size\n");
410 return EFI_UNSUPPORTED
;
413 if (Section
->SizeOfRawData
> 0)
414 CopyMem(base
, data
+ Section
->PointerToRawData
, size
);
416 if (size
< Section
->Misc
.VirtualSize
)
417 ZeroMem (base
+ size
, Section
->Misc
.VirtualSize
- size
);
423 * Run the relocation fixups
425 efi_status
= relocate_coff(&context
, buffer
);
427 if (efi_status
!= EFI_SUCCESS
) {
428 Print(L
"Relocation failed\n");
433 entry_point
= ImageAddress(buffer
, context
.ImageSize
, context
.EntryPoint
);
435 * grub needs to know its location and size in memory, its location on
436 * disk, and its load options, so fix up the loaded image protocol values
438 DevicePath
= FileDevicePath(NULL
, FileName
);
439 // DevicePath = FileDevicePath(DeviceVolume->DeviceHandle, FileName);
440 li
->DeviceHandle
= DeviceVolume
->DeviceHandle
;
441 li
->FilePath
= DevicePath
;
442 li
->LoadOptionsSize
= ((UINT32
)StrLen(Options
) + 1) * sizeof(CHAR16
);
443 li
->LoadOptions
= (VOID
*)Options
;
444 li
->ImageBase
= buffer
;
445 li
->ImageSize
= context
.ImageSize
;
448 Print(L
"Invalid entry point\n");
450 return EFI_UNSUPPORTED
;
456 #endif /* defined(EFIX64) */
459 * Load and run an EFI executable.
460 * Note that most of this function compiles only on x86-64 (X64) systems, since
461 * shim/MOK works only on those systems. I'm leaving just enough to get the
462 * function to return EFI_ACCESS_DENIED on x86 (IA32) systems, which should
463 * let the calling function work in case it somehow ends up calling this
464 * function inappropriately.
466 EFI_STATUS
start_image(EFI_HANDLE image_handle
, CHAR16
*ImagePath
, VOID
*data
, UINTN datasize
,
467 CHAR16
*Options
, REFIT_VOLUME
*DeviceVolume
, IN EFI_DEVICE_PATH
*DevicePath
)
469 EFI_STATUS efi_status
= EFI_ACCESS_DENIED
;
471 EFI_GUID loaded_image_protocol
= LOADED_IMAGE_PROTOCOL
;
472 EFI_LOADED_IMAGE
*li
, li_bak
;
473 CHAR16
*PathName
= NULL
;
476 return EFI_LOAD_ERROR
;
479 * We need to refer to the loaded image protocol on the running
480 * binary in order to find our path
482 efi_status
= uefi_call_wrapper(BS
->HandleProtocol
, 3, image_handle
, &loaded_image_protocol
, (void **)&li
);
484 if (efi_status
!= EFI_SUCCESS
) {
485 Print(L
"Unable to init protocol\n");
490 * We need to modify the loaded image protocol entry before running
491 * the new binary, so back it up
493 CopyMem(&li_bak
, li
, sizeof(li_bak
));
496 * Verify and, if appropriate, relocate and execute the executable
498 efi_status
= handle_image(data
, datasize
, li
, Options
, DeviceVolume
, ImagePath
);
500 if (efi_status
!= EFI_SUCCESS
) {
501 Print(L
"Failed to load image\n");
502 CopyMem(li
, &li_bak
, sizeof(li_bak
));
507 * The binary is trusted and relocated. Run it
509 efi_status
= refit_call2_wrapper(entry_point
, image_handle
, ST
);
511 // efi_status = refit_call1_wrapper(BS->UnloadImage, li);
514 * Restore our original loaded image values
516 CopyMem(li
, &li_bak
, sizeof(li_bak
));
526 } // EFI_STATUS start_image()