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 * Perform basic bounds checking of the intra-image pointers
85 static void *ImageAddress (void *image
, int size
, unsigned int address
)
90 return image
+ address
;
94 * Perform the actual relocation
96 static EFI_STATUS
relocate_coff (GNUEFI_PE_COFF_LOADER_IMAGE_CONTEXT
*context
,
99 EFI_IMAGE_BASE_RELOCATION
*RelocBase
, *RelocBaseEnd
;
101 UINT16
*Reloc
, *RelocEnd
;
102 char *Fixup
, *FixupBase
, *FixupData
= NULL
;
106 int size
= context
->ImageSize
;
107 void *ImageEnd
= (char *)data
+ size
;
109 context
->PEHdr
->Pe32Plus
.OptionalHeader
.ImageBase
= (UINT64
)data
;
111 if (context
->NumberOfRvaAndSizes
<= EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC
) {
112 Print(L
"Image has no relocation entry\n");
113 return EFI_UNSUPPORTED
;
116 // Linux kernels with EFI stub support don't have relocation information, so
117 // we can skip all this stuff....
118 if (((context
->RelocDir
->VirtualAddress
== 0) && (context
->RelocDir
->Size
== 0)) ||
119 ((context
->PEHdr
->Pe32
.FileHeader
.Characteristics
& EFI_IMAGE_FILE_RELOCS_STRIPPED
) != 0)) {
123 RelocBase
= ImageAddress(data
, size
, context
->RelocDir
->VirtualAddress
);
124 RelocBaseEnd
= ImageAddress(data
, size
, context
->RelocDir
->VirtualAddress
+ context
->RelocDir
->Size
- 1);
126 if (!RelocBase
|| !RelocBaseEnd
) {
127 Print(L
"Reloc table overflows binary\n");
128 return EFI_UNSUPPORTED
;
131 Adjust
= (UINT64
)data
- context
->ImageAddress
;
133 while (RelocBase
< RelocBaseEnd
) {
134 Reloc
= (UINT16
*) ((char *) RelocBase
+ sizeof (EFI_IMAGE_BASE_RELOCATION
));
135 RelocEnd
= (UINT16
*) ((char *) RelocBase
+ RelocBase
->SizeOfBlock
);
137 if ((void *)RelocEnd
< data
|| (void *)RelocEnd
> ImageEnd
) {
138 Print(L
"Reloc entry overflows binary\n");
139 return EFI_UNSUPPORTED
;
142 FixupBase
= ImageAddress(data
, size
, RelocBase
->VirtualAddress
);
144 Print(L
"Invalid fixupbase\n");
145 return EFI_UNSUPPORTED
;
148 while (Reloc
< RelocEnd
) {
149 Fixup
= FixupBase
+ (*Reloc
& 0xFFF);
150 switch ((*Reloc
) >> 12) {
151 case EFI_IMAGE_REL_BASED_ABSOLUTE
:
154 case EFI_IMAGE_REL_BASED_HIGH
:
155 Fixup16
= (UINT16
*) Fixup
;
156 *Fixup16
= (UINT16
) (*Fixup16
+ ((UINT16
) ((UINT32
) Adjust
>> 16)));
157 if (FixupData
!= NULL
) {
158 *(UINT16
*) FixupData
= *Fixup16
;
159 FixupData
= FixupData
+ sizeof (UINT16
);
163 case EFI_IMAGE_REL_BASED_LOW
:
164 Fixup16
= (UINT16
*) Fixup
;
165 *Fixup16
= (UINT16
) (*Fixup16
+ (UINT16
) Adjust
);
166 if (FixupData
!= NULL
) {
167 *(UINT16
*) FixupData
= *Fixup16
;
168 FixupData
= FixupData
+ sizeof (UINT16
);
172 case EFI_IMAGE_REL_BASED_HIGHLOW
:
173 Fixup32
= (UINT32
*) Fixup
;
174 *Fixup32
= *Fixup32
+ (UINT32
) Adjust
;
175 if (FixupData
!= NULL
) {
176 FixupData
= ALIGN_POINTER (FixupData
, sizeof (UINT32
));
177 *(UINT32
*)FixupData
= *Fixup32
;
178 FixupData
= FixupData
+ sizeof (UINT32
);
182 case EFI_IMAGE_REL_BASED_DIR64
:
183 Fixup64
= (UINT64
*) Fixup
;
184 *Fixup64
= *Fixup64
+ (UINT64
) Adjust
;
185 if (FixupData
!= NULL
) {
186 FixupData
= ALIGN_POINTER (FixupData
, sizeof(UINT64
));
187 *(UINT64
*)(FixupData
) = *Fixup64
;
188 FixupData
= FixupData
+ sizeof(UINT64
);
193 Print(L
"Unknown relocation\n");
194 return EFI_UNSUPPORTED
;
198 RelocBase
= (EFI_IMAGE_BASE_RELOCATION
*) RelocEnd
;
205 * Read the binary header and grab appropriate information from it
207 static EFI_STATUS
read_header(void *data
, unsigned int datasize
,
208 GNUEFI_PE_COFF_LOADER_IMAGE_CONTEXT
*context
)
210 EFI_IMAGE_DOS_HEADER
*DosHdr
= data
;
211 EFI_IMAGE_OPTIONAL_HEADER_UNION
*PEHdr
= data
;
213 if (datasize
< sizeof(EFI_IMAGE_DOS_HEADER
)) {
214 Print(L
"Invalid image\n");
215 return EFI_UNSUPPORTED
;
218 if (DosHdr
->e_magic
== EFI_IMAGE_DOS_SIGNATURE
)
219 PEHdr
= (EFI_IMAGE_OPTIONAL_HEADER_UNION
*)((char *)data
+ DosHdr
->e_lfanew
);
221 if ((((UINT8
*)PEHdr
- (UINT8
*)data
) + sizeof(EFI_IMAGE_OPTIONAL_HEADER_UNION
)) > datasize
) {
222 Print(L
"Invalid image\n");
223 return EFI_UNSUPPORTED
;
226 if (PEHdr
->Te
.Signature
!= EFI_IMAGE_NT_SIGNATURE
) {
227 Print(L
"Unsupported image type\n");
228 return EFI_UNSUPPORTED
;
231 if (PEHdr
->Pe32
.FileHeader
.Characteristics
& EFI_IMAGE_FILE_RELOCS_STRIPPED
) {
232 Print(L
"Unsupported image - Relocations have been stripped\n");
233 return EFI_UNSUPPORTED
;
236 if (PEHdr
->Pe32
.OptionalHeader
.Magic
!= EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC
) {
237 Print(L
"Only 64-bit images supported\n");
238 return EFI_UNSUPPORTED
;
241 context
->PEHdr
= PEHdr
;
242 context
->ImageAddress
= PEHdr
->Pe32Plus
.OptionalHeader
.ImageBase
;
243 context
->ImageSize
= (UINT64
)PEHdr
->Pe32Plus
.OptionalHeader
.SizeOfImage
;
244 context
->SizeOfHeaders
= PEHdr
->Pe32Plus
.OptionalHeader
.SizeOfHeaders
;
245 context
->EntryPoint
= PEHdr
->Pe32Plus
.OptionalHeader
.AddressOfEntryPoint
;
246 context
->RelocDir
= &PEHdr
->Pe32Plus
.OptionalHeader
.DataDirectory
[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC
];
247 context
->NumberOfRvaAndSizes
= PEHdr
->Pe32Plus
.OptionalHeader
.NumberOfRvaAndSizes
;
248 context
->NumberOfSections
= PEHdr
->Pe32
.FileHeader
.NumberOfSections
;
249 context
->FirstSection
= (EFI_IMAGE_SECTION_HEADER
*)((char *)PEHdr
+ PEHdr
->Pe32
.FileHeader
.SizeOfOptionalHeader
+ sizeof(UINT32
) + sizeof(EFI_IMAGE_FILE_HEADER
));
250 context
->SecDir
= (EFI_IMAGE_DATA_DIRECTORY
*) &PEHdr
->Pe32Plus
.OptionalHeader
.DataDirectory
[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY
];
252 if (context
->ImageSize
< context
->SizeOfHeaders
) {
253 Print(L
"Invalid image\n");
254 return EFI_UNSUPPORTED
;
257 if (((UINT8
*)context
->SecDir
- (UINT8
*)data
) > (datasize
- sizeof(EFI_IMAGE_DATA_DIRECTORY
))) {
258 Print(L
"Invalid image\n");
259 return EFI_UNSUPPORTED
;
262 if (context
->SecDir
->VirtualAddress
>= datasize
) {
263 Print(L
"Malformed security header\n");
264 return EFI_INVALID_PARAMETER
;
269 // The following is based on the grub_linuxefi_secure_validate() function in Fedora's
270 // version of GRUB 2.
271 // Returns TRUE if the specified data is validated by Shim's MOK, FALSE otherwise
272 static BOOLEAN
ShimValidate (VOID
*data
, UINT32 size
)
274 EFI_GUID ShimLockGuid
= SHIM_LOCK_GUID
;
275 SHIM_LOCK
*shim_lock
;
277 if (BS
->LocateProtocol(&ShimLockGuid
, NULL
, (VOID
**) &shim_lock
) == EFI_SUCCESS
) {
281 if (shim_lock
->shim_verify(data
, size
) == EFI_SUCCESS
)
286 } // BOOLEAN ShimValidate()
289 * Once the image has been loaded it needs to be validated and relocated
291 static EFI_STATUS
handle_image (void *data
, unsigned int datasize
,
292 EFI_LOADED_IMAGE
*li
, CHAR16
*Options
, REFIT_VOLUME
*DeviceVolume
)
294 EFI_STATUS efi_status
;
297 EFI_IMAGE_SECTION_HEADER
*Section
;
299 GNUEFI_PE_COFF_LOADER_IMAGE_CONTEXT context
;
302 * The binary header contains relevant context and section pointers
304 efi_status
= read_header(data
, datasize
, &context
);
305 if (efi_status
!= EFI_SUCCESS
) {
306 Print(L
"Failed to read header\n");
311 * Validate the image; if this fails, return EFI_ACCESS_DENIED
313 if (!ShimValidate(data
, datasize
)) {
314 return EFI_ACCESS_DENIED
;
317 buffer
= AllocatePool(context
.ImageSize
);
320 Print(L
"Failed to allocate image buffer\n");
321 return EFI_OUT_OF_RESOURCES
;
324 CopyMem(buffer
, data
, context
.SizeOfHeaders
);
327 * Copy the executable's sections to their desired offsets
329 Section
= context
.FirstSection
;
330 for (i
= 0; i
< context
.NumberOfSections
; i
++) {
331 size
= Section
->Misc
.VirtualSize
;
333 if (size
> Section
->SizeOfRawData
)
334 size
= Section
->SizeOfRawData
;
336 base
= ImageAddress (buffer
, context
.ImageSize
, Section
->VirtualAddress
);
337 end
= ImageAddress (buffer
, context
.ImageSize
, Section
->VirtualAddress
+ size
- 1);
340 Print(L
"Invalid section size\n");
341 return EFI_UNSUPPORTED
;
344 if (Section
->SizeOfRawData
> 0)
345 CopyMem(base
, data
+ Section
->PointerToRawData
, size
);
347 if (size
< Section
->Misc
.VirtualSize
)
348 ZeroMem (base
+ size
, Section
->Misc
.VirtualSize
- size
);
354 * Run the relocation fixups
356 efi_status
= relocate_coff(&context
, buffer
);
358 if (efi_status
!= EFI_SUCCESS
) {
359 Print(L
"Relocation failed\n");
364 entry_point
= ImageAddress(buffer
, context
.ImageSize
, context
.EntryPoint
);
366 * grub needs to know its location and size in memory, its location on
367 * disk, and its load options, so fix up the loaded image protocol values
369 li
->ImageBase
= buffer
;
370 li
->ImageSize
= context
.ImageSize
;
371 li
->DeviceHandle
= DeviceVolume
->DeviceHandle
;
372 li
->FilePath
= DeviceVolume
->DevicePath
;
373 li
->LoadOptions
= (VOID
*)Options
;
374 li
->LoadOptionsSize
= ((UINT32
)StrLen(Options
) + 1) * sizeof(CHAR16
);
377 Print(L
"Invalid entry point\n");
379 return EFI_UNSUPPORTED
;
386 * Load and run an EFI executable
388 EFI_STATUS
start_image(EFI_HANDLE image_handle
, CHAR16
*ImagePath
, VOID
*data
, UINTN datasize
,
389 CHAR16
*Options
, REFIT_VOLUME
*DeviceVolume
)
391 EFI_GUID loaded_image_protocol
= LOADED_IMAGE_PROTOCOL
;
392 EFI_STATUS efi_status
;
393 EFI_LOADED_IMAGE
*li
, li_bak
;
394 CHAR16
*PathName
= NULL
;
397 return EFI_LOAD_ERROR
;
400 * We need to refer to the loaded image protocol on the running
401 * binary in order to find our path
403 efi_status
= uefi_call_wrapper(BS
->HandleProtocol
, 3, image_handle
,
404 &loaded_image_protocol
, (void **)&li
);
406 if (efi_status
!= EFI_SUCCESS
) {
407 Print(L
"Unable to init protocol\n");
412 * We need to modify the loaded image protocol entry before running
413 * the new binary, so back it up
415 CopyMem(&li_bak
, li
, sizeof(li_bak
));
418 * Verify and, if appropriate, relocate and execute the executable
420 efi_status
= handle_image(data
, datasize
, li
, Options
, DeviceVolume
);
422 if (efi_status
!= EFI_SUCCESS
) {
423 Print(L
"Failed to load image\n");
424 CopyMem(li
, &li_bak
, sizeof(li_bak
));
429 * The binary is trusted and relocated. Run it
431 efi_status
= refit_call2_wrapper(entry_point
, image_handle
, ST
);
434 * Restore our original loaded image values
436 CopyMem(li
, &li_bak
, sizeof(li_bak
));