]> code.delx.au - refind/blob - refind/mok.c
New files to support Secure Boot & MOK verification of binaries.
[refind] / refind / mok.c
1 /* refind/mok.c
2 *
3 * Based mostly on shim.c by Matthew J. Garrett/Red Hat (see below
4 * copyright notice).
5 *
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.
9 *
10 */
11
12 /*
13 * shim - trivial UEFI first-stage bootloader
14 *
15 * Copyright 2012 Red Hat, Inc <mjg@redhat.com>
16 *
17 * Redistribution and use in source and binary forms, with or without
18 * modification, are permitted provided that the following conditions
19 * are met:
20 *
21 * Redistributions of source code must retain the above copyright
22 * notice, this list of conditions and the following disclaimer.
23 *
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
27 * distribution.
28 *
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.
41 *
42 * Significant portions of this code are derived from Tianocore
43 * (http://tianocore.sf.net) and are Copyright 2009-2012 Intel
44 * Corporation.
45 */
46
47 #include "global.h"
48 #include "mok.h"
49 #include "lib.h"
50 #include "config.h"
51 #include "screen.h"
52 #include "../include/refit_call_wrapper.h"
53 #include "../include/PeImage.h"
54
55 #define SECOND_STAGE L"\\refind.efi"
56 #define MOK_MANAGER L"\\MokManager.efi"
57
58 static EFI_STATUS (EFIAPI *entry_point) (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table);
59
60 /*
61 * The vendor certificate used for validating the second stage loader
62 */
63 extern UINT8 vendor_cert[];
64 extern UINT32 vendor_cert_size;
65 extern UINT32 vendor_dbx_size;
66
67 #define EFI_IMAGE_SECURITY_DATABASE_GUID { 0xd719b2cb, 0x3d3a, 0x4596, { 0xa3, 0xbc, 0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f }}
68
69 //static UINT8 insecure_mode;
70
71 typedef enum {
72 DATA_FOUND,
73 DATA_NOT_FOUND,
74 VAR_NOT_FOUND
75 } CHECK_STATUS;
76
77 typedef struct {
78 UINT32 MokSize;
79 UINT8 *Mok;
80 } MokListNode;
81
82 /*
83 * Perform basic bounds checking of the intra-image pointers
84 */
85 static void *ImageAddress (void *image, int size, unsigned int address)
86 {
87 if (address > size)
88 return NULL;
89
90 return image + address;
91 }
92
93 /*
94 * Perform the actual relocation
95 */
96 static EFI_STATUS relocate_coff (GNUEFI_PE_COFF_LOADER_IMAGE_CONTEXT *context,
97 void *data)
98 {
99 EFI_IMAGE_BASE_RELOCATION *RelocBase, *RelocBaseEnd;
100 UINT64 Adjust;
101 UINT16 *Reloc, *RelocEnd;
102 char *Fixup, *FixupBase, *FixupData = NULL;
103 UINT16 *Fixup16;
104 UINT32 *Fixup32;
105 UINT64 *Fixup64;
106 int size = context->ImageSize;
107 void *ImageEnd = (char *)data + size;
108
109 context->PEHdr->Pe32Plus.OptionalHeader.ImageBase = (UINT64)data;
110
111 if (context->NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC) {
112 Print(L"Image has no relocation entry\n");
113 return EFI_UNSUPPORTED;
114 }
115
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)) {
120 return EFI_SUCCESS;
121 }
122
123 RelocBase = ImageAddress(data, size, context->RelocDir->VirtualAddress);
124 RelocBaseEnd = ImageAddress(data, size, context->RelocDir->VirtualAddress + context->RelocDir->Size - 1);
125
126 if (!RelocBase || !RelocBaseEnd) {
127 Print(L"Reloc table overflows binary\n");
128 return EFI_UNSUPPORTED;
129 }
130
131 Adjust = (UINT64)data - context->ImageAddress;
132
133 while (RelocBase < RelocBaseEnd) {
134 Reloc = (UINT16 *) ((char *) RelocBase + sizeof (EFI_IMAGE_BASE_RELOCATION));
135 RelocEnd = (UINT16 *) ((char *) RelocBase + RelocBase->SizeOfBlock);
136
137 if ((void *)RelocEnd < data || (void *)RelocEnd > ImageEnd) {
138 Print(L"Reloc entry overflows binary\n");
139 return EFI_UNSUPPORTED;
140 }
141
142 FixupBase = ImageAddress(data, size, RelocBase->VirtualAddress);
143 if (!FixupBase) {
144 Print(L"Invalid fixupbase\n");
145 return EFI_UNSUPPORTED;
146 }
147
148 while (Reloc < RelocEnd) {
149 Fixup = FixupBase + (*Reloc & 0xFFF);
150 switch ((*Reloc) >> 12) {
151 case EFI_IMAGE_REL_BASED_ABSOLUTE:
152 break;
153
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);
160 }
161 break;
162
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);
169 }
170 break;
171
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);
179 }
180 break;
181
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);
189 }
190 break;
191
192 default:
193 Print(L"Unknown relocation\n");
194 return EFI_UNSUPPORTED;
195 }
196 Reloc += 1;
197 }
198 RelocBase = (EFI_IMAGE_BASE_RELOCATION *) RelocEnd;
199 }
200
201 return EFI_SUCCESS;
202 }
203
204 /*
205 * Read the binary header and grab appropriate information from it
206 */
207 static EFI_STATUS read_header(void *data, unsigned int datasize,
208 GNUEFI_PE_COFF_LOADER_IMAGE_CONTEXT *context)
209 {
210 EFI_IMAGE_DOS_HEADER *DosHdr = data;
211 EFI_IMAGE_OPTIONAL_HEADER_UNION *PEHdr = data;
212
213 if (datasize < sizeof(EFI_IMAGE_DOS_HEADER)) {
214 Print(L"Invalid image\n");
215 return EFI_UNSUPPORTED;
216 }
217
218 if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE)
219 PEHdr = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)((char *)data + DosHdr->e_lfanew);
220
221 if ((((UINT8 *)PEHdr - (UINT8 *)data) + sizeof(EFI_IMAGE_OPTIONAL_HEADER_UNION)) > datasize) {
222 Print(L"Invalid image\n");
223 return EFI_UNSUPPORTED;
224 }
225
226 if (PEHdr->Te.Signature != EFI_IMAGE_NT_SIGNATURE) {
227 Print(L"Unsupported image type\n");
228 return EFI_UNSUPPORTED;
229 }
230
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;
234 }
235
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;
239 }
240
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];
251
252 if (context->ImageSize < context->SizeOfHeaders) {
253 Print(L"Invalid image\n");
254 return EFI_UNSUPPORTED;
255 }
256
257 if (((UINT8 *)context->SecDir - (UINT8 *)data) > (datasize - sizeof(EFI_IMAGE_DATA_DIRECTORY))) {
258 Print(L"Invalid image\n");
259 return EFI_UNSUPPORTED;
260 }
261
262 if (context->SecDir->VirtualAddress >= datasize) {
263 Print(L"Malformed security header\n");
264 return EFI_INVALID_PARAMETER;
265 }
266 return EFI_SUCCESS;
267 }
268
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)
273 {
274 EFI_GUID ShimLockGuid = SHIM_LOCK_GUID;
275 SHIM_LOCK *shim_lock;
276
277 if (BS->LocateProtocol(&ShimLockGuid, NULL, (VOID**) &shim_lock) == EFI_SUCCESS) {
278 if (!shim_lock)
279 return FALSE;
280
281 if (shim_lock->shim_verify(data, size) == EFI_SUCCESS)
282 return TRUE;
283 } // if
284
285 return FALSE;
286 } // BOOLEAN ShimValidate()
287
288 /*
289 * Once the image has been loaded it needs to be validated and relocated
290 */
291 static EFI_STATUS handle_image (void *data, unsigned int datasize,
292 EFI_LOADED_IMAGE *li, CHAR16 *Options, REFIT_VOLUME *DeviceVolume)
293 {
294 EFI_STATUS efi_status;
295 char *buffer;
296 int i, size;
297 EFI_IMAGE_SECTION_HEADER *Section;
298 char *base, *end;
299 GNUEFI_PE_COFF_LOADER_IMAGE_CONTEXT context;
300
301 /*
302 * The binary header contains relevant context and section pointers
303 */
304 efi_status = read_header(data, datasize, &context);
305 if (efi_status != EFI_SUCCESS) {
306 Print(L"Failed to read header\n");
307 return efi_status;
308 }
309
310 /*
311 * Validate the image; if this fails, return EFI_ACCESS_DENIED
312 */
313 if (!ShimValidate(data, datasize)) {
314 return EFI_ACCESS_DENIED;
315 }
316
317 buffer = AllocatePool(context.ImageSize);
318
319 if (!buffer) {
320 Print(L"Failed to allocate image buffer\n");
321 return EFI_OUT_OF_RESOURCES;
322 }
323
324 CopyMem(buffer, data, context.SizeOfHeaders);
325
326 /*
327 * Copy the executable's sections to their desired offsets
328 */
329 Section = context.FirstSection;
330 for (i = 0; i < context.NumberOfSections; i++) {
331 size = Section->Misc.VirtualSize;
332
333 if (size > Section->SizeOfRawData)
334 size = Section->SizeOfRawData;
335
336 base = ImageAddress (buffer, context.ImageSize, Section->VirtualAddress);
337 end = ImageAddress (buffer, context.ImageSize, Section->VirtualAddress + size - 1);
338
339 if (!base || !end) {
340 Print(L"Invalid section size\n");
341 return EFI_UNSUPPORTED;
342 }
343
344 if (Section->SizeOfRawData > 0)
345 CopyMem(base, data + Section->PointerToRawData, size);
346
347 if (size < Section->Misc.VirtualSize)
348 ZeroMem (base + size, Section->Misc.VirtualSize - size);
349
350 Section += 1;
351 }
352
353 /*
354 * Run the relocation fixups
355 */
356 efi_status = relocate_coff(&context, buffer);
357
358 if (efi_status != EFI_SUCCESS) {
359 Print(L"Relocation failed\n");
360 FreePool(buffer);
361 return efi_status;
362 }
363
364 entry_point = ImageAddress(buffer, context.ImageSize, context.EntryPoint);
365 /*
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
368 */
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);
375
376 if (!entry_point) {
377 Print(L"Invalid entry point\n");
378 FreePool(buffer);
379 return EFI_UNSUPPORTED;
380 }
381
382 return EFI_SUCCESS;
383 }
384
385 /*
386 * Load and run an EFI executable
387 */
388 EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath, VOID *data, UINTN datasize,
389 CHAR16 *Options, REFIT_VOLUME *DeviceVolume)
390 {
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;
395
396 if (data == NULL)
397 return EFI_LOAD_ERROR;
398
399 /*
400 * We need to refer to the loaded image protocol on the running
401 * binary in order to find our path
402 */
403 efi_status = uefi_call_wrapper(BS->HandleProtocol, 3, image_handle,
404 &loaded_image_protocol, (void **)&li);
405
406 if (efi_status != EFI_SUCCESS) {
407 Print(L"Unable to init protocol\n");
408 return efi_status;
409 }
410
411 /*
412 * We need to modify the loaded image protocol entry before running
413 * the new binary, so back it up
414 */
415 CopyMem(&li_bak, li, sizeof(li_bak));
416
417 /*
418 * Verify and, if appropriate, relocate and execute the executable
419 */
420 efi_status = handle_image(data, datasize, li, Options, DeviceVolume);
421
422 if (efi_status != EFI_SUCCESS) {
423 Print(L"Failed to load image\n");
424 CopyMem(li, &li_bak, sizeof(li_bak));
425 goto done;
426 }
427
428 /*
429 * The binary is trusted and relocated. Run it
430 */
431 efi_status = refit_call2_wrapper(entry_point, image_handle, ST);
432
433 /*
434 * Restore our original loaded image values
435 */
436 CopyMem(li, &li_bak, sizeof(li_bak));
437 done:
438 if (PathName)
439 FreePool(PathName);
440
441 if (data)
442 FreePool(data);
443
444 return efi_status;
445 }