]>
Commit | Line | Data |
---|---|---|
031e5cce SM |
1 | // SPDX-License-Identifier: BSD-2-Clause-Patent |
2 | /* | |
3 | * pe.c - helper functions for pe binaries. | |
4 | * Copyright Peter Jones <pjones@redhat.com> | |
5 | */ | |
6 | ||
7 | #include "shim.h" | |
8 | ||
9 | #include <openssl/err.h> | |
10 | #include <openssl/bn.h> | |
11 | #include <openssl/dh.h> | |
12 | #include <openssl/ocsp.h> | |
13 | #include <openssl/pkcs12.h> | |
14 | #include <openssl/rand.h> | |
15 | #include <openssl/crypto.h> | |
16 | #include <openssl/ssl.h> | |
17 | #include <openssl/x509.h> | |
18 | #include <openssl/x509v3.h> | |
19 | #include <openssl/rsa.h> | |
20 | #include <openssl/dso.h> | |
21 | ||
22 | #include <Library/BaseCryptLib.h> | |
23 | ||
24 | /* | |
25 | * Perform basic bounds checking of the intra-image pointers | |
26 | */ | |
27 | void * | |
28 | ImageAddress (void *image, uint64_t size, uint64_t address) | |
29 | { | |
30 | /* ensure our local pointer isn't bigger than our size */ | |
31 | if (address > size) | |
32 | return NULL; | |
33 | ||
34 | /* Insure our math won't overflow */ | |
35 | if (UINT64_MAX - address < (uint64_t)(intptr_t)image) | |
36 | return NULL; | |
37 | ||
38 | /* return the absolute pointer */ | |
39 | return image + address; | |
40 | } | |
41 | ||
42 | /* | |
43 | * Perform the actual relocation | |
44 | */ | |
45 | EFI_STATUS | |
46 | relocate_coff (PE_COFF_LOADER_IMAGE_CONTEXT *context, | |
47 | EFI_IMAGE_SECTION_HEADER *Section, | |
48 | void *orig, void *data) | |
49 | { | |
50 | EFI_IMAGE_BASE_RELOCATION *RelocBase, *RelocBaseEnd; | |
51 | UINT64 Adjust; | |
52 | UINT16 *Reloc, *RelocEnd; | |
53 | char *Fixup, *FixupBase; | |
54 | UINT16 *Fixup16; | |
55 | UINT32 *Fixup32; | |
56 | UINT64 *Fixup64; | |
57 | int size = context->ImageSize; | |
58 | void *ImageEnd = (char *)orig + size; | |
59 | int n = 0; | |
60 | ||
61 | /* Alright, so here's how this works: | |
62 | * | |
63 | * context->RelocDir gives us two things: | |
64 | * - the VA the table of base relocation blocks are (maybe) to be | |
65 | * mapped at (RelocDir->VirtualAddress) | |
66 | * - the virtual size (RelocDir->Size) | |
67 | * | |
68 | * The .reloc section (Section here) gives us some other things: | |
69 | * - the name! kind of. (Section->Name) | |
70 | * - the virtual size (Section->VirtualSize), which should be the same | |
71 | * as RelocDir->Size | |
72 | * - the virtual address (Section->VirtualAddress) | |
73 | * - the file section size (Section->SizeOfRawData), which is | |
74 | * a multiple of OptHdr->FileAlignment. Only useful for image | |
75 | * validation, not really useful for iteration bounds. | |
76 | * - the file address (Section->PointerToRawData) | |
77 | * - a bunch of stuff we don't use that's 0 in our binaries usually | |
78 | * - Flags (Section->Characteristics) | |
79 | * | |
80 | * and then the thing that's actually at the file address is an array | |
81 | * of EFI_IMAGE_BASE_RELOCATION structs with some values packed behind | |
82 | * them. The SizeOfBlock field of this structure includes the | |
83 | * structure itself, and adding it to that structure's address will | |
84 | * yield the next entry in the array. | |
85 | */ | |
86 | RelocBase = ImageAddress(orig, size, Section->PointerToRawData); | |
87 | /* RelocBaseEnd here is the address of the first entry /past/ the | |
88 | * table. */ | |
89 | RelocBaseEnd = ImageAddress(orig, size, Section->PointerToRawData + | |
90 | Section->Misc.VirtualSize); | |
91 | ||
92 | if (!RelocBase && !RelocBaseEnd) | |
93 | return EFI_SUCCESS; | |
94 | ||
95 | if (!RelocBase || !RelocBaseEnd) { | |
96 | perror(L"Reloc table overflows binary\n"); | |
97 | return EFI_UNSUPPORTED; | |
98 | } | |
99 | ||
100 | Adjust = (UINTN)data - context->ImageAddress; | |
101 | ||
102 | if (Adjust == 0) | |
103 | return EFI_SUCCESS; | |
104 | ||
105 | while (RelocBase < RelocBaseEnd) { | |
106 | Reloc = (UINT16 *) ((char *) RelocBase + sizeof (EFI_IMAGE_BASE_RELOCATION)); | |
107 | ||
108 | if (RelocBase->SizeOfBlock == 0) { | |
109 | perror(L"Reloc %d block size 0 is invalid\n", n); | |
110 | return EFI_UNSUPPORTED; | |
111 | } else if (RelocBase->SizeOfBlock > context->RelocDir->Size) { | |
112 | perror(L"Reloc %d block size %d greater than reloc dir" | |
113 | "size %d, which is invalid\n", n, | |
114 | RelocBase->SizeOfBlock, | |
115 | context->RelocDir->Size); | |
116 | return EFI_UNSUPPORTED; | |
117 | } | |
118 | ||
119 | RelocEnd = (UINT16 *) ((char *) RelocBase + RelocBase->SizeOfBlock); | |
120 | if ((void *)RelocEnd < orig || (void *)RelocEnd > ImageEnd) { | |
121 | perror(L"Reloc %d entry overflows binary\n", n); | |
122 | return EFI_UNSUPPORTED; | |
123 | } | |
124 | ||
125 | FixupBase = ImageAddress(data, size, RelocBase->VirtualAddress); | |
126 | if (!FixupBase) { | |
127 | perror(L"Reloc %d Invalid fixupbase\n", n); | |
128 | return EFI_UNSUPPORTED; | |
129 | } | |
130 | ||
131 | while (Reloc < RelocEnd) { | |
132 | Fixup = FixupBase + (*Reloc & 0xFFF); | |
133 | switch ((*Reloc) >> 12) { | |
134 | case EFI_IMAGE_REL_BASED_ABSOLUTE: | |
135 | break; | |
136 | ||
137 | case EFI_IMAGE_REL_BASED_HIGH: | |
138 | Fixup16 = (UINT16 *) Fixup; | |
139 | *Fixup16 = (UINT16) (*Fixup16 + ((UINT16) ((UINT32) Adjust >> 16))); | |
140 | break; | |
141 | ||
142 | case EFI_IMAGE_REL_BASED_LOW: | |
143 | Fixup16 = (UINT16 *) Fixup; | |
144 | *Fixup16 = (UINT16) (*Fixup16 + (UINT16) Adjust); | |
145 | break; | |
146 | ||
147 | case EFI_IMAGE_REL_BASED_HIGHLOW: | |
148 | Fixup32 = (UINT32 *) Fixup; | |
149 | *Fixup32 = *Fixup32 + (UINT32) Adjust; | |
150 | break; | |
151 | ||
152 | case EFI_IMAGE_REL_BASED_DIR64: | |
153 | Fixup64 = (UINT64 *) Fixup; | |
154 | *Fixup64 = *Fixup64 + (UINT64) Adjust; | |
155 | break; | |
156 | ||
157 | default: | |
158 | perror(L"Reloc %d Unknown relocation\n", n); | |
159 | return EFI_UNSUPPORTED; | |
160 | } | |
161 | Reloc += 1; | |
162 | } | |
163 | RelocBase = (EFI_IMAGE_BASE_RELOCATION *) RelocEnd; | |
164 | n++; | |
165 | } | |
166 | ||
167 | return EFI_SUCCESS; | |
168 | } | |
169 | ||
170 | #define check_size_line(data, datasize_in, hashbase, hashsize, l) ({ \ | |
171 | if ((unsigned long)hashbase > \ | |
172 | (unsigned long)data + datasize_in) { \ | |
173 | efi_status = EFI_INVALID_PARAMETER; \ | |
174 | perror(L"shim.c:%d Invalid hash base 0x%016x\n", l, \ | |
175 | hashbase); \ | |
176 | goto done; \ | |
177 | } \ | |
178 | if ((unsigned long)hashbase + hashsize > \ | |
179 | (unsigned long)data + datasize_in) { \ | |
180 | efi_status = EFI_INVALID_PARAMETER; \ | |
181 | perror(L"shim.c:%d Invalid hash size 0x%016x\n", l, \ | |
182 | hashsize); \ | |
183 | goto done; \ | |
184 | } \ | |
185 | }) | |
186 | #define check_size(d, ds, h, hs) check_size_line(d, ds, h, hs, __LINE__) | |
187 | ||
188 | EFI_STATUS | |
189 | get_section_vma (UINTN section_num, | |
190 | char *buffer, size_t bufsz UNUSED, | |
191 | PE_COFF_LOADER_IMAGE_CONTEXT *context, | |
192 | char **basep, size_t *sizep, | |
193 | EFI_IMAGE_SECTION_HEADER **sectionp) | |
194 | { | |
195 | EFI_IMAGE_SECTION_HEADER *sections = context->FirstSection; | |
196 | EFI_IMAGE_SECTION_HEADER *section; | |
197 | char *base = NULL, *end = NULL; | |
198 | ||
199 | if (section_num >= context->NumberOfSections) | |
200 | return EFI_NOT_FOUND; | |
201 | ||
202 | if (context->FirstSection == NULL) { | |
203 | perror(L"Invalid section %d requested\n", section_num); | |
204 | return EFI_UNSUPPORTED; | |
205 | } | |
206 | ||
207 | section = §ions[section_num]; | |
208 | ||
209 | base = ImageAddress (buffer, context->ImageSize, section->VirtualAddress); | |
210 | end = ImageAddress (buffer, context->ImageSize, | |
211 | section->VirtualAddress + section->Misc.VirtualSize - 1); | |
212 | ||
213 | if (!(section->Characteristics & EFI_IMAGE_SCN_MEM_DISCARDABLE)) { | |
214 | if (!base) { | |
215 | perror(L"Section %d has invalid base address\n", section_num); | |
216 | return EFI_UNSUPPORTED; | |
217 | } | |
218 | if (!end) { | |
219 | perror(L"Section %d has zero size\n", section_num); | |
220 | return EFI_UNSUPPORTED; | |
221 | } | |
222 | } | |
223 | ||
224 | if (!(section->Characteristics & EFI_IMAGE_SCN_CNT_UNINITIALIZED_DATA) && | |
225 | (section->VirtualAddress < context->SizeOfHeaders || | |
226 | section->PointerToRawData < context->SizeOfHeaders)) { | |
227 | perror(L"Section %d is inside image headers\n", section_num); | |
228 | return EFI_UNSUPPORTED; | |
229 | } | |
230 | ||
231 | if (end < base) { | |
232 | perror(L"Section %d has negative size\n", section_num); | |
233 | return EFI_UNSUPPORTED; | |
234 | } | |
235 | ||
236 | *basep = base; | |
237 | *sizep = end - base; | |
238 | *sectionp = section; | |
239 | return EFI_SUCCESS; | |
240 | } | |
241 | ||
242 | EFI_STATUS | |
243 | get_section_vma_by_name (char *name, size_t namesz, | |
244 | char *buffer, size_t bufsz, | |
245 | PE_COFF_LOADER_IMAGE_CONTEXT *context, | |
246 | char **basep, size_t *sizep, | |
247 | EFI_IMAGE_SECTION_HEADER **sectionp) | |
248 | { | |
249 | UINTN i; | |
250 | char namebuf[9]; | |
251 | ||
252 | if (!name || namesz == 0 || !buffer || bufsz < namesz || !context | |
253 | || !basep || !sizep || !sectionp) | |
254 | return EFI_INVALID_PARAMETER; | |
255 | ||
256 | /* | |
257 | * This code currently is only used for ".reloc\0\0" and | |
258 | * ".sbat\0\0\0", and it doesn't know how to look up longer section | |
259 | * names. | |
260 | */ | |
261 | if (namesz > 8) | |
262 | return EFI_UNSUPPORTED; | |
263 | ||
264 | SetMem(namebuf, sizeof(namebuf), 0); | |
265 | CopyMem(namebuf, name, MIN(namesz, 8)); | |
266 | ||
267 | /* | |
268 | * Copy the executable's sections to their desired offsets | |
269 | */ | |
270 | for (i = 0; i < context->NumberOfSections; i++) { | |
271 | EFI_STATUS status; | |
272 | EFI_IMAGE_SECTION_HEADER *section = NULL; | |
273 | char *base = NULL; | |
274 | size_t size = 0; | |
275 | ||
276 | status = get_section_vma(i, buffer, bufsz, context, &base, &size, §ion); | |
277 | if (!EFI_ERROR(status)) { | |
278 | if (CompareMem(section->Name, namebuf, 8) == 0) { | |
279 | *basep = base; | |
280 | *sizep = size; | |
281 | *sectionp = section; | |
282 | return EFI_SUCCESS; | |
283 | } | |
284 | continue; | |
285 | } | |
286 | ||
287 | switch(status) { | |
288 | case EFI_NOT_FOUND: | |
289 | break; | |
290 | } | |
291 | } | |
292 | ||
293 | return EFI_NOT_FOUND; | |
294 | } | |
295 | ||
296 | /* | |
297 | * Calculate the SHA1 and SHA256 hashes of a binary | |
298 | */ | |
299 | ||
300 | EFI_STATUS | |
8529e0f7 | 301 | generate_hash(char *data, unsigned int datasize, |
031e5cce SM |
302 | PE_COFF_LOADER_IMAGE_CONTEXT *context, UINT8 *sha256hash, |
303 | UINT8 *sha1hash) | |
304 | { | |
305 | unsigned int sha256ctxsize, sha1ctxsize; | |
031e5cce SM |
306 | void *sha256ctx = NULL, *sha1ctx = NULL; |
307 | char *hashbase; | |
308 | unsigned int hashsize; | |
309 | unsigned int SumOfBytesHashed, SumOfSectionBytes; | |
310 | unsigned int index, pos; | |
031e5cce SM |
311 | EFI_IMAGE_SECTION_HEADER *Section; |
312 | EFI_IMAGE_SECTION_HEADER *SectionHeader = NULL; | |
313 | EFI_STATUS efi_status = EFI_SUCCESS; | |
314 | EFI_IMAGE_DOS_HEADER *DosHdr = (void *)data; | |
315 | unsigned int PEHdr_offset = 0; | |
316 | ||
031e5cce SM |
317 | if (datasize <= sizeof (*DosHdr) || |
318 | DosHdr->e_magic != EFI_IMAGE_DOS_SIGNATURE) { | |
319 | perror(L"Invalid signature\n"); | |
320 | return EFI_INVALID_PARAMETER; | |
321 | } | |
322 | PEHdr_offset = DosHdr->e_lfanew; | |
323 | ||
324 | sha256ctxsize = Sha256GetContextSize(); | |
325 | sha256ctx = AllocatePool(sha256ctxsize); | |
326 | ||
327 | sha1ctxsize = Sha1GetContextSize(); | |
328 | sha1ctx = AllocatePool(sha1ctxsize); | |
329 | ||
330 | if (!sha256ctx || !sha1ctx) { | |
331 | perror(L"Unable to allocate memory for hash context\n"); | |
332 | return EFI_OUT_OF_RESOURCES; | |
333 | } | |
334 | ||
335 | if (!Sha256Init(sha256ctx) || !Sha1Init(sha1ctx)) { | |
336 | perror(L"Unable to initialise hash\n"); | |
337 | efi_status = EFI_OUT_OF_RESOURCES; | |
338 | goto done; | |
339 | } | |
340 | ||
341 | /* Hash start to checksum */ | |
342 | hashbase = data; | |
343 | hashsize = (char *)&context->PEHdr->Pe32.OptionalHeader.CheckSum - | |
344 | hashbase; | |
8529e0f7 | 345 | check_size(data, datasize, hashbase, hashsize); |
031e5cce SM |
346 | |
347 | if (!(Sha256Update(sha256ctx, hashbase, hashsize)) || | |
348 | !(Sha1Update(sha1ctx, hashbase, hashsize))) { | |
349 | perror(L"Unable to generate hash\n"); | |
350 | efi_status = EFI_OUT_OF_RESOURCES; | |
351 | goto done; | |
352 | } | |
353 | ||
354 | /* Hash post-checksum to start of certificate table */ | |
355 | hashbase = (char *)&context->PEHdr->Pe32.OptionalHeader.CheckSum + | |
356 | sizeof (int); | |
357 | hashsize = (char *)context->SecDir - hashbase; | |
8529e0f7 | 358 | check_size(data, datasize, hashbase, hashsize); |
031e5cce SM |
359 | |
360 | if (!(Sha256Update(sha256ctx, hashbase, hashsize)) || | |
361 | !(Sha1Update(sha1ctx, hashbase, hashsize))) { | |
362 | perror(L"Unable to generate hash\n"); | |
363 | efi_status = EFI_OUT_OF_RESOURCES; | |
364 | goto done; | |
365 | } | |
366 | ||
367 | /* Hash end of certificate table to end of image header */ | |
368 | EFI_IMAGE_DATA_DIRECTORY *dd = context->SecDir + 1; | |
369 | hashbase = (char *)dd; | |
370 | hashsize = context->SizeOfHeaders - (unsigned long)((char *)dd - data); | |
8529e0f7 | 371 | if (hashsize > datasize) { |
031e5cce SM |
372 | perror(L"Data Directory size %d is invalid\n", hashsize); |
373 | efi_status = EFI_INVALID_PARAMETER; | |
374 | goto done; | |
375 | } | |
8529e0f7 | 376 | check_size(data, datasize, hashbase, hashsize); |
031e5cce SM |
377 | |
378 | if (!(Sha256Update(sha256ctx, hashbase, hashsize)) || | |
379 | !(Sha1Update(sha1ctx, hashbase, hashsize))) { | |
380 | perror(L"Unable to generate hash\n"); | |
381 | efi_status = EFI_OUT_OF_RESOURCES; | |
382 | goto done; | |
383 | } | |
384 | ||
385 | /* Sort sections */ | |
386 | SumOfBytesHashed = context->SizeOfHeaders; | |
387 | ||
388 | /* | |
389 | * XXX Do we need this here, or is it already done in all cases? | |
390 | */ | |
391 | if (context->NumberOfSections == 0 || | |
392 | context->FirstSection == NULL) { | |
393 | uint16_t opthdrsz; | |
394 | uint64_t addr; | |
395 | uint16_t nsections; | |
396 | EFI_IMAGE_SECTION_HEADER *section0, *sectionN; | |
397 | ||
398 | nsections = context->PEHdr->Pe32.FileHeader.NumberOfSections; | |
399 | opthdrsz = context->PEHdr->Pe32.FileHeader.SizeOfOptionalHeader; | |
400 | ||
401 | /* Validate section0 is within image */ | |
402 | addr = PEHdr_offset + sizeof(UINT32) | |
403 | + sizeof(EFI_IMAGE_FILE_HEADER) | |
404 | + opthdrsz; | |
405 | section0 = ImageAddress(data, datasize, addr); | |
406 | if (!section0) { | |
407 | perror(L"Malformed file header.\n"); | |
408 | perror(L"Image address for Section Header 0 is 0x%016llx\n", | |
409 | addr); | |
410 | perror(L"File size is 0x%016llx\n", datasize); | |
411 | efi_status = EFI_INVALID_PARAMETER; | |
412 | goto done; | |
413 | } | |
414 | ||
415 | /* Validate sectionN is within image */ | |
416 | addr += (uint64_t)(intptr_t)§ion0[nsections-1] - | |
417 | (uint64_t)(intptr_t)section0; | |
418 | sectionN = ImageAddress(data, datasize, addr); | |
419 | if (!sectionN) { | |
420 | perror(L"Malformed file header.\n"); | |
421 | perror(L"Image address for Section Header %d is 0x%016llx\n", | |
422 | nsections - 1, addr); | |
423 | perror(L"File size is 0x%016llx\n", datasize); | |
424 | efi_status = EFI_INVALID_PARAMETER; | |
425 | goto done; | |
426 | } | |
427 | ||
428 | context->NumberOfSections = nsections; | |
429 | context->FirstSection = section0; | |
430 | } | |
431 | ||
432 | /* | |
433 | * Allocate a new section table so we can sort them without | |
434 | * modifying the image. | |
435 | */ | |
436 | SectionHeader = AllocateZeroPool (sizeof (EFI_IMAGE_SECTION_HEADER) | |
437 | * context->NumberOfSections); | |
438 | if (SectionHeader == NULL) { | |
439 | perror(L"Unable to allocate section header\n"); | |
440 | efi_status = EFI_OUT_OF_RESOURCES; | |
441 | goto done; | |
442 | } | |
443 | ||
444 | /* | |
445 | * Validate section locations and sizes, and sort the table into | |
446 | * our newly allocated header table | |
447 | */ | |
448 | SumOfSectionBytes = 0; | |
449 | Section = context->FirstSection; | |
450 | for (index = 0; index < context->NumberOfSections; index++) { | |
451 | EFI_IMAGE_SECTION_HEADER *SectionPtr; | |
452 | char *base; | |
453 | size_t size; | |
454 | ||
455 | efi_status = get_section_vma(index, data, datasize, context, | |
456 | &base, &size, &SectionPtr); | |
457 | if (efi_status == EFI_NOT_FOUND) | |
458 | break; | |
459 | if (EFI_ERROR(efi_status)) { | |
460 | perror(L"Malformed section header\n"); | |
461 | goto done; | |
462 | } | |
463 | ||
464 | /* Validate section size is within image. */ | |
465 | if (SectionPtr->SizeOfRawData > | |
466 | datasize - SumOfBytesHashed - SumOfSectionBytes) { | |
467 | perror(L"Malformed section %d size\n", index); | |
468 | efi_status = EFI_INVALID_PARAMETER; | |
469 | goto done; | |
470 | } | |
471 | SumOfSectionBytes += SectionPtr->SizeOfRawData; | |
472 | ||
473 | pos = index; | |
474 | while ((pos > 0) && (Section->PointerToRawData < SectionHeader[pos - 1].PointerToRawData)) { | |
475 | CopyMem (&SectionHeader[pos], &SectionHeader[pos - 1], sizeof (EFI_IMAGE_SECTION_HEADER)); | |
476 | pos--; | |
477 | } | |
478 | CopyMem (&SectionHeader[pos], Section, sizeof (EFI_IMAGE_SECTION_HEADER)); | |
479 | Section += 1; | |
480 | ||
481 | } | |
482 | ||
483 | /* Hash the sections */ | |
484 | for (index = 0; index < context->NumberOfSections; index++) { | |
485 | Section = &SectionHeader[index]; | |
486 | if (Section->SizeOfRawData == 0) { | |
487 | continue; | |
488 | } | |
489 | ||
8529e0f7 SM |
490 | hashbase = ImageAddress(data, datasize, |
491 | Section->PointerToRawData); | |
031e5cce SM |
492 | if (!hashbase) { |
493 | perror(L"Malformed section header\n"); | |
494 | efi_status = EFI_INVALID_PARAMETER; | |
495 | goto done; | |
496 | } | |
497 | ||
498 | /* Verify hashsize within image. */ | |
499 | if (Section->SizeOfRawData > | |
500 | datasize - Section->PointerToRawData) { | |
501 | perror(L"Malformed section raw size %d\n", index); | |
502 | efi_status = EFI_INVALID_PARAMETER; | |
503 | goto done; | |
504 | } | |
505 | hashsize = (unsigned int) Section->SizeOfRawData; | |
8529e0f7 | 506 | check_size(data, datasize, hashbase, hashsize); |
031e5cce SM |
507 | |
508 | if (!(Sha256Update(sha256ctx, hashbase, hashsize)) || | |
509 | !(Sha1Update(sha1ctx, hashbase, hashsize))) { | |
510 | perror(L"Unable to generate hash\n"); | |
511 | efi_status = EFI_OUT_OF_RESOURCES; | |
512 | goto done; | |
513 | } | |
514 | SumOfBytesHashed += Section->SizeOfRawData; | |
515 | } | |
516 | ||
517 | /* Hash all remaining data up to SecDir if SecDir->Size is not 0 */ | |
518 | if (datasize > SumOfBytesHashed && context->SecDir->Size) { | |
519 | hashbase = data + SumOfBytesHashed; | |
520 | hashsize = datasize - context->SecDir->Size - SumOfBytesHashed; | |
521 | ||
522 | if ((datasize - SumOfBytesHashed < context->SecDir->Size) || | |
523 | (SumOfBytesHashed + hashsize != context->SecDir->VirtualAddress)) { | |
524 | perror(L"Malformed binary after Attribute Certificate Table\n"); | |
525 | console_print(L"datasize: %u SumOfBytesHashed: %u SecDir->Size: %lu\n", | |
526 | datasize, SumOfBytesHashed, context->SecDir->Size); | |
527 | console_print(L"hashsize: %u SecDir->VirtualAddress: 0x%08lx\n", | |
528 | hashsize, context->SecDir->VirtualAddress); | |
529 | efi_status = EFI_INVALID_PARAMETER; | |
530 | goto done; | |
531 | } | |
8529e0f7 | 532 | check_size(data, datasize, hashbase, hashsize); |
031e5cce SM |
533 | |
534 | if (!(Sha256Update(sha256ctx, hashbase, hashsize)) || | |
535 | !(Sha1Update(sha1ctx, hashbase, hashsize))) { | |
536 | perror(L"Unable to generate hash\n"); | |
537 | efi_status = EFI_OUT_OF_RESOURCES; | |
538 | goto done; | |
539 | } | |
540 | ||
541 | #if 1 | |
542 | } | |
543 | #else // we have to migrate to doing this later :/ | |
544 | SumOfBytesHashed += hashsize; | |
545 | } | |
546 | ||
547 | /* Hash all remaining data */ | |
548 | if (datasize > SumOfBytesHashed) { | |
549 | hashbase = data + SumOfBytesHashed; | |
550 | hashsize = datasize - SumOfBytesHashed; | |
551 | ||
8529e0f7 | 552 | check_size(data, datasize, hashbase, hashsize); |
031e5cce SM |
553 | |
554 | if (!(Sha256Update(sha256ctx, hashbase, hashsize)) || | |
555 | !(Sha1Update(sha1ctx, hashbase, hashsize))) { | |
556 | perror(L"Unable to generate hash\n"); | |
557 | efi_status = EFI_OUT_OF_RESOURCES; | |
558 | goto done; | |
559 | } | |
560 | ||
561 | SumOfBytesHashed += hashsize; | |
562 | } | |
563 | #endif | |
564 | ||
565 | if (!(Sha256Final(sha256ctx, sha256hash)) || | |
566 | !(Sha1Final(sha1ctx, sha1hash))) { | |
567 | perror(L"Unable to finalise hash\n"); | |
568 | efi_status = EFI_OUT_OF_RESOURCES; | |
569 | goto done; | |
570 | } | |
571 | ||
572 | dprint(L"sha1 authenticode hash:\n"); | |
573 | dhexdumpat(sha1hash, SHA1_DIGEST_SIZE, 0); | |
574 | dprint(L"sha256 authenticode hash:\n"); | |
575 | dhexdumpat(sha256hash, SHA256_DIGEST_SIZE, 0); | |
576 | ||
577 | done: | |
578 | if (SectionHeader) | |
579 | FreePool(SectionHeader); | |
580 | if (sha1ctx) | |
581 | FreePool(sha1ctx); | |
582 | if (sha256ctx) | |
583 | FreePool(sha256ctx); | |
584 | ||
585 | return efi_status; | |
586 | } | |
587 | ||
588 | /* here's a chart: | |
589 | * i686 x86_64 aarch64 | |
590 | * 64-on-64: nyet yes yes | |
591 | * 64-on-32: nyet yes nyet | |
592 | * 32-on-32: yes yes no | |
593 | */ | |
594 | static int | |
595 | allow_64_bit(void) | |
596 | { | |
597 | #if defined(__x86_64__) || defined(__aarch64__) | |
598 | return 1; | |
599 | #elif defined(__i386__) || defined(__i686__) | |
600 | /* Right now blindly assuming the kernel will correctly detect this | |
601 | * and /halt the system/ if you're not really on a 64-bit cpu */ | |
602 | if (in_protocol) | |
603 | return 1; | |
604 | return 0; | |
605 | #else /* assuming everything else is 32-bit... */ | |
606 | return 0; | |
607 | #endif | |
608 | } | |
609 | ||
610 | static int | |
611 | allow_32_bit(void) | |
612 | { | |
613 | #if defined(__x86_64__) | |
614 | #if defined(ALLOW_32BIT_KERNEL_ON_X64) | |
615 | if (in_protocol) | |
616 | return 1; | |
617 | return 0; | |
618 | #else | |
619 | return 0; | |
620 | #endif | |
621 | #elif defined(__i386__) || defined(__i686__) | |
622 | return 1; | |
623 | #elif defined(__aarch64__) | |
624 | return 0; | |
625 | #else /* assuming everything else is 32-bit... */ | |
626 | return 1; | |
627 | #endif | |
628 | } | |
629 | ||
630 | static int | |
631 | image_is_64_bit(EFI_IMAGE_OPTIONAL_HEADER_UNION *PEHdr) | |
632 | { | |
633 | /* .Magic is the same offset in all cases */ | |
634 | if (PEHdr->Pe32Plus.OptionalHeader.Magic | |
635 | == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) | |
636 | return 1; | |
637 | return 0; | |
638 | } | |
639 | ||
640 | static const UINT16 machine_type = | |
641 | #if defined(__x86_64__) | |
642 | IMAGE_FILE_MACHINE_X64; | |
643 | #elif defined(__aarch64__) | |
644 | IMAGE_FILE_MACHINE_ARM64; | |
645 | #elif defined(__arm__) | |
646 | IMAGE_FILE_MACHINE_ARMTHUMB_MIXED; | |
647 | #elif defined(__i386__) || defined(__i486__) || defined(__i686__) | |
648 | IMAGE_FILE_MACHINE_I386; | |
649 | #elif defined(__ia64__) | |
650 | IMAGE_FILE_MACHINE_IA64; | |
651 | #else | |
652 | #error this architecture is not supported by shim | |
653 | #endif | |
654 | ||
655 | static int | |
656 | image_is_loadable(EFI_IMAGE_OPTIONAL_HEADER_UNION *PEHdr) | |
657 | { | |
658 | /* If the machine type doesn't match the binary, bail, unless | |
659 | * we're in an allowed 64-on-32 scenario */ | |
660 | if (PEHdr->Pe32.FileHeader.Machine != machine_type) { | |
661 | if (!(machine_type == IMAGE_FILE_MACHINE_I386 && | |
662 | PEHdr->Pe32.FileHeader.Machine == IMAGE_FILE_MACHINE_X64 && | |
663 | allow_64_bit())) { | |
664 | return 0; | |
665 | } | |
666 | } | |
667 | ||
668 | /* If it's not a header type we recognize at all, bail */ | |
669 | switch (PEHdr->Pe32Plus.OptionalHeader.Magic) { | |
670 | case EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC: | |
671 | case EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC: | |
672 | break; | |
673 | default: | |
674 | return 0; | |
675 | } | |
676 | ||
677 | /* and now just check for general 64-vs-32 compatibility */ | |
678 | if (image_is_64_bit(PEHdr)) { | |
679 | if (allow_64_bit()) | |
680 | return 1; | |
681 | } else { | |
682 | if (allow_32_bit()) | |
683 | return 1; | |
684 | } | |
685 | return 0; | |
686 | } | |
687 | ||
688 | /* | |
689 | * Read the binary header and grab appropriate information from it | |
690 | */ | |
691 | EFI_STATUS | |
692 | read_header(void *data, unsigned int datasize, | |
693 | PE_COFF_LOADER_IMAGE_CONTEXT *context) | |
694 | { | |
695 | EFI_IMAGE_DOS_HEADER *DosHdr = data; | |
696 | EFI_IMAGE_OPTIONAL_HEADER_UNION *PEHdr = data; | |
697 | unsigned long HeaderWithoutDataDir, SectionHeaderOffset, OptHeaderSize; | |
698 | unsigned long FileAlignment = 0; | |
699 | ||
700 | if (datasize < sizeof (PEHdr->Pe32)) { | |
701 | perror(L"Invalid image\n"); | |
702 | return EFI_UNSUPPORTED; | |
703 | } | |
704 | ||
705 | if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) | |
706 | PEHdr = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)((char *)data + DosHdr->e_lfanew); | |
707 | ||
708 | if (!image_is_loadable(PEHdr)) { | |
709 | perror(L"Platform does not support this image\n"); | |
710 | return EFI_UNSUPPORTED; | |
711 | } | |
712 | ||
713 | if (image_is_64_bit(PEHdr)) { | |
714 | context->NumberOfRvaAndSizes = PEHdr->Pe32Plus.OptionalHeader.NumberOfRvaAndSizes; | |
715 | context->SizeOfHeaders = PEHdr->Pe32Plus.OptionalHeader.SizeOfHeaders; | |
716 | context->ImageSize = PEHdr->Pe32Plus.OptionalHeader.SizeOfImage; | |
717 | context->SectionAlignment = PEHdr->Pe32Plus.OptionalHeader.SectionAlignment; | |
718 | FileAlignment = PEHdr->Pe32Plus.OptionalHeader.FileAlignment; | |
719 | OptHeaderSize = sizeof(EFI_IMAGE_OPTIONAL_HEADER64); | |
720 | } else { | |
721 | context->NumberOfRvaAndSizes = PEHdr->Pe32.OptionalHeader.NumberOfRvaAndSizes; | |
722 | context->SizeOfHeaders = PEHdr->Pe32.OptionalHeader.SizeOfHeaders; | |
723 | context->ImageSize = (UINT64)PEHdr->Pe32.OptionalHeader.SizeOfImage; | |
724 | context->SectionAlignment = PEHdr->Pe32.OptionalHeader.SectionAlignment; | |
725 | FileAlignment = PEHdr->Pe32.OptionalHeader.FileAlignment; | |
726 | OptHeaderSize = sizeof(EFI_IMAGE_OPTIONAL_HEADER32); | |
727 | } | |
728 | ||
729 | if (FileAlignment % 2 != 0) { | |
730 | perror(L"File Alignment is invalid (%d)\n", FileAlignment); | |
731 | return EFI_UNSUPPORTED; | |
732 | } | |
733 | if (FileAlignment == 0) | |
734 | FileAlignment = 0x200; | |
735 | if (context->SectionAlignment == 0) | |
736 | context->SectionAlignment = PAGE_SIZE; | |
737 | if (context->SectionAlignment < FileAlignment) | |
738 | context->SectionAlignment = FileAlignment; | |
739 | ||
740 | context->NumberOfSections = PEHdr->Pe32.FileHeader.NumberOfSections; | |
741 | ||
742 | if (EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES < context->NumberOfRvaAndSizes) { | |
743 | perror(L"Image header too small\n"); | |
744 | return EFI_UNSUPPORTED; | |
745 | } | |
746 | ||
747 | HeaderWithoutDataDir = OptHeaderSize | |
748 | - sizeof (EFI_IMAGE_DATA_DIRECTORY) * EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES; | |
749 | if (((UINT32)PEHdr->Pe32.FileHeader.SizeOfOptionalHeader - HeaderWithoutDataDir) != | |
750 | context->NumberOfRvaAndSizes * sizeof (EFI_IMAGE_DATA_DIRECTORY)) { | |
751 | perror(L"Image header overflows data directory\n"); | |
752 | return EFI_UNSUPPORTED; | |
753 | } | |
754 | ||
755 | SectionHeaderOffset = DosHdr->e_lfanew | |
756 | + sizeof (UINT32) | |
757 | + sizeof (EFI_IMAGE_FILE_HEADER) | |
758 | + PEHdr->Pe32.FileHeader.SizeOfOptionalHeader; | |
759 | if (((UINT32)context->ImageSize - SectionHeaderOffset) / EFI_IMAGE_SIZEOF_SECTION_HEADER | |
760 | <= context->NumberOfSections) { | |
761 | perror(L"Image sections overflow image size\n"); | |
762 | return EFI_UNSUPPORTED; | |
763 | } | |
764 | ||
765 | if ((context->SizeOfHeaders - SectionHeaderOffset) / EFI_IMAGE_SIZEOF_SECTION_HEADER | |
766 | < (UINT32)context->NumberOfSections) { | |
767 | perror(L"Image sections overflow section headers\n"); | |
768 | return EFI_UNSUPPORTED; | |
769 | } | |
770 | ||
771 | if ((((UINT8 *)PEHdr - (UINT8 *)data) + sizeof(EFI_IMAGE_OPTIONAL_HEADER_UNION)) > datasize) { | |
772 | perror(L"Invalid image\n"); | |
773 | return EFI_UNSUPPORTED; | |
774 | } | |
775 | ||
776 | if (PEHdr->Te.Signature != EFI_IMAGE_NT_SIGNATURE) { | |
777 | perror(L"Unsupported image type\n"); | |
778 | return EFI_UNSUPPORTED; | |
779 | } | |
780 | ||
781 | if (PEHdr->Pe32.FileHeader.Characteristics & EFI_IMAGE_FILE_RELOCS_STRIPPED) { | |
782 | perror(L"Unsupported image - Relocations have been stripped\n"); | |
783 | return EFI_UNSUPPORTED; | |
784 | } | |
785 | ||
786 | context->PEHdr = PEHdr; | |
787 | ||
788 | if (image_is_64_bit(PEHdr)) { | |
789 | context->ImageAddress = PEHdr->Pe32Plus.OptionalHeader.ImageBase; | |
790 | context->EntryPoint = PEHdr->Pe32Plus.OptionalHeader.AddressOfEntryPoint; | |
791 | context->RelocDir = &PEHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC]; | |
792 | context->SecDir = &PEHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]; | |
793 | } else { | |
794 | context->ImageAddress = PEHdr->Pe32.OptionalHeader.ImageBase; | |
795 | context->EntryPoint = PEHdr->Pe32.OptionalHeader.AddressOfEntryPoint; | |
796 | context->RelocDir = &PEHdr->Pe32.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC]; | |
797 | context->SecDir = &PEHdr->Pe32.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]; | |
798 | } | |
799 | ||
800 | context->FirstSection = (EFI_IMAGE_SECTION_HEADER *)((char *)PEHdr + PEHdr->Pe32.FileHeader.SizeOfOptionalHeader + sizeof(UINT32) + sizeof(EFI_IMAGE_FILE_HEADER)); | |
801 | ||
802 | if (context->ImageSize < context->SizeOfHeaders) { | |
803 | perror(L"Invalid image\n"); | |
804 | return EFI_UNSUPPORTED; | |
805 | } | |
806 | ||
807 | if ((unsigned long)((UINT8 *)context->SecDir - (UINT8 *)data) > | |
808 | (datasize - sizeof(EFI_IMAGE_DATA_DIRECTORY))) { | |
809 | perror(L"Invalid image\n"); | |
810 | return EFI_UNSUPPORTED; | |
811 | } | |
812 | ||
813 | if (context->SecDir->VirtualAddress > datasize || | |
814 | (context->SecDir->VirtualAddress == datasize && | |
815 | context->SecDir->Size > 0)) { | |
816 | perror(L"Malformed security header\n"); | |
817 | return EFI_INVALID_PARAMETER; | |
818 | } | |
819 | return EFI_SUCCESS; | |
820 | } | |
821 | ||
822 | EFI_STATUS | |
823 | handle_sbat(char *SBATBase, size_t SBATSize) | |
824 | { | |
825 | unsigned int i; | |
826 | EFI_STATUS efi_status; | |
827 | size_t n; | |
828 | struct sbat_section_entry **entries = NULL; | |
829 | char *sbat_data; | |
830 | size_t sbat_size; | |
831 | ||
832 | if (list_empty(&sbat_var)) | |
833 | return EFI_SUCCESS; | |
834 | ||
835 | if (SBATBase == NULL || SBATSize == 0) { | |
836 | dprint(L"No .sbat section data\n"); | |
837 | return EFI_SECURITY_VIOLATION; | |
838 | } | |
839 | ||
840 | sbat_size = SBATSize + 1; | |
841 | sbat_data = AllocatePool(sbat_size); | |
842 | if (!sbat_data) { | |
843 | console_print(L"Failed to allocate .sbat section buffer\n"); | |
844 | return EFI_OUT_OF_RESOURCES; | |
845 | } | |
846 | CopyMem(sbat_data, SBATBase, SBATSize); | |
847 | sbat_data[SBATSize] = '\0'; | |
848 | ||
849 | efi_status = parse_sbat_section(sbat_data, sbat_size, &n, &entries); | |
850 | if (EFI_ERROR(efi_status)) { | |
851 | perror(L"Could not parse .sbat section data: %r\n", efi_status); | |
852 | goto err; | |
853 | } | |
854 | ||
855 | dprint(L"SBAT section data\n"); | |
856 | for (i = 0; i < n; i++) { | |
857 | dprint(L"%a, %a, %a, %a, %a, %a\n", | |
858 | entries[i]->component_name, | |
859 | entries[i]->component_generation, | |
860 | entries[i]->vendor_name, | |
861 | entries[i]->vendor_package_name, | |
862 | entries[i]->vendor_version, | |
863 | entries[i]->vendor_url); | |
864 | } | |
865 | ||
866 | efi_status = verify_sbat(n, entries); | |
867 | ||
868 | cleanup_sbat_section_entries(n, entries); | |
869 | ||
870 | err: | |
871 | FreePool(sbat_data); | |
872 | ||
873 | return efi_status; | |
874 | } | |
875 | ||
876 | /* | |
877 | * Once the image has been loaded it needs to be validated and relocated | |
878 | */ | |
879 | EFI_STATUS | |
880 | handle_image (void *data, unsigned int datasize, | |
881 | EFI_LOADED_IMAGE *li, | |
882 | EFI_IMAGE_ENTRY_POINT *entry_point, | |
883 | EFI_PHYSICAL_ADDRESS *alloc_address, | |
884 | UINTN *alloc_pages) | |
885 | { | |
886 | EFI_STATUS efi_status; | |
887 | char *buffer; | |
888 | int i; | |
889 | EFI_IMAGE_SECTION_HEADER *Section; | |
890 | char *base, *end; | |
891 | PE_COFF_LOADER_IMAGE_CONTEXT context; | |
892 | unsigned int alignment, alloc_size; | |
893 | int found_entry_point = 0; | |
894 | UINT8 sha1hash[SHA1_DIGEST_SIZE]; | |
895 | UINT8 sha256hash[SHA256_DIGEST_SIZE]; | |
896 | ||
897 | /* | |
898 | * The binary header contains relevant context and section pointers | |
899 | */ | |
900 | efi_status = read_header(data, datasize, &context); | |
901 | if (EFI_ERROR(efi_status)) { | |
902 | perror(L"Failed to read header: %r\n", efi_status); | |
903 | return efi_status; | |
904 | } | |
905 | ||
906 | /* | |
907 | * We only need to verify the binary if we're in secure mode | |
908 | */ | |
909 | efi_status = generate_hash(data, datasize, &context, sha256hash, | |
910 | sha1hash); | |
911 | if (EFI_ERROR(efi_status)) | |
912 | return efi_status; | |
913 | ||
914 | /* Measure the binary into the TPM */ | |
915 | #ifdef REQUIRE_TPM | |
916 | efi_status = | |
917 | #endif | |
918 | tpm_log_pe((EFI_PHYSICAL_ADDRESS)(UINTN)data, datasize, | |
919 | (EFI_PHYSICAL_ADDRESS)(UINTN)context.ImageAddress, | |
920 | li->FilePath, sha1hash, 4); | |
921 | #ifdef REQUIRE_TPM | |
922 | if (efi_status != EFI_SUCCESS) { | |
923 | return efi_status; | |
924 | } | |
925 | #endif | |
926 | ||
927 | /* The spec says, uselessly, of SectionAlignment: | |
928 | * ===== | |
929 | * The alignment (in bytes) of sections when they are loaded into | |
930 | * memory. It must be greater than or equal to FileAlignment. The | |
931 | * default is the page size for the architecture. | |
932 | * ===== | |
933 | * Which doesn't tell you whose responsibility it is to enforce the | |
934 | * "default", or when. It implies that the value in the field must | |
935 | * be > FileAlignment (also poorly defined), but it appears visual | |
936 | * studio will happily write 512 for FileAlignment (its default) and | |
937 | * 0 for SectionAlignment, intending to imply PAGE_SIZE. | |
938 | * | |
939 | * We only support one page size, so if it's zero, nerf it to 4096. | |
940 | */ | |
941 | alignment = context.SectionAlignment; | |
942 | if (!alignment) | |
943 | alignment = 4096; | |
944 | ||
945 | alloc_size = ALIGN_VALUE(context.ImageSize + context.SectionAlignment, | |
946 | PAGE_SIZE); | |
947 | *alloc_pages = alloc_size / PAGE_SIZE; | |
948 | ||
8529e0f7 SM |
949 | efi_status = BS->AllocatePages(AllocateAnyPages, EfiLoaderCode, |
950 | *alloc_pages, alloc_address); | |
031e5cce SM |
951 | if (EFI_ERROR(efi_status)) { |
952 | perror(L"Failed to allocate image buffer\n"); | |
953 | return EFI_OUT_OF_RESOURCES; | |
954 | } | |
955 | ||
956 | buffer = (void *)ALIGN_VALUE((unsigned long)*alloc_address, alignment); | |
957 | ||
958 | CopyMem(buffer, data, context.SizeOfHeaders); | |
959 | ||
960 | *entry_point = ImageAddress(buffer, context.ImageSize, context.EntryPoint); | |
961 | if (!*entry_point) { | |
962 | perror(L"Entry point is invalid\n"); | |
8529e0f7 | 963 | BS->FreePages(*alloc_address, *alloc_pages); |
031e5cce SM |
964 | return EFI_UNSUPPORTED; |
965 | } | |
966 | ||
967 | char *RelocBase, *RelocBaseEnd; | |
968 | /* | |
969 | * These are relative virtual addresses, so we have to check them | |
970 | * against the image size, not the data size. | |
971 | */ | |
972 | RelocBase = ImageAddress(buffer, context.ImageSize, | |
973 | context.RelocDir->VirtualAddress); | |
974 | /* | |
975 | * RelocBaseEnd here is the address of the last byte of the table | |
976 | */ | |
977 | RelocBaseEnd = ImageAddress(buffer, context.ImageSize, | |
978 | context.RelocDir->VirtualAddress + | |
979 | context.RelocDir->Size - 1); | |
980 | ||
981 | EFI_IMAGE_SECTION_HEADER *RelocSection = NULL; | |
982 | ||
983 | char *SBATBase = NULL; | |
984 | size_t SBATSize = 0; | |
985 | ||
986 | /* | |
987 | * Copy the executable's sections to their desired offsets | |
988 | */ | |
989 | Section = context.FirstSection; | |
990 | for (i = 0; i < context.NumberOfSections; i++, Section++) { | |
991 | /* Don't try to copy discardable sections with zero size */ | |
992 | if ((Section->Characteristics & EFI_IMAGE_SCN_MEM_DISCARDABLE) && | |
993 | !Section->Misc.VirtualSize) | |
994 | continue; | |
995 | ||
996 | base = ImageAddress (buffer, context.ImageSize, | |
997 | Section->VirtualAddress); | |
998 | end = ImageAddress (buffer, context.ImageSize, | |
999 | Section->VirtualAddress | |
1000 | + Section->Misc.VirtualSize - 1); | |
1001 | ||
1002 | if (end < base) { | |
1003 | perror(L"Section %d has negative size\n", i); | |
8529e0f7 | 1004 | BS->FreePages(*alloc_address, *alloc_pages); |
031e5cce SM |
1005 | return EFI_UNSUPPORTED; |
1006 | } | |
1007 | ||
1008 | if (Section->VirtualAddress <= context.EntryPoint && | |
1009 | (Section->VirtualAddress + Section->SizeOfRawData - 1) | |
1010 | > context.EntryPoint) | |
1011 | found_entry_point++; | |
1012 | ||
1013 | /* We do want to process .reloc, but it's often marked | |
1014 | * discardable, so we don't want to memcpy it. */ | |
1015 | if (CompareMem(Section->Name, ".reloc\0\0", 8) == 0) { | |
1016 | if (RelocSection) { | |
1017 | perror(L"Image has multiple relocation sections\n"); | |
1018 | return EFI_UNSUPPORTED; | |
1019 | } | |
1020 | /* If it has nonzero sizes, and our bounds check | |
1021 | * made sense, and the VA and size match RelocDir's | |
1022 | * versions, then we believe in this section table. */ | |
1023 | if (Section->SizeOfRawData && | |
1024 | Section->Misc.VirtualSize && | |
1025 | base && end && | |
1026 | RelocBase == base && | |
1027 | RelocBaseEnd == end) { | |
1028 | RelocSection = Section; | |
1029 | } | |
1030 | } else if (CompareMem(Section->Name, ".sbat\0\0\0", 8) == 0) { | |
1031 | if (SBATBase || SBATSize) { | |
1032 | perror(L"Image has multiple SBAT sections\n"); | |
1033 | return EFI_UNSUPPORTED; | |
1034 | } | |
1035 | ||
1036 | if (Section->NumberOfRelocations != 0 || | |
1037 | Section->PointerToRelocations != 0) { | |
1038 | perror(L"SBAT section has relocations\n"); | |
1039 | return EFI_UNSUPPORTED; | |
1040 | } | |
1041 | ||
1042 | /* The virtual size corresponds to the size of the SBAT | |
1043 | * metadata and isn't necessarily a multiple of the file | |
1044 | * alignment. The on-disk size is a multiple of the file | |
1045 | * alignment and is zero padded. Make sure that the | |
1046 | * on-disk size is at least as large as virtual size, | |
1047 | * and ignore the section if it isn't. */ | |
1048 | if (Section->SizeOfRawData && | |
1049 | Section->SizeOfRawData >= Section->Misc.VirtualSize && | |
1050 | base && end) { | |
1051 | SBATBase = base; | |
1052 | /* +1 because of size vs last byte location */ | |
1053 | SBATSize = end - base + 1; | |
8119f718 SM |
1054 | dprint(L"sbat section base:0x%lx size:0x%lx\n", |
1055 | SBATBase, SBATSize); | |
031e5cce SM |
1056 | } |
1057 | } | |
1058 | ||
1059 | if (Section->Characteristics & EFI_IMAGE_SCN_MEM_DISCARDABLE) { | |
1060 | continue; | |
1061 | } | |
1062 | ||
1063 | if (!base) { | |
1064 | perror(L"Section %d has invalid base address\n", i); | |
1065 | return EFI_UNSUPPORTED; | |
1066 | } | |
1067 | if (!end) { | |
1068 | perror(L"Section %d has zero size\n", i); | |
1069 | return EFI_UNSUPPORTED; | |
1070 | } | |
1071 | ||
1072 | if (!(Section->Characteristics & EFI_IMAGE_SCN_CNT_UNINITIALIZED_DATA) && | |
1073 | (Section->VirtualAddress < context.SizeOfHeaders || | |
1074 | Section->PointerToRawData < context.SizeOfHeaders)) { | |
1075 | perror(L"Section %d is inside image headers\n", i); | |
1076 | return EFI_UNSUPPORTED; | |
1077 | } | |
1078 | ||
1079 | if (Section->Characteristics & EFI_IMAGE_SCN_CNT_UNINITIALIZED_DATA) { | |
1080 | ZeroMem(base, Section->Misc.VirtualSize); | |
1081 | } else { | |
1082 | if (Section->PointerToRawData < context.SizeOfHeaders) { | |
1083 | perror(L"Section %d is inside image headers\n", i); | |
1084 | return EFI_UNSUPPORTED; | |
1085 | } | |
1086 | ||
1087 | if (Section->SizeOfRawData > 0) | |
1088 | CopyMem(base, data + Section->PointerToRawData, | |
1089 | Section->SizeOfRawData); | |
1090 | ||
1091 | if (Section->SizeOfRawData < Section->Misc.VirtualSize) | |
1092 | ZeroMem(base + Section->SizeOfRawData, | |
1093 | Section->Misc.VirtualSize - Section->SizeOfRawData); | |
1094 | } | |
1095 | } | |
1096 | ||
1097 | if (secure_mode ()) { | |
1098 | efi_status = handle_sbat(SBATBase, SBATSize); | |
1099 | ||
1100 | if (!EFI_ERROR(efi_status)) | |
1101 | efi_status = verify_buffer(data, datasize, | |
1102 | &context, sha256hash, sha1hash); | |
1103 | ||
1104 | if (EFI_ERROR(efi_status)) { | |
1105 | if (verbose) | |
1106 | console_print(L"Verification failed: %r\n", efi_status); | |
1107 | else | |
1108 | console_error(L"Verification failed", efi_status); | |
1109 | return efi_status; | |
1110 | } else { | |
1111 | if (verbose) | |
1112 | console_print(L"Verification succeeded\n"); | |
1113 | } | |
1114 | } | |
1115 | ||
1116 | if (context.NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC) { | |
1117 | perror(L"Image has no relocation entry\n"); | |
1118 | FreePool(buffer); | |
1119 | return EFI_UNSUPPORTED; | |
1120 | } | |
1121 | ||
1122 | if (context.RelocDir->Size && RelocSection) { | |
1123 | /* | |
1124 | * Run the relocation fixups | |
1125 | */ | |
1126 | efi_status = relocate_coff(&context, RelocSection, data, | |
1127 | buffer); | |
1128 | ||
1129 | if (EFI_ERROR(efi_status)) { | |
1130 | perror(L"Relocation failed: %r\n", efi_status); | |
1131 | FreePool(buffer); | |
1132 | return efi_status; | |
1133 | } | |
1134 | } | |
1135 | ||
1136 | /* | |
1137 | * grub needs to know its location and size in memory, so fix up | |
1138 | * the loaded image protocol values | |
1139 | */ | |
1140 | li->ImageBase = buffer; | |
1141 | li->ImageSize = context.ImageSize; | |
1142 | ||
1143 | /* Pass the load options to the second stage loader */ | |
8529e0f7 SM |
1144 | li->LoadOptions = load_options; |
1145 | li->LoadOptionsSize = load_options_size; | |
031e5cce SM |
1146 | |
1147 | if (!found_entry_point) { | |
1148 | perror(L"Entry point is not within sections\n"); | |
1149 | return EFI_UNSUPPORTED; | |
1150 | } | |
1151 | if (found_entry_point > 1) { | |
8529e0f7 | 1152 | perror(L"%d sections contain entry point\n", found_entry_point); |
031e5cce SM |
1153 | return EFI_UNSUPPORTED; |
1154 | } | |
1155 | ||
1156 | return EFI_SUCCESS; | |
1157 | } | |
1158 | ||
1159 | // vim:fenc=utf-8:tw=75:noet |