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) {
134 * Currently, shim/MOK only works on x86-64 (X64) systems, and some of this code
135 * generates warnings on x86 (IA32) builds, so don't bother compiling it at all
143 * Perform basic bounds checking of the intra-image pointers
145 static void *ImageAddress (void *image
, int size
, unsigned int address
)
150 return image
+ address
;
154 * Perform the actual relocation
156 static EFI_STATUS
relocate_coff (GNUEFI_PE_COFF_LOADER_IMAGE_CONTEXT
*context
, void *data
)
158 EFI_IMAGE_BASE_RELOCATION
*RelocBase
, *RelocBaseEnd
;
160 UINT16
*Reloc
, *RelocEnd
;
161 char *Fixup
, *FixupBase
, *FixupData
= NULL
;
165 int size
= context
->ImageSize
;
166 void *ImageEnd
= (char *)data
+ size
;
168 context
->PEHdr
->Pe32Plus
.OptionalHeader
.ImageBase
= (UINT64
)data
;
170 // Linux kernels with EFI stub support don't have relocation information, so
171 // we can skip all this stuff....
172 if (((context
->RelocDir
->VirtualAddress
== 0) && (context
->RelocDir
->Size
== 0)) ||
173 ((context
->PEHdr
->Pe32
.FileHeader
.Characteristics
& EFI_IMAGE_FILE_RELOCS_STRIPPED
) != 0)) {
177 if (context
->NumberOfRvaAndSizes
<= EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC
) {
178 Print(L
"Image has no relocation entry\n");
179 return EFI_UNSUPPORTED
;
182 RelocBase
= ImageAddress(data
, size
, context
->RelocDir
->VirtualAddress
);
183 RelocBaseEnd
= ImageAddress(data
, size
, context
->RelocDir
->VirtualAddress
+ context
->RelocDir
->Size
- 1);
185 if (!RelocBase
|| !RelocBaseEnd
) {
186 Print(L
"Reloc table overflows binary\n");
187 return EFI_UNSUPPORTED
;
190 Adjust
= (UINT64
)data
- context
->ImageAddress
;
192 while (RelocBase
< RelocBaseEnd
) {
193 Reloc
= (UINT16
*) ((char *) RelocBase
+ sizeof (EFI_IMAGE_BASE_RELOCATION
));
194 RelocEnd
= (UINT16
*) ((char *) RelocBase
+ RelocBase
->SizeOfBlock
);
196 if ((void *)RelocEnd
< data
|| (void *)RelocEnd
> ImageEnd
) {
197 Print(L
"Reloc entry overflows binary\n");
198 return EFI_UNSUPPORTED
;
201 FixupBase
= ImageAddress(data
, size
, RelocBase
->VirtualAddress
);
203 Print(L
"Invalid fixupbase\n");
204 return EFI_UNSUPPORTED
;
207 while (Reloc
< RelocEnd
) {
208 Fixup
= FixupBase
+ (*Reloc
& 0xFFF);
209 switch ((*Reloc
) >> 12) {
210 case EFI_IMAGE_REL_BASED_ABSOLUTE
:
213 case EFI_IMAGE_REL_BASED_HIGH
:
214 Fixup16
= (UINT16
*) Fixup
;
215 *Fixup16
= (UINT16
) (*Fixup16
+ ((UINT16
) ((UINT32
) Adjust
>> 16)));
216 if (FixupData
!= NULL
) {
217 *(UINT16
*) FixupData
= *Fixup16
;
218 FixupData
= FixupData
+ sizeof (UINT16
);
222 case EFI_IMAGE_REL_BASED_LOW
:
223 Fixup16
= (UINT16
*) Fixup
;
224 *Fixup16
= (UINT16
) (*Fixup16
+ (UINT16
) Adjust
);
225 if (FixupData
!= NULL
) {
226 *(UINT16
*) FixupData
= *Fixup16
;
227 FixupData
= FixupData
+ sizeof (UINT16
);
231 case EFI_IMAGE_REL_BASED_HIGHLOW
:
232 Fixup32
= (UINT32
*) Fixup
;
233 *Fixup32
= *Fixup32
+ (UINT32
) Adjust
;
234 if (FixupData
!= NULL
) {
235 FixupData
= ALIGN_POINTER (FixupData
, sizeof (UINT32
));
236 *(UINT32
*)FixupData
= *Fixup32
;
237 FixupData
= FixupData
+ sizeof (UINT32
);
241 case EFI_IMAGE_REL_BASED_DIR64
:
242 Fixup64
= (UINT64
*) Fixup
;
243 *Fixup64
= *Fixup64
+ (UINT64
) Adjust
;
244 if (FixupData
!= NULL
) {
245 FixupData
= ALIGN_POINTER (FixupData
, sizeof(UINT64
));
246 *(UINT64
*)(FixupData
) = *Fixup64
;
247 FixupData
= FixupData
+ sizeof(UINT64
);
252 Print(L
"Unknown relocation\n");
253 return EFI_UNSUPPORTED
;
257 RelocBase
= (EFI_IMAGE_BASE_RELOCATION
*) RelocEnd
;
261 } /* relocate_coff() */
264 * Read the binary header and grab appropriate information from it
266 static EFI_STATUS
read_header(void *data
, unsigned int datasize
,
267 GNUEFI_PE_COFF_LOADER_IMAGE_CONTEXT
*context
)
269 EFI_IMAGE_DOS_HEADER
*DosHdr
= data
;
270 EFI_IMAGE_OPTIONAL_HEADER_UNION
*PEHdr
= data
;
272 if (datasize
< sizeof(EFI_IMAGE_DOS_HEADER
)) {
273 Print(L
"Invalid image\n");
274 return EFI_UNSUPPORTED
;
277 if (DosHdr
->e_magic
== EFI_IMAGE_DOS_SIGNATURE
)
278 PEHdr
= (EFI_IMAGE_OPTIONAL_HEADER_UNION
*)((char *)data
+ DosHdr
->e_lfanew
);
280 if ((((UINT8
*)PEHdr
- (UINT8
*)data
) + sizeof(EFI_IMAGE_OPTIONAL_HEADER_UNION
)) > datasize
) {
281 Print(L
"Invalid image\n");
282 return EFI_UNSUPPORTED
;
285 if (PEHdr
->Te
.Signature
!= EFI_IMAGE_NT_SIGNATURE
) {
286 Print(L
"Unsupported image type\n");
287 return EFI_UNSUPPORTED
;
290 if (PEHdr
->Pe32
.FileHeader
.Characteristics
& EFI_IMAGE_FILE_RELOCS_STRIPPED
) {
291 Print(L
"Unsupported image - Relocations have been stripped\n");
292 return EFI_UNSUPPORTED
;
295 if (PEHdr
->Pe32
.OptionalHeader
.Magic
!= EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC
) {
296 Print(L
"Only 64-bit images supported\n");
297 return EFI_UNSUPPORTED
;
300 context
->PEHdr
= PEHdr
;
301 context
->ImageAddress
= PEHdr
->Pe32Plus
.OptionalHeader
.ImageBase
;
302 context
->ImageSize
= (UINT64
)PEHdr
->Pe32Plus
.OptionalHeader
.SizeOfImage
;
303 context
->SizeOfHeaders
= PEHdr
->Pe32Plus
.OptionalHeader
.SizeOfHeaders
;
304 context
->EntryPoint
= PEHdr
->Pe32Plus
.OptionalHeader
.AddressOfEntryPoint
;
305 context
->RelocDir
= &PEHdr
->Pe32Plus
.OptionalHeader
.DataDirectory
[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC
];
306 context
->NumberOfRvaAndSizes
= PEHdr
->Pe32Plus
.OptionalHeader
.NumberOfRvaAndSizes
;
307 context
->NumberOfSections
= PEHdr
->Pe32
.FileHeader
.NumberOfSections
;
308 context
->FirstSection
= (EFI_IMAGE_SECTION_HEADER
*)((char *)PEHdr
+ PEHdr
->Pe32
.FileHeader
.SizeOfOptionalHeader
+ sizeof(UINT32
) + sizeof(EFI_IMAGE_FILE_HEADER
));
309 context
->SecDir
= (EFI_IMAGE_DATA_DIRECTORY
*) &PEHdr
->Pe32Plus
.OptionalHeader
.DataDirectory
[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY
];
311 if (context
->ImageSize
< context
->SizeOfHeaders
) {
312 Print(L
"Invalid image\n");
313 return EFI_UNSUPPORTED
;
316 if (((UINT8
*)context
->SecDir
- (UINT8
*)data
) > (datasize
- sizeof(EFI_IMAGE_DATA_DIRECTORY
))) {
317 Print(L
"Invalid image\n");
318 return EFI_UNSUPPORTED
;
321 if (context
->SecDir
->VirtualAddress
>= datasize
) {
322 Print(L
"Malformed security header\n");
323 return EFI_INVALID_PARAMETER
;
328 // The following is based on the grub_linuxefi_secure_validate() function in Fedora's
329 // version of GRUB 2.
330 // Returns TRUE if the specified data is validated by Shim's MOK, FALSE otherwise
331 static BOOLEAN
ShimValidate (VOID
*data
, UINT32 size
)
333 EFI_GUID ShimLockGuid
= SHIM_LOCK_GUID
;
334 SHIM_LOCK
*shim_lock
;
336 if (BS
->LocateProtocol(&ShimLockGuid
, NULL
, (VOID
**) &shim_lock
) == EFI_SUCCESS
) {
340 if (shim_lock
->shim_verify(data
, size
) == EFI_SUCCESS
)
345 } // BOOLEAN ShimValidate()
348 * Once the image has been loaded it needs to be validated and relocated
350 static EFI_STATUS
handle_image (void *data
, unsigned int datasize
, EFI_LOADED_IMAGE
*li
,
351 CHAR16
*Options
, REFIT_VOLUME
*DeviceVolume
, IN EFI_DEVICE_PATH
*DevicePath
)
353 EFI_STATUS efi_status
;
356 EFI_IMAGE_SECTION_HEADER
*Section
;
358 GNUEFI_PE_COFF_LOADER_IMAGE_CONTEXT context
;
361 * The binary header contains relevant context and section pointers
363 efi_status
= read_header(data
, datasize
, &context
);
364 if (efi_status
!= EFI_SUCCESS
) {
365 Print(L
"Failed to read header\n");
370 * Validate the image; if this fails, return EFI_ACCESS_DENIED
372 if (!ShimValidate(data
, datasize
)) {
373 return EFI_ACCESS_DENIED
;
376 buffer
= AllocatePool(context
.ImageSize
);
379 Print(L
"Failed to allocate image buffer\n");
380 return EFI_OUT_OF_RESOURCES
;
383 CopyMem(buffer
, data
, context
.SizeOfHeaders
);
386 * Copy the executable's sections to their desired offsets
388 Section
= context
.FirstSection
;
389 for (i
= 0; i
< context
.NumberOfSections
; i
++) {
390 size
= Section
->Misc
.VirtualSize
;
392 if (size
> Section
->SizeOfRawData
)
393 size
= Section
->SizeOfRawData
;
395 base
= ImageAddress (buffer
, context
.ImageSize
, Section
->VirtualAddress
);
396 end
= ImageAddress (buffer
, context
.ImageSize
, Section
->VirtualAddress
+ size
- 1);
399 Print(L
"Invalid section size\n");
400 return EFI_UNSUPPORTED
;
403 if (Section
->SizeOfRawData
> 0)
404 CopyMem(base
, data
+ Section
->PointerToRawData
, size
);
406 if (size
< Section
->Misc
.VirtualSize
)
407 ZeroMem (base
+ size
, Section
->Misc
.VirtualSize
- size
);
413 * Run the relocation fixups
415 efi_status
= relocate_coff(&context
, buffer
);
417 if (efi_status
!= EFI_SUCCESS
) {
418 Print(L
"Relocation failed\n");
423 entry_point
= ImageAddress(buffer
, context
.ImageSize
, context
.EntryPoint
);
425 * grub needs to know its location and size in memory, its location on
426 * disk, and its load options, so fix up the loaded image protocol values
428 li
->DeviceHandle
= DeviceVolume
->DeviceHandle
;
429 li
->FilePath
= DevicePath
;
430 li
->LoadOptionsSize
= ((UINT32
)StrLen(Options
) + 1) * sizeof(CHAR16
);
431 li
->LoadOptions
= (VOID
*)Options
;
432 li
->ImageBase
= buffer
;
433 li
->ImageSize
= context
.ImageSize
;
436 Print(L
"Invalid entry point\n");
438 return EFI_UNSUPPORTED
;
444 #endif /* defined(EFIX64) */
447 * Load and run an EFI executable.
448 * Note that most of this function compiles only on x86-64 (X64) systems, since
449 * shim/MOK works only on those systems. I'm leaving just enough to get the
450 * function to return EFI_ACCESS_DENIED on x86 (IA32) systems, which should
451 * let the calling function work in case it somehow ends up calling this
452 * function inappropriately.
454 EFI_STATUS
start_image(EFI_HANDLE image_handle
, CHAR16
*ImagePath
, VOID
*data
, UINTN datasize
,
455 CHAR16
*Options
, REFIT_VOLUME
*DeviceVolume
, IN EFI_DEVICE_PATH
*DevicePath
)
457 EFI_STATUS efi_status
= EFI_ACCESS_DENIED
;
459 EFI_GUID loaded_image_protocol
= LOADED_IMAGE_PROTOCOL
;
460 EFI_LOADED_IMAGE
*li
, li_bak
;
461 CHAR16
*PathName
= NULL
;
464 return EFI_LOAD_ERROR
;
467 * We need to refer to the loaded image protocol on the running
468 * binary in order to find our path
470 efi_status
= uefi_call_wrapper(BS
->HandleProtocol
, 3, image_handle
, &loaded_image_protocol
, (void **)&li
);
472 if (efi_status
!= EFI_SUCCESS
) {
473 Print(L
"Unable to init protocol\n");
478 * We need to modify the loaded image protocol entry before running
479 * the new binary, so back it up
481 CopyMem(&li_bak
, li
, sizeof(li_bak
));
484 * Verify and, if appropriate, relocate and execute the executable
486 efi_status
= handle_image(data
, datasize
, li
, Options
, DeviceVolume
, DevicePath
);
488 if (efi_status
!= EFI_SUCCESS
) {
489 Print(L
"Failed to load image\n");
490 CopyMem(li
, &li_bak
, sizeof(li_bak
));
495 * The binary is trusted and relocated. Run it
497 efi_status
= refit_call2_wrapper(entry_point
, image_handle
, ST
);
499 // efi_status = refit_call1_wrapper(BS->UnloadImage, li);
502 * Restore our original loaded image values
504 CopyMem(li
, &li_bak
, sizeof(li_bak
));
514 } // EFI_STATUS start_image()