]>
Commit | Line | Data |
---|---|---|
f898777d MG |
1 | /* |
2 | * shim - trivial UEFI first-stage bootloader | |
3 | * | |
4 | * Copyright 2012 Red Hat, Inc <mjg@redhat.com> | |
5 | * | |
6 | * Redistribution and use in source and binary forms, with or without | |
7 | * modification, are permitted provided that the following conditions | |
8 | * are met: | |
9 | * | |
10 | * Redistributions of source code must retain the above copyright | |
11 | * notice, this list of conditions and the following disclaimer. | |
12 | * | |
13 | * Redistributions in binary form must reproduce the above copyright | |
14 | * notice, this list of conditions and the following disclaimer in the | |
15 | * documentation and/or other materials provided with the | |
16 | * distribution. | |
17 | * | |
18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
19 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
20 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | |
21 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | |
22 | * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, | |
23 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
24 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |
25 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | |
27 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED | |
29 | * OF THE POSSIBILITY OF SUCH DAMAGE. | |
30 | * | |
31 | * Significant portions of this code are derived from Tianocore | |
32 | * (http://tianocore.sf.net) and are Copyright 2009-2012 Intel | |
33 | * Corporation. | |
34 | */ | |
35 | ||
b2fe1780 MG |
36 | #include <efi.h> |
37 | #include <efilib.h> | |
7f055335 | 38 | #include <Library/BaseCryptLib.h> |
b2fe1780 | 39 | #include "PeImage.h" |
f4b24734 | 40 | #include "shim.h" |
3df68c18 | 41 | #include "signature.h" |
1c595706 | 42 | #include "netboot.h" |
ef8c9962 | 43 | #include "shim_cert.h" |
6e1bd3dc | 44 | #include "ucs2.h" |
b2fe1780 | 45 | |
09e2c939 | 46 | #define DEFAULT_LOADER L"\\grub.efi" |
6d6b0221 | 47 | #define FALLBACK L"\\fallback.efi" |
4b34567d | 48 | #define MOK_MANAGER L"\\MokManager.efi" |
f898777d | 49 | |
7f055335 MG |
50 | static EFI_SYSTEM_TABLE *systab; |
51 | static EFI_STATUS (EFIAPI *entry_point) (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table); | |
52 | ||
09e2c939 GCPL |
53 | static CHAR16 *second_stage; |
54 | static void *load_options; | |
55 | static UINT32 load_options_size; | |
e75294e5 | 56 | static UINT8 verbose; |
09e2c939 | 57 | |
f898777d MG |
58 | /* |
59 | * The vendor certificate used for validating the second stage loader | |
60 | */ | |
8518b8cc PJ |
61 | extern UINT8 vendor_cert[]; |
62 | extern UINT32 vendor_cert_size; | |
92888645 | 63 | extern UINT8 vendor_dbx[]; |
5f0a358b | 64 | extern UINT32 vendor_dbx_size; |
b2fe1780 | 65 | |
c13fc2f7 MG |
66 | #define EFI_IMAGE_SECURITY_DATABASE_GUID { 0xd719b2cb, 0x3d3a, 0x4596, { 0xa3, 0xbc, 0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f }} |
67 | ||
9eaadb0d MG |
68 | static UINT8 insecure_mode; |
69 | ||
c16548d0 MG |
70 | typedef enum { |
71 | DATA_FOUND, | |
72 | DATA_NOT_FOUND, | |
73 | VAR_NOT_FOUND | |
74 | } CHECK_STATUS; | |
75 | ||
13422973 GCPL |
76 | typedef struct { |
77 | UINT32 MokSize; | |
78 | UINT8 *Mok; | |
79 | } MokListNode; | |
80 | ||
31d3bd05 | 81 | static EFI_STATUS get_variable (CHAR16 *name, EFI_GUID guid, UINT32 *attributes, |
3df68c18 | 82 | UINTN *size, void **buffer) |
5bc80cec MG |
83 | { |
84 | EFI_STATUS efi_status; | |
c13fc2f7 | 85 | char allocate = !(*size); |
5bc80cec MG |
86 | |
87 | efi_status = uefi_call_wrapper(RT->GetVariable, 5, name, &guid, | |
31d3bd05 | 88 | attributes, size, buffer); |
5bc80cec | 89 | |
c13fc2f7 | 90 | if (efi_status != EFI_BUFFER_TOO_SMALL || !allocate) { |
5bc80cec | 91 | return efi_status; |
c13fc2f7 | 92 | } |
5bc80cec | 93 | |
cbe21407 | 94 | *buffer = AllocatePool(*size); |
5bc80cec MG |
95 | |
96 | if (!*buffer) { | |
97 | Print(L"Unable to allocate variable buffer\n"); | |
98 | return EFI_OUT_OF_RESOURCES; | |
99 | } | |
100 | ||
101 | efi_status = uefi_call_wrapper(RT->GetVariable, 5, name, &guid, | |
31d3bd05 | 102 | attributes, size, *buffer); |
5bc80cec MG |
103 | |
104 | return efi_status; | |
105 | } | |
106 | ||
f898777d MG |
107 | /* |
108 | * Perform basic bounds checking of the intra-image pointers | |
109 | */ | |
110 | static void *ImageAddress (void *image, int size, unsigned int address) | |
b2fe1780 | 111 | { |
f898777d MG |
112 | if (address > size) |
113 | return NULL; | |
9d56c38f | 114 | |
f898777d MG |
115 | return image + address; |
116 | } | |
9d56c38f | 117 | |
f898777d MG |
118 | /* |
119 | * Perform the actual relocation | |
120 | */ | |
7db60bd8 MG |
121 | static EFI_STATUS relocate_coff (PE_COFF_LOADER_IMAGE_CONTEXT *context, |
122 | void *data) | |
f898777d MG |
123 | { |
124 | EFI_IMAGE_BASE_RELOCATION *RelocBase, *RelocBaseEnd; | |
125 | UINT64 Adjust; | |
126 | UINT16 *Reloc, *RelocEnd; | |
127 | char *Fixup, *FixupBase, *FixupData = NULL; | |
128 | UINT16 *Fixup16; | |
129 | UINT32 *Fixup32; | |
130 | UINT64 *Fixup64; | |
131 | int size = context->ImageSize; | |
7db60bd8 | 132 | void *ImageEnd = (char *)data + size; |
9d56c38f | 133 | |
7db60bd8 | 134 | context->PEHdr->Pe32Plus.OptionalHeader.ImageBase = (UINT64)data; |
7f055335 | 135 | |
f898777d MG |
136 | if (context->NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC) { |
137 | Print(L"Image has no relocation entry\n"); | |
138 | return EFI_UNSUPPORTED; | |
7f055335 MG |
139 | } |
140 | ||
7db60bd8 MG |
141 | RelocBase = ImageAddress(data, size, context->RelocDir->VirtualAddress); |
142 | RelocBaseEnd = ImageAddress(data, size, context->RelocDir->VirtualAddress + context->RelocDir->Size - 1); | |
9d56c38f | 143 | |
f898777d MG |
144 | if (!RelocBase || !RelocBaseEnd) { |
145 | Print(L"Reloc table overflows binary\n"); | |
146 | return EFI_UNSUPPORTED; | |
9d56c38f MG |
147 | } |
148 | ||
7db60bd8 | 149 | Adjust = (UINT64)data - context->ImageAddress; |
9d56c38f | 150 | |
f898777d MG |
151 | while (RelocBase < RelocBaseEnd) { |
152 | Reloc = (UINT16 *) ((char *) RelocBase + sizeof (EFI_IMAGE_BASE_RELOCATION)); | |
153 | RelocEnd = (UINT16 *) ((char *) RelocBase + RelocBase->SizeOfBlock); | |
9d56c38f | 154 | |
7db60bd8 | 155 | if ((void *)RelocEnd < data || (void *)RelocEnd > ImageEnd) { |
f898777d MG |
156 | Print(L"Reloc entry overflows binary\n"); |
157 | return EFI_UNSUPPORTED; | |
9d56c38f MG |
158 | } |
159 | ||
7db60bd8 | 160 | FixupBase = ImageAddress(data, size, RelocBase->VirtualAddress); |
f898777d MG |
161 | if (!FixupBase) { |
162 | Print(L"Invalid fixupbase\n"); | |
163 | return EFI_UNSUPPORTED; | |
9d56c38f | 164 | } |
9d56c38f | 165 | |
f898777d MG |
166 | while (Reloc < RelocEnd) { |
167 | Fixup = FixupBase + (*Reloc & 0xFFF); | |
168 | switch ((*Reloc) >> 12) { | |
169 | case EFI_IMAGE_REL_BASED_ABSOLUTE: | |
170 | break; | |
9d56c38f | 171 | |
f898777d MG |
172 | case EFI_IMAGE_REL_BASED_HIGH: |
173 | Fixup16 = (UINT16 *) Fixup; | |
174 | *Fixup16 = (UINT16) (*Fixup16 + ((UINT16) ((UINT32) Adjust >> 16))); | |
175 | if (FixupData != NULL) { | |
176 | *(UINT16 *) FixupData = *Fixup16; | |
177 | FixupData = FixupData + sizeof (UINT16); | |
178 | } | |
179 | break; | |
9d56c38f | 180 | |
f898777d MG |
181 | case EFI_IMAGE_REL_BASED_LOW: |
182 | Fixup16 = (UINT16 *) Fixup; | |
183 | *Fixup16 = (UINT16) (*Fixup16 + (UINT16) Adjust); | |
184 | if (FixupData != NULL) { | |
185 | *(UINT16 *) FixupData = *Fixup16; | |
186 | FixupData = FixupData + sizeof (UINT16); | |
187 | } | |
188 | break; | |
b2fe1780 | 189 | |
f898777d MG |
190 | case EFI_IMAGE_REL_BASED_HIGHLOW: |
191 | Fixup32 = (UINT32 *) Fixup; | |
192 | *Fixup32 = *Fixup32 + (UINT32) Adjust; | |
193 | if (FixupData != NULL) { | |
194 | FixupData = ALIGN_POINTER (FixupData, sizeof (UINT32)); | |
195 | *(UINT32 *)FixupData = *Fixup32; | |
196 | FixupData = FixupData + sizeof (UINT32); | |
197 | } | |
198 | break; | |
b2fe1780 | 199 | |
f898777d MG |
200 | case EFI_IMAGE_REL_BASED_DIR64: |
201 | Fixup64 = (UINT64 *) Fixup; | |
202 | *Fixup64 = *Fixup64 + (UINT64) Adjust; | |
203 | if (FixupData != NULL) { | |
204 | FixupData = ALIGN_POINTER (FixupData, sizeof(UINT64)); | |
205 | *(UINT64 *)(FixupData) = *Fixup64; | |
206 | FixupData = FixupData + sizeof(UINT64); | |
207 | } | |
208 | break; | |
b2fe1780 | 209 | |
f898777d MG |
210 | default: |
211 | Print(L"Unknown relocation\n"); | |
212 | return EFI_UNSUPPORTED; | |
213 | } | |
214 | Reloc += 1; | |
0e6b0195 | 215 | } |
f898777d | 216 | RelocBase = (EFI_IMAGE_BASE_RELOCATION *) RelocEnd; |
7f055335 | 217 | } |
b2fe1780 MG |
218 | |
219 | return EFI_SUCCESS; | |
220 | } | |
221 | ||
5f0a358b PJ |
222 | static CHECK_STATUS check_db_cert_in_ram(EFI_SIGNATURE_LIST *CertList, |
223 | UINTN dbsize, | |
224 | WIN_CERTIFICATE_EFI_PKCS *data, | |
225 | UINT8 *hash) | |
3df68c18 | 226 | { |
3df68c18 | 227 | EFI_SIGNATURE_DATA *Cert; |
c16548d0 | 228 | UINTN CertCount, Index; |
c13fc2f7 | 229 | BOOLEAN IsFound = FALSE; |
c16548d0 MG |
230 | EFI_GUID CertType = EfiCertX509Guid; |
231 | ||
c16548d0 | 232 | while ((dbsize > 0) && (dbsize >= CertList->SignatureListSize)) { |
c13fc2f7 | 233 | if (CompareGuid (&CertList->SignatureType, &CertType) == 0) { |
c16548d0 MG |
234 | CertCount = (CertList->SignatureListSize - CertList->SignatureHeaderSize) / CertList->SignatureSize; |
235 | Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize); | |
236 | for (Index = 0; Index < CertCount; Index++) { | |
237 | IsFound = AuthenticodeVerify (data->CertData, | |
238 | data->Hdr.dwLength - sizeof(data->Hdr), | |
239 | Cert->SignatureData, | |
240 | CertList->SignatureSize, | |
241 | hash, SHA256_DIGEST_SIZE); | |
7430b901 PJ |
242 | if (IsFound) |
243 | break; | |
201574d1 MG |
244 | |
245 | Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) Cert + CertList->SignatureSize); | |
c16548d0 MG |
246 | } |
247 | ||
c16548d0 MG |
248 | } |
249 | ||
058c0368 MG |
250 | if (IsFound) |
251 | break; | |
252 | ||
c16548d0 MG |
253 | dbsize -= CertList->SignatureListSize; |
254 | CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize); | |
255 | } | |
256 | ||
c16548d0 MG |
257 | if (IsFound) |
258 | return DATA_FOUND; | |
259 | ||
260 | return DATA_NOT_FOUND; | |
261 | } | |
262 | ||
5f0a358b PJ |
263 | static CHECK_STATUS check_db_cert(CHAR16 *dbname, EFI_GUID guid, |
264 | WIN_CERTIFICATE_EFI_PKCS *data, UINT8 *hash) | |
c16548d0 | 265 | { |
5f0a358b | 266 | CHECK_STATUS rc; |
c16548d0 | 267 | EFI_STATUS efi_status; |
c16548d0 | 268 | EFI_SIGNATURE_LIST *CertList; |
c16548d0 | 269 | UINTN dbsize = 0; |
31d3bd05 | 270 | UINT32 attributes; |
3df68c18 | 271 | void *db; |
3df68c18 | 272 | |
0a6565c5 | 273 | efi_status = get_variable(dbname, guid, &attributes, &dbsize, &db); |
3df68c18 | 274 | |
5f0a358b | 275 | if (efi_status != EFI_SUCCESS) |
c16548d0 | 276 | return VAR_NOT_FOUND; |
3df68c18 MG |
277 | |
278 | CertList = db; | |
279 | ||
5f0a358b PJ |
280 | rc = check_db_cert_in_ram(CertList, dbsize, data, hash); |
281 | ||
282 | FreePool(db); | |
283 | ||
284 | return rc; | |
285 | } | |
286 | ||
20f6cde6 MG |
287 | /* |
288 | * Check a hash against an EFI_SIGNATURE_LIST in a buffer | |
289 | */ | |
5f0a358b PJ |
290 | static CHECK_STATUS check_db_hash_in_ram(EFI_SIGNATURE_LIST *CertList, |
291 | UINTN dbsize, UINT8 *data, | |
292 | int SignatureSize, EFI_GUID CertType) | |
293 | { | |
294 | EFI_SIGNATURE_DATA *Cert; | |
295 | UINTN CertCount, Index; | |
296 | BOOLEAN IsFound = FALSE; | |
297 | ||
c16548d0 | 298 | while ((dbsize > 0) && (dbsize >= CertList->SignatureListSize)) { |
3df68c18 MG |
299 | CertCount = (CertList->SignatureListSize - CertList->SignatureHeaderSize) / CertList->SignatureSize; |
300 | Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize); | |
c13fc2f7 | 301 | if (CompareGuid(&CertList->SignatureType, &CertType) == 0) { |
3df68c18 | 302 | for (Index = 0; Index < CertCount; Index++) { |
c16548d0 | 303 | if (CompareMem (Cert->SignatureData, data, SignatureSize) == 0) { |
3df68c18 MG |
304 | // |
305 | // Find the signature in database. | |
306 | // | |
307 | IsFound = TRUE; | |
308 | break; | |
309 | } | |
310 | ||
311 | Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) Cert + CertList->SignatureSize); | |
312 | } | |
313 | if (IsFound) { | |
314 | break; | |
315 | } | |
316 | } | |
317 | ||
c16548d0 | 318 | dbsize -= CertList->SignatureListSize; |
3df68c18 MG |
319 | CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize); |
320 | } | |
321 | ||
3df68c18 | 322 | if (IsFound) |
c16548d0 MG |
323 | return DATA_FOUND; |
324 | ||
325 | return DATA_NOT_FOUND; | |
326 | } | |
327 | ||
20f6cde6 MG |
328 | /* |
329 | * Check a hash against an EFI_SIGNATURE_LIST in a UEFI variable | |
330 | */ | |
5f0a358b PJ |
331 | static CHECK_STATUS check_db_hash(CHAR16 *dbname, EFI_GUID guid, UINT8 *data, |
332 | int SignatureSize, EFI_GUID CertType) | |
333 | { | |
334 | EFI_STATUS efi_status; | |
335 | EFI_SIGNATURE_LIST *CertList; | |
336 | UINT32 attributes; | |
337 | UINTN dbsize = 0; | |
338 | void *db; | |
339 | ||
340 | efi_status = get_variable(dbname, guid, &attributes, &dbsize, &db); | |
341 | ||
342 | if (efi_status != EFI_SUCCESS) { | |
343 | return VAR_NOT_FOUND; | |
344 | } | |
345 | ||
346 | CertList = db; | |
347 | ||
348 | CHECK_STATUS rc = check_db_hash_in_ram(CertList, dbsize, data, | |
349 | SignatureSize, CertType); | |
350 | FreePool(db); | |
351 | return rc; | |
352 | ||
353 | } | |
354 | ||
20f6cde6 MG |
355 | /* |
356 | * Check whether the binary signature or hash are present in dbx or the | |
357 | * built-in blacklist | |
358 | */ | |
ce6a5748 MG |
359 | static EFI_STATUS check_blacklist (WIN_CERTIFICATE_EFI_PKCS *cert, |
360 | UINT8 *sha256hash, UINT8 *sha1hash) | |
c16548d0 | 361 | { |
0a6565c5 | 362 | EFI_GUID secure_var = EFI_IMAGE_SECURITY_DATABASE_GUID; |
92888645 | 363 | EFI_SIGNATURE_LIST *dbx = (EFI_SIGNATURE_LIST *)vendor_dbx; |
0a6565c5 | 364 | |
92888645 | 365 | if (check_db_hash_in_ram(dbx, vendor_dbx_size, sha256hash, |
5f0a358b | 366 | SHA256_DIGEST_SIZE, EfiHashSha256Guid) == |
8b7685b2 | 367 | DATA_FOUND) |
5f0a358b | 368 | return EFI_ACCESS_DENIED; |
92888645 | 369 | if (check_db_hash_in_ram(dbx, vendor_dbx_size, sha1hash, |
5f0a358b | 370 | SHA1_DIGEST_SIZE, EfiHashSha1Guid) == |
8b7685b2 | 371 | DATA_FOUND) |
5f0a358b | 372 | return EFI_ACCESS_DENIED; |
92888645 | 373 | if (check_db_cert_in_ram(dbx, vendor_dbx_size, cert, |
8b7685b2 | 374 | sha256hash) == DATA_FOUND) |
5f0a358b PJ |
375 | return EFI_ACCESS_DENIED; |
376 | ||
0a6565c5 | 377 | if (check_db_hash(L"dbx", secure_var, sha256hash, SHA256_DIGEST_SIZE, |
ce6a5748 | 378 | EfiHashSha256Guid) == DATA_FOUND) |
c16548d0 | 379 | return EFI_ACCESS_DENIED; |
0a6565c5 | 380 | if (check_db_hash(L"dbx", secure_var, sha1hash, SHA1_DIGEST_SIZE, |
ce6a5748 MG |
381 | EfiHashSha1Guid) == DATA_FOUND) |
382 | return EFI_ACCESS_DENIED; | |
0a6565c5 | 383 | if (check_db_cert(L"dbx", secure_var, cert, sha256hash) == DATA_FOUND) |
3df68c18 MG |
384 | return EFI_ACCESS_DENIED; |
385 | ||
386 | return EFI_SUCCESS; | |
387 | } | |
388 | ||
20f6cde6 MG |
389 | /* |
390 | * Check whether the binary signature or hash are present in db or MokList | |
391 | */ | |
ce6a5748 MG |
392 | static EFI_STATUS check_whitelist (WIN_CERTIFICATE_EFI_PKCS *cert, |
393 | UINT8 *sha256hash, UINT8 *sha1hash) | |
b2058cf8 | 394 | { |
0a6565c5 MG |
395 | EFI_GUID secure_var = EFI_IMAGE_SECURITY_DATABASE_GUID; |
396 | EFI_GUID shim_var = SHIM_LOCK_GUID; | |
397 | ||
398 | if (check_db_hash(L"db", secure_var, sha256hash, SHA256_DIGEST_SIZE, | |
ce6a5748 MG |
399 | EfiHashSha256Guid) == DATA_FOUND) |
400 | return EFI_SUCCESS; | |
0a6565c5 | 401 | if (check_db_hash(L"db", secure_var, sha1hash, SHA1_DIGEST_SIZE, |
ce6a5748 | 402 | EfiHashSha1Guid) == DATA_FOUND) |
b2058cf8 | 403 | return EFI_SUCCESS; |
0a6565c5 MG |
404 | if (check_db_hash(L"MokList", shim_var, sha256hash, SHA256_DIGEST_SIZE, |
405 | EfiHashSha256Guid) == DATA_FOUND) | |
406 | return EFI_SUCCESS; | |
407 | if (check_db_cert(L"db", secure_var, cert, sha256hash) == DATA_FOUND) | |
408 | return EFI_SUCCESS; | |
409 | if (check_db_cert(L"MokList", shim_var, cert, sha256hash) == DATA_FOUND) | |
b2058cf8 MG |
410 | return EFI_SUCCESS; |
411 | ||
412 | return EFI_ACCESS_DENIED; | |
413 | } | |
414 | ||
6279b58e MG |
415 | /* |
416 | * Check whether we're in Secure Boot and user mode | |
417 | */ | |
418 | ||
419 | static BOOLEAN secure_mode (void) | |
420 | { | |
421 | EFI_STATUS status; | |
422 | EFI_GUID global_var = EFI_GLOBAL_VARIABLE; | |
423 | UINTN charsize = sizeof(char); | |
424 | UINT8 sb, setupmode; | |
31d3bd05 | 425 | UINT32 attributes; |
6279b58e | 426 | |
9eaadb0d MG |
427 | if (insecure_mode) |
428 | return FALSE; | |
429 | ||
31d3bd05 GCPL |
430 | status = get_variable(L"SecureBoot", global_var, &attributes, &charsize, |
431 | (void *)&sb); | |
6279b58e MG |
432 | |
433 | /* FIXME - more paranoia here? */ | |
434 | if (status != EFI_SUCCESS || sb != 1) { | |
e75294e5 PJ |
435 | if (verbose) |
436 | Print(L"Secure boot not enabled\n"); | |
6279b58e MG |
437 | return FALSE; |
438 | } | |
439 | ||
31d3bd05 GCPL |
440 | status = get_variable(L"SetupMode", global_var, &attributes, &charsize, |
441 | (void *)&setupmode); | |
6279b58e MG |
442 | |
443 | if (status == EFI_SUCCESS && setupmode == 1) { | |
e75294e5 PJ |
444 | if (verbose) |
445 | Print(L"Platform is in setup mode\n"); | |
6279b58e MG |
446 | return FALSE; |
447 | } | |
448 | ||
449 | return TRUE; | |
450 | } | |
451 | ||
f898777d | 452 | /* |
f394b22e | 453 | * Calculate the SHA1 and SHA256 hashes of a binary |
f898777d | 454 | */ |
f394b22e MG |
455 | |
456 | static EFI_STATUS generate_hash (char *data, int datasize, | |
457 | PE_COFF_LOADER_IMAGE_CONTEXT *context, | |
458 | UINT8 *sha256hash, UINT8 *sha1hash) | |
459 | ||
7f055335 | 460 | { |
ce6a5748 | 461 | unsigned int sha256ctxsize, sha1ctxsize; |
f394b22e | 462 | unsigned int size = datasize; |
ce6a5748 | 463 | void *sha256ctx = NULL, *sha1ctx = NULL; |
7f055335 MG |
464 | char *hashbase; |
465 | unsigned int hashsize; | |
7f055335 | 466 | unsigned int SumOfBytesHashed, SumOfSectionBytes; |
3df68c18 | 467 | unsigned int index, pos; |
7f055335 | 468 | EFI_IMAGE_SECTION_HEADER *Section; |
0db1af8a | 469 | EFI_IMAGE_SECTION_HEADER *SectionHeader = NULL; |
7f055335 | 470 | EFI_IMAGE_SECTION_HEADER *SectionCache; |
f394b22e | 471 | EFI_STATUS status = EFI_SUCCESS; |
7f055335 | 472 | |
ce6a5748 MG |
473 | sha256ctxsize = Sha256GetContextSize(); |
474 | sha256ctx = AllocatePool(sha256ctxsize); | |
475 | ||
476 | sha1ctxsize = Sha1GetContextSize(); | |
477 | sha1ctx = AllocatePool(sha1ctxsize); | |
7f055335 | 478 | |
ce6a5748 | 479 | if (!sha256ctx || !sha1ctx) { |
7f055335 MG |
480 | Print(L"Unable to allocate memory for hash context\n"); |
481 | return EFI_OUT_OF_RESOURCES; | |
482 | } | |
483 | ||
ce6a5748 | 484 | if (!Sha256Init(sha256ctx) || !Sha1Init(sha1ctx)) { |
7f055335 MG |
485 | Print(L"Unable to initialise hash\n"); |
486 | status = EFI_OUT_OF_RESOURCES; | |
487 | goto done; | |
488 | } | |
489 | ||
490 | /* Hash start to checksum */ | |
7db60bd8 | 491 | hashbase = data; |
7f055335 MG |
492 | hashsize = (char *)&context->PEHdr->Pe32.OptionalHeader.CheckSum - |
493 | hashbase; | |
494 | ||
ce6a5748 MG |
495 | if (!(Sha256Update(sha256ctx, hashbase, hashsize)) || |
496 | !(Sha1Update(sha1ctx, hashbase, hashsize))) { | |
7f055335 MG |
497 | Print(L"Unable to generate hash\n"); |
498 | status = EFI_OUT_OF_RESOURCES; | |
499 | goto done; | |
500 | } | |
501 | ||
502 | /* Hash post-checksum to start of certificate table */ | |
503 | hashbase = (char *)&context->PEHdr->Pe32.OptionalHeader.CheckSum + | |
504 | sizeof (int); | |
505 | hashsize = (char *)context->SecDir - hashbase; | |
506 | ||
ce6a5748 MG |
507 | if (!(Sha256Update(sha256ctx, hashbase, hashsize)) || |
508 | !(Sha1Update(sha1ctx, hashbase, hashsize))) { | |
7f055335 MG |
509 | Print(L"Unable to generate hash\n"); |
510 | status = EFI_OUT_OF_RESOURCES; | |
511 | goto done; | |
512 | } | |
513 | ||
514 | /* Hash end of certificate table to end of image header */ | |
515 | hashbase = (char *) &context->PEHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1]; | |
516 | hashsize = context->PEHdr->Pe32Plus.OptionalHeader.SizeOfHeaders - | |
7db60bd8 | 517 | (int) ((char *) (&context->PEHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1]) - data); |
0db1af8a | 518 | |
ce6a5748 MG |
519 | if (!(Sha256Update(sha256ctx, hashbase, hashsize)) || |
520 | !(Sha1Update(sha1ctx, hashbase, hashsize))) { | |
7f055335 MG |
521 | Print(L"Unable to generate hash\n"); |
522 | status = EFI_OUT_OF_RESOURCES; | |
523 | goto done; | |
524 | } | |
525 | ||
f898777d | 526 | /* Sort sections */ |
7f055335 MG |
527 | SumOfBytesHashed = context->PEHdr->Pe32Plus.OptionalHeader.SizeOfHeaders; |
528 | ||
529 | Section = (EFI_IMAGE_SECTION_HEADER *) ( | |
530 | (char *)context->PEHdr + sizeof (UINT32) + | |
531 | sizeof (EFI_IMAGE_FILE_HEADER) + | |
532 | context->PEHdr->Pe32.FileHeader.SizeOfOptionalHeader | |
533 | ); | |
534 | ||
535 | SectionCache = Section; | |
536 | ||
537 | for (index = 0, SumOfSectionBytes = 0; index < context->PEHdr->Pe32.FileHeader.NumberOfSections; index++, SectionCache++) { | |
538 | SumOfSectionBytes += SectionCache->SizeOfRawData; | |
539 | } | |
540 | ||
7db60bd8 | 541 | if (SumOfSectionBytes >= datasize) { |
7f055335 MG |
542 | Print(L"Malformed binary: %x %x\n", SumOfSectionBytes, size); |
543 | status = EFI_INVALID_PARAMETER; | |
544 | goto done; | |
545 | } | |
546 | ||
547 | SectionHeader = (EFI_IMAGE_SECTION_HEADER *) AllocateZeroPool (sizeof (EFI_IMAGE_SECTION_HEADER) * context->PEHdr->Pe32.FileHeader.NumberOfSections); | |
548 | if (SectionHeader == NULL) { | |
549 | Print(L"Unable to allocate section header\n"); | |
550 | status = EFI_OUT_OF_RESOURCES; | |
551 | goto done; | |
552 | } | |
553 | ||
554 | /* Sort the section headers */ | |
555 | for (index = 0; index < context->PEHdr->Pe32.FileHeader.NumberOfSections; index++) { | |
556 | pos = index; | |
557 | while ((pos > 0) && (Section->PointerToRawData < SectionHeader[pos - 1].PointerToRawData)) { | |
558 | CopyMem (&SectionHeader[pos], &SectionHeader[pos - 1], sizeof (EFI_IMAGE_SECTION_HEADER)); | |
559 | pos--; | |
560 | } | |
561 | CopyMem (&SectionHeader[pos], Section, sizeof (EFI_IMAGE_SECTION_HEADER)); | |
562 | Section += 1; | |
563 | } | |
564 | ||
565 | /* Hash the sections */ | |
566 | for (index = 0; index < context->PEHdr->Pe32.FileHeader.NumberOfSections; index++) { | |
567 | Section = &SectionHeader[index]; | |
568 | if (Section->SizeOfRawData == 0) { | |
569 | continue; | |
570 | } | |
7db60bd8 | 571 | hashbase = ImageAddress(data, size, Section->PointerToRawData); |
7f055335 MG |
572 | hashsize = (unsigned int) Section->SizeOfRawData; |
573 | ||
f898777d MG |
574 | if (!hashbase) { |
575 | Print(L"Malformed section header\n"); | |
cbe21407 MG |
576 | status = EFI_INVALID_PARAMETER; |
577 | goto done; | |
f898777d MG |
578 | } |
579 | ||
ce6a5748 MG |
580 | if (!(Sha256Update(sha256ctx, hashbase, hashsize)) || |
581 | !(Sha1Update(sha1ctx, hashbase, hashsize))) { | |
7f055335 MG |
582 | Print(L"Unable to generate hash\n"); |
583 | status = EFI_OUT_OF_RESOURCES; | |
584 | goto done; | |
585 | } | |
586 | SumOfBytesHashed += Section->SizeOfRawData; | |
587 | } | |
588 | ||
589 | /* Hash all remaining data */ | |
590 | if (size > SumOfBytesHashed) { | |
7db60bd8 | 591 | hashbase = data + SumOfBytesHashed; |
7f055335 MG |
592 | hashsize = (unsigned int)( |
593 | size - | |
594 | context->PEHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size - | |
595 | SumOfBytesHashed); | |
596 | ||
ce6a5748 MG |
597 | if (!(Sha256Update(sha256ctx, hashbase, hashsize)) || |
598 | !(Sha1Update(sha1ctx, hashbase, hashsize))) { | |
7f055335 MG |
599 | Print(L"Unable to generate hash\n"); |
600 | status = EFI_OUT_OF_RESOURCES; | |
601 | goto done; | |
602 | } | |
603 | } | |
604 | ||
ce6a5748 MG |
605 | if (!(Sha256Final(sha256ctx, sha256hash)) || |
606 | !(Sha1Final(sha1ctx, sha1hash))) { | |
7f055335 MG |
607 | Print(L"Unable to finalise hash\n"); |
608 | status = EFI_OUT_OF_RESOURCES; | |
609 | goto done; | |
610 | } | |
611 | ||
f394b22e MG |
612 | done: |
613 | if (SectionHeader) | |
614 | FreePool(SectionHeader); | |
615 | if (sha1ctx) | |
616 | FreePool(sha1ctx); | |
617 | if (sha256ctx) | |
618 | FreePool(sha256ctx); | |
619 | ||
620 | return status; | |
621 | } | |
622 | ||
20f6cde6 MG |
623 | /* |
624 | * Ensure that the MOK database hasn't been set or modified from an OS | |
625 | */ | |
0a6565c5 MG |
626 | static EFI_STATUS verify_mok (void) { |
627 | EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; | |
628 | EFI_STATUS status = EFI_SUCCESS; | |
629 | void *MokListData = NULL; | |
630 | UINTN MokListDataSize = 0; | |
631 | UINT32 attributes; | |
632 | ||
633 | status = get_variable(L"MokList", shim_lock_guid, &attributes, | |
634 | &MokListDataSize, &MokListData); | |
635 | ||
636 | if (attributes & EFI_VARIABLE_RUNTIME_ACCESS) { | |
637 | Print(L"MokList is compromised!\nErase all keys in MokList!\n"); | |
638 | if (LibDeleteVariable(L"MokList", &shim_lock_guid) != EFI_SUCCESS) { | |
639 | Print(L"Failed to erase MokList\n"); | |
640 | } | |
641 | status = EFI_ACCESS_DENIED; | |
642 | return status; | |
643 | } | |
644 | ||
645 | return EFI_SUCCESS; | |
646 | } | |
647 | ||
f394b22e MG |
648 | /* |
649 | * Check that the signature is valid and matches the binary | |
650 | */ | |
651 | static EFI_STATUS verify_buffer (char *data, int datasize, | |
0a6565c5 | 652 | PE_COFF_LOADER_IMAGE_CONTEXT *context) |
f394b22e | 653 | { |
f394b22e MG |
654 | UINT8 sha256hash[SHA256_DIGEST_SIZE]; |
655 | UINT8 sha1hash[SHA1_DIGEST_SIZE]; | |
656 | EFI_STATUS status = EFI_ACCESS_DENIED; | |
657 | WIN_CERTIFICATE_EFI_PKCS *cert; | |
f394b22e MG |
658 | unsigned int size = datasize; |
659 | ||
832e5161 MG |
660 | if (context->SecDir->Size == 0) { |
661 | Print(L"Empty security header\n"); | |
662 | return EFI_INVALID_PARAMETER; | |
663 | } | |
664 | ||
f394b22e MG |
665 | cert = ImageAddress (data, size, context->SecDir->VirtualAddress); |
666 | ||
667 | if (!cert) { | |
668 | Print(L"Certificate located outside the image\n"); | |
669 | return EFI_INVALID_PARAMETER; | |
670 | } | |
671 | ||
672 | if (cert->Hdr.wCertificateType != WIN_CERT_TYPE_PKCS_SIGNED_DATA) { | |
673 | Print(L"Unsupported certificate type %x\n", | |
674 | cert->Hdr.wCertificateType); | |
675 | return EFI_UNSUPPORTED; | |
676 | } | |
677 | ||
678 | status = generate_hash(data, datasize, context, sha256hash, sha1hash); | |
679 | ||
680 | if (status != EFI_SUCCESS) | |
681 | return status; | |
682 | ||
20f6cde6 MG |
683 | /* |
684 | * Check that the MOK database hasn't been modified | |
685 | */ | |
0a6565c5 MG |
686 | verify_mok(); |
687 | ||
20f6cde6 MG |
688 | /* |
689 | * Ensure that the binary isn't blacklisted | |
690 | */ | |
ce6a5748 | 691 | status = check_blacklist(cert, sha256hash, sha1hash); |
3df68c18 MG |
692 | |
693 | if (status != EFI_SUCCESS) { | |
694 | Print(L"Binary is blacklisted\n"); | |
f394b22e | 695 | return status; |
3df68c18 MG |
696 | } |
697 | ||
20f6cde6 MG |
698 | /* |
699 | * Check whether the binary is whitelisted in any of the firmware | |
700 | * databases | |
701 | */ | |
0a6565c5 | 702 | status = check_whitelist(cert, sha256hash, sha1hash); |
b2058cf8 | 703 | |
0a6565c5 | 704 | if (status == EFI_SUCCESS) { |
e75294e5 PJ |
705 | if (verbose) |
706 | Print(L"Binary is whitelisted\n"); | |
0a6565c5 | 707 | return status; |
b2058cf8 MG |
708 | } |
709 | ||
ef8c9962 MG |
710 | /* |
711 | * Check against the shim build key | |
712 | */ | |
713 | if (AuthenticodeVerify(cert->CertData, | |
714 | context->SecDir->Size - sizeof(cert->Hdr), | |
715 | shim_cert, sizeof(shim_cert), sha256hash, | |
716 | SHA256_DIGEST_SIZE)) { | |
717 | status = EFI_SUCCESS; | |
e75294e5 PJ |
718 | if (verbose) |
719 | Print(L"Binary is verified by the vendor certificate\n"); | |
ef8c9962 MG |
720 | return status; |
721 | } | |
722 | ||
723 | ||
20f6cde6 MG |
724 | /* |
725 | * And finally, check against shim's built-in key | |
726 | */ | |
13422973 GCPL |
727 | if (AuthenticodeVerify(cert->CertData, |
728 | context->SecDir->Size - sizeof(cert->Hdr), | |
ce6a5748 | 729 | vendor_cert, vendor_cert_size, sha256hash, |
13422973 GCPL |
730 | SHA256_DIGEST_SIZE)) { |
731 | status = EFI_SUCCESS; | |
e75294e5 PJ |
732 | if (verbose) |
733 | Print(L"Binary is verified by the vendor certificate\n"); | |
f394b22e | 734 | return status; |
13422973 GCPL |
735 | } |
736 | ||
13422973 GCPL |
737 | Print(L"Invalid signature\n"); |
738 | status = EFI_ACCESS_DENIED; | |
7f055335 | 739 | |
f898777d MG |
740 | return status; |
741 | } | |
b2fe1780 | 742 | |
f898777d MG |
743 | /* |
744 | * Read the binary header and grab appropriate information from it | |
745 | */ | |
ce78d2d2 | 746 | static EFI_STATUS read_header(void *data, unsigned int datasize, |
f898777d MG |
747 | PE_COFF_LOADER_IMAGE_CONTEXT *context) |
748 | { | |
7db60bd8 MG |
749 | EFI_IMAGE_DOS_HEADER *DosHdr = data; |
750 | EFI_IMAGE_OPTIONAL_HEADER_UNION *PEHdr = data; | |
b2fe1780 | 751 | |
cbe21407 MG |
752 | if (datasize < sizeof(EFI_IMAGE_DOS_HEADER)) { |
753 | Print(L"Invalid image\n"); | |
754 | return EFI_UNSUPPORTED; | |
755 | } | |
756 | ||
f898777d | 757 | if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) |
7db60bd8 | 758 | PEHdr = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)((char *)data + DosHdr->e_lfanew); |
b2fe1780 | 759 | |
cbe21407 MG |
760 | if ((((UINT8 *)PEHdr - (UINT8 *)data) + sizeof(EFI_IMAGE_OPTIONAL_HEADER_UNION)) > datasize) { |
761 | Print(L"Invalid image\n"); | |
762 | return EFI_UNSUPPORTED; | |
763 | } | |
764 | ||
f898777d MG |
765 | if (PEHdr->Te.Signature != EFI_IMAGE_NT_SIGNATURE) { |
766 | Print(L"Unsupported image type\n"); | |
767 | return EFI_UNSUPPORTED; | |
768 | } | |
b2fe1780 | 769 | |
f898777d MG |
770 | if (PEHdr->Pe32.FileHeader.Characteristics & EFI_IMAGE_FILE_RELOCS_STRIPPED) { |
771 | Print(L"Unsupported image - Relocations have been stripped\n"); | |
772 | return EFI_UNSUPPORTED; | |
773 | } | |
b2fe1780 | 774 | |
f898777d MG |
775 | if (PEHdr->Pe32.OptionalHeader.Magic != EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) { |
776 | Print(L"Only 64-bit images supported\n"); | |
777 | return EFI_UNSUPPORTED; | |
778 | } | |
b2fe1780 | 779 | |
f898777d MG |
780 | context->PEHdr = PEHdr; |
781 | context->ImageAddress = PEHdr->Pe32Plus.OptionalHeader.ImageBase; | |
782 | context->ImageSize = (UINT64)PEHdr->Pe32Plus.OptionalHeader.SizeOfImage; | |
783 | context->SizeOfHeaders = PEHdr->Pe32Plus.OptionalHeader.SizeOfHeaders; | |
784 | context->EntryPoint = PEHdr->Pe32Plus.OptionalHeader.AddressOfEntryPoint; | |
785 | context->RelocDir = &PEHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC]; | |
786 | context->NumberOfRvaAndSizes = PEHdr->Pe32Plus.OptionalHeader.NumberOfRvaAndSizes; | |
787 | context->NumberOfSections = PEHdr->Pe32.FileHeader.NumberOfSections; | |
788 | context->FirstSection = (EFI_IMAGE_SECTION_HEADER *)((char *)PEHdr + PEHdr->Pe32.FileHeader.SizeOfOptionalHeader + sizeof(UINT32) + sizeof(EFI_IMAGE_FILE_HEADER)); | |
789 | context->SecDir = (EFI_IMAGE_DATA_DIRECTORY *) &PEHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]; | |
b2fe1780 | 790 | |
cbe21407 MG |
791 | if (context->ImageSize < context->SizeOfHeaders) { |
792 | Print(L"Invalid image\n"); | |
793 | return EFI_UNSUPPORTED; | |
b2fe1780 MG |
794 | } |
795 | ||
cbe21407 MG |
796 | if (((UINT8 *)context->SecDir - (UINT8 *)data) > (datasize - sizeof(EFI_IMAGE_DATA_DIRECTORY))) { |
797 | Print(L"Invalid image\n"); | |
798 | return EFI_UNSUPPORTED; | |
799 | } | |
800 | ||
ce78d2d2 | 801 | if (context->SecDir->VirtualAddress >= datasize) { |
f898777d | 802 | Print(L"Malformed security header\n"); |
f898777d MG |
803 | return EFI_INVALID_PARAMETER; |
804 | } | |
f898777d | 805 | return EFI_SUCCESS; |
b2fe1780 MG |
806 | } |
807 | ||
f898777d MG |
808 | /* |
809 | * Once the image has been loaded it needs to be validated and relocated | |
810 | */ | |
000c565c GCPL |
811 | static EFI_STATUS handle_image (void *data, unsigned int datasize, |
812 | EFI_LOADED_IMAGE *li) | |
b2fe1780 MG |
813 | { |
814 | EFI_STATUS efi_status; | |
815 | char *buffer; | |
816 | int i, size; | |
817 | EFI_IMAGE_SECTION_HEADER *Section; | |
0e6b0195 | 818 | char *base, *end; |
b2fe1780 | 819 | PE_COFF_LOADER_IMAGE_CONTEXT context; |
7f055335 | 820 | |
20f6cde6 MG |
821 | /* |
822 | * The binary header contains relevant context and section pointers | |
823 | */ | |
ce78d2d2 | 824 | efi_status = read_header(data, datasize, &context); |
b2fe1780 MG |
825 | if (efi_status != EFI_SUCCESS) { |
826 | Print(L"Failed to read header\n"); | |
827 | return efi_status; | |
828 | } | |
829 | ||
20f6cde6 MG |
830 | /* |
831 | * We only need to verify the binary if we're in secure mode | |
832 | */ | |
6279b58e | 833 | if (secure_mode ()) { |
0a6565c5 | 834 | efi_status = verify_buffer(data, datasize, &context); |
7f055335 | 835 | |
6279b58e MG |
836 | if (efi_status != EFI_SUCCESS) { |
837 | Print(L"Verification failed\n"); | |
838 | return efi_status; | |
839 | } | |
7f055335 MG |
840 | } |
841 | ||
b2fe1780 MG |
842 | buffer = AllocatePool(context.ImageSize); |
843 | ||
0e6b0195 MG |
844 | if (!buffer) { |
845 | Print(L"Failed to allocate image buffer\n"); | |
846 | return EFI_OUT_OF_RESOURCES; | |
847 | } | |
848 | ||
7db60bd8 | 849 | CopyMem(buffer, data, context.SizeOfHeaders); |
b2fe1780 | 850 | |
20f6cde6 MG |
851 | /* |
852 | * Copy the executable's sections to their desired offsets | |
853 | */ | |
b2fe1780 MG |
854 | Section = context.FirstSection; |
855 | for (i = 0; i < context.NumberOfSections; i++) { | |
856 | size = Section->Misc.VirtualSize; | |
857 | ||
858 | if (size > Section->SizeOfRawData) | |
859 | size = Section->SizeOfRawData; | |
860 | ||
861 | base = ImageAddress (buffer, context.ImageSize, Section->VirtualAddress); | |
862 | end = ImageAddress (buffer, context.ImageSize, Section->VirtualAddress + size - 1); | |
863 | ||
864 | if (!base || !end) { | |
865 | Print(L"Invalid section size\n"); | |
866 | return EFI_UNSUPPORTED; | |
867 | } | |
868 | ||
b2fe1780 | 869 | if (Section->SizeOfRawData > 0) |
7db60bd8 | 870 | CopyMem(base, data + Section->PointerToRawData, size); |
b2fe1780 MG |
871 | |
872 | if (size < Section->Misc.VirtualSize) | |
873 | ZeroMem (base + size, Section->Misc.VirtualSize - size); | |
874 | ||
875 | Section += 1; | |
876 | } | |
877 | ||
20f6cde6 MG |
878 | /* |
879 | * Run the relocation fixups | |
880 | */ | |
7db60bd8 | 881 | efi_status = relocate_coff(&context, buffer); |
b2fe1780 MG |
882 | |
883 | if (efi_status != EFI_SUCCESS) { | |
884 | Print(L"Relocation failed\n"); | |
0db1af8a | 885 | FreePool(buffer); |
b2fe1780 MG |
886 | return efi_status; |
887 | } | |
888 | ||
0e6b0195 | 889 | entry_point = ImageAddress(buffer, context.ImageSize, context.EntryPoint); |
20f6cde6 MG |
890 | /* |
891 | * grub needs to know its location and size in memory, so fix up | |
892 | * the loaded image protocol values | |
893 | */ | |
5fe882ba MG |
894 | li->ImageBase = buffer; |
895 | li->ImageSize = context.ImageSize; | |
896 | ||
09e2c939 GCPL |
897 | /* Pass the load options to the second stage loader */ |
898 | li->LoadOptions = load_options; | |
899 | li->LoadOptionsSize = load_options_size; | |
900 | ||
0e6b0195 MG |
901 | if (!entry_point) { |
902 | Print(L"Invalid entry point\n"); | |
0db1af8a | 903 | FreePool(buffer); |
0e6b0195 MG |
904 | return EFI_UNSUPPORTED; |
905 | } | |
b2fe1780 MG |
906 | |
907 | return EFI_SUCCESS; | |
908 | } | |
909 | ||
6d6b0221 PJ |
910 | static int |
911 | should_use_fallback(EFI_HANDLE image_handle) | |
912 | { | |
913 | EFI_GUID loaded_image_protocol = LOADED_IMAGE_PROTOCOL; | |
914 | EFI_LOADED_IMAGE *li; | |
6d6b0221 PJ |
915 | unsigned int pathlen = 0; |
916 | CHAR16 *bootpath; | |
917 | EFI_FILE_IO_INTERFACE *fio = NULL; | |
35b0b55b PJ |
918 | EFI_FILE *vh; |
919 | EFI_FILE *fh; | |
6d6b0221 PJ |
920 | EFI_STATUS rc; |
921 | ||
922 | rc = uefi_call_wrapper(BS->HandleProtocol, 3, image_handle, | |
923 | &loaded_image_protocol, (void **)&li); | |
c9d11306 PJ |
924 | if (EFI_ERROR(rc)) { |
925 | Print(L"Could not get image for bootx64.efi: %d\n", rc); | |
6d6b0221 | 926 | return 0; |
c9d11306 | 927 | } |
6d6b0221 | 928 | |
2e7fc28d | 929 | bootpath = DevicePathToStr(li->FilePath); |
6d6b0221 PJ |
930 | |
931 | /* Check the beginning of the string and the end, to avoid | |
932 | * caring about which arch this is. */ | |
933 | /* I really don't know why, but sometimes bootpath gives us | |
934 | * L"\\EFI\\BOOT\\/BOOTX64.EFI". So just handle that here... | |
935 | */ | |
936 | if (StrnCaseCmp(bootpath, L"\\EFI\\BOOT\\BOOT", 14) && | |
937 | StrnCaseCmp(bootpath, L"\\EFI\\BOOT\\/BOOT", 15)) | |
938 | return 0; | |
2e7fc28d | 939 | |
6d6b0221 PJ |
940 | pathlen = StrLen(bootpath); |
941 | if (pathlen < 5 || StrCaseCmp(bootpath + pathlen - 4, L".EFI")) | |
942 | return 0; | |
943 | ||
6d6b0221 | 944 | rc = uefi_call_wrapper(BS->HandleProtocol, 3, li->DeviceHandle, |
35b0b55b | 945 | &FileSystemProtocol, (void **)&fio); |
c9d11306 PJ |
946 | if (EFI_ERROR(rc)) { |
947 | Print(L"Could not get fio for li->DeviceHandle: %d\n", rc); | |
6d6b0221 | 948 | return 0; |
c9d11306 | 949 | } |
6d6b0221 PJ |
950 | |
951 | rc = uefi_call_wrapper(fio->OpenVolume, 2, fio, &vh); | |
c9d11306 PJ |
952 | if (EFI_ERROR(rc)) { |
953 | Print(L"Could not open fio volume: %d\n", rc); | |
6d6b0221 | 954 | return 0; |
c9d11306 | 955 | } |
6d6b0221 PJ |
956 | |
957 | rc = uefi_call_wrapper(vh->Open, 5, vh, &fh, L"\\EFI\\BOOT" FALLBACK, | |
5bb3e64e | 958 | EFI_FILE_MODE_READ, 0); |
6d6b0221 | 959 | if (EFI_ERROR(rc)) { |
b32a3ce1 PJ |
960 | /* Do not print the error here - this is an acceptable case |
961 | * for removable media, where we genuinely don't want | |
962 | * fallback.efi to exist. | |
963 | * Print(L"Could not open \"\\EFI\\BOOT%s\": %d\n", FALLBACK, | |
964 | * rc); | |
965 | */ | |
6d6b0221 PJ |
966 | uefi_call_wrapper(vh->Close, 1, vh); |
967 | return 0; | |
968 | } | |
969 | uefi_call_wrapper(fh->Close, 1, fh); | |
970 | uefi_call_wrapper(vh->Close, 1, vh); | |
971 | ||
972 | return 1; | |
973 | } | |
974 | ||
20f6cde6 MG |
975 | /* |
976 | * Generate the path of an executable given shim's path and the name | |
977 | * of the executable | |
978 | */ | |
822d089e GCPL |
979 | static EFI_STATUS generate_path(EFI_LOADED_IMAGE *li, CHAR16 *ImagePath, |
980 | EFI_DEVICE_PATH **grubpath, CHAR16 **PathName) | |
f898777d | 981 | { |
0db1af8a | 982 | EFI_DEVICE_PATH *devpath; |
f898777d | 983 | EFI_HANDLE device; |
bc6aaefa | 984 | int i; |
db54b0a4 MG |
985 | unsigned int pathlen = 0; |
986 | EFI_STATUS efi_status = EFI_SUCCESS; | |
bc6aaefa | 987 | CHAR16 *bootpath; |
f898777d | 988 | |
f898777d | 989 | device = li->DeviceHandle; |
0db1af8a | 990 | devpath = li->FilePath; |
f898777d | 991 | |
bc6aaefa | 992 | bootpath = DevicePathToStr(devpath); |
f898777d | 993 | |
bc6aaefa | 994 | pathlen = StrLen(bootpath); |
f898777d | 995 | |
bc6aaefa MG |
996 | for (i=pathlen; i>0; i--) { |
997 | if (bootpath[i] == '\\') | |
998 | break; | |
f898777d MG |
999 | } |
1000 | ||
bc6aaefa MG |
1001 | bootpath[i+1] = '\0'; |
1002 | ||
cbe21407 | 1003 | if (i == 0 || bootpath[i-i] == '\\') |
00ced0c1 MG |
1004 | bootpath[i] = '\0'; |
1005 | ||
822d089e | 1006 | *PathName = AllocatePool(StrSize(bootpath) + StrSize(ImagePath)); |
f898777d | 1007 | |
db54b0a4 | 1008 | if (!*PathName) { |
0db1af8a MG |
1009 | Print(L"Failed to allocate path buffer\n"); |
1010 | efi_status = EFI_OUT_OF_RESOURCES; | |
1011 | goto error; | |
f898777d MG |
1012 | } |
1013 | ||
bc6aaefa | 1014 | *PathName[0] = '\0'; |
155a76bb PJ |
1015 | if (StrnCaseCmp(bootpath, ImagePath, StrLen(bootpath))) |
1016 | StrCat(*PathName, bootpath); | |
822d089e | 1017 | StrCat(*PathName, ImagePath); |
db54b0a4 MG |
1018 | |
1019 | *grubpath = FileDevicePath(device, *PathName); | |
1020 | ||
1021 | error: | |
1022 | return efi_status; | |
1023 | } | |
1024 | ||
1025 | /* | |
20f6cde6 | 1026 | * Open the second stage bootloader and read it into a buffer |
db54b0a4 | 1027 | */ |
822d089e GCPL |
1028 | static EFI_STATUS load_image (EFI_LOADED_IMAGE *li, void **data, |
1029 | int *datasize, CHAR16 *PathName) | |
db54b0a4 MG |
1030 | { |
1031 | EFI_GUID simple_file_system_protocol = SIMPLE_FILE_SYSTEM_PROTOCOL; | |
1032 | EFI_GUID file_info_id = EFI_FILE_INFO_ID; | |
1033 | EFI_STATUS efi_status; | |
1034 | EFI_HANDLE device; | |
1035 | EFI_FILE_INFO *fileinfo = NULL; | |
1036 | EFI_FILE_IO_INTERFACE *drive; | |
1037 | EFI_FILE *root, *grub; | |
6eb1eca4 | 1038 | UINTN buffersize = sizeof(EFI_FILE_INFO); |
db54b0a4 MG |
1039 | |
1040 | device = li->DeviceHandle; | |
f898777d | 1041 | |
20f6cde6 MG |
1042 | /* |
1043 | * Open the device | |
1044 | */ | |
0db1af8a | 1045 | efi_status = uefi_call_wrapper(BS->HandleProtocol, 3, device, |
ed711b02 MG |
1046 | &simple_file_system_protocol, |
1047 | (void **)&drive); | |
f898777d | 1048 | |
0db1af8a MG |
1049 | if (efi_status != EFI_SUCCESS) { |
1050 | Print(L"Failed to find fs\n"); | |
1051 | goto error; | |
1052 | } | |
f898777d | 1053 | |
0db1af8a MG |
1054 | efi_status = uefi_call_wrapper(drive->OpenVolume, 2, drive, &root); |
1055 | ||
1056 | if (efi_status != EFI_SUCCESS) { | |
1057 | Print(L"Failed to open fs\n"); | |
1058 | goto error; | |
1059 | } | |
f898777d | 1060 | |
20f6cde6 MG |
1061 | /* |
1062 | * And then open the file | |
1063 | */ | |
f898777d MG |
1064 | efi_status = uefi_call_wrapper(root->Open, 5, root, &grub, PathName, |
1065 | EFI_FILE_MODE_READ, 0); | |
1066 | ||
1067 | if (efi_status != EFI_SUCCESS) { | |
1068 | Print(L"Failed to open %s - %lx\n", PathName, efi_status); | |
0db1af8a MG |
1069 | goto error; |
1070 | } | |
1071 | ||
1072 | fileinfo = AllocatePool(buffersize); | |
1073 | ||
1074 | if (!fileinfo) { | |
1075 | Print(L"Unable to allocate file info buffer\n"); | |
1076 | efi_status = EFI_OUT_OF_RESOURCES; | |
1077 | goto error; | |
f898777d MG |
1078 | } |
1079 | ||
20f6cde6 MG |
1080 | /* |
1081 | * Find out how big the file is in order to allocate the storage | |
1082 | * buffer | |
1083 | */ | |
f898777d MG |
1084 | efi_status = uefi_call_wrapper(grub->GetInfo, 4, grub, &file_info_id, |
1085 | &buffersize, fileinfo); | |
1086 | ||
1087 | if (efi_status == EFI_BUFFER_TOO_SMALL) { | |
cbe21407 | 1088 | FreePool(fileinfo); |
f898777d MG |
1089 | fileinfo = AllocatePool(buffersize); |
1090 | if (!fileinfo) { | |
0db1af8a MG |
1091 | Print(L"Unable to allocate file info buffer\n"); |
1092 | efi_status = EFI_OUT_OF_RESOURCES; | |
1093 | goto error; | |
f898777d MG |
1094 | } |
1095 | efi_status = uefi_call_wrapper(grub->GetInfo, 4, grub, | |
1096 | &file_info_id, &buffersize, | |
1097 | fileinfo); | |
1098 | } | |
1099 | ||
1100 | if (efi_status != EFI_SUCCESS) { | |
1101 | Print(L"Unable to get file info\n"); | |
0db1af8a | 1102 | goto error; |
f898777d MG |
1103 | } |
1104 | ||
1105 | buffersize = fileinfo->FileSize; | |
0db1af8a | 1106 | |
7db60bd8 | 1107 | *data = AllocatePool(buffersize); |
f898777d | 1108 | |
7db60bd8 | 1109 | if (!*data) { |
f898777d | 1110 | Print(L"Unable to allocate file buffer\n"); |
0db1af8a MG |
1111 | efi_status = EFI_OUT_OF_RESOURCES; |
1112 | goto error; | |
f898777d | 1113 | } |
20f6cde6 MG |
1114 | |
1115 | /* | |
1116 | * Perform the actual read | |
1117 | */ | |
f898777d | 1118 | efi_status = uefi_call_wrapper(grub->Read, 3, grub, &buffersize, |
7db60bd8 | 1119 | *data); |
f898777d | 1120 | |
0db1af8a MG |
1121 | if (efi_status == EFI_BUFFER_TOO_SMALL) { |
1122 | FreePool(*data); | |
1123 | *data = AllocatePool(buffersize); | |
1124 | efi_status = uefi_call_wrapper(grub->Read, 3, grub, | |
1125 | &buffersize, *data); | |
f898777d MG |
1126 | } |
1127 | ||
1128 | if (efi_status != EFI_SUCCESS) { | |
0db1af8a MG |
1129 | Print(L"Unexpected return from initial read: %x, buffersize %x\n", efi_status, buffersize); |
1130 | goto error; | |
f898777d MG |
1131 | } |
1132 | ||
7db60bd8 | 1133 | *datasize = buffersize; |
f898777d | 1134 | |
cbe21407 MG |
1135 | FreePool(fileinfo); |
1136 | ||
f898777d | 1137 | return EFI_SUCCESS; |
0db1af8a MG |
1138 | error: |
1139 | if (*data) { | |
1140 | FreePool(*data); | |
1141 | *data = NULL; | |
1142 | } | |
6f161626 | 1143 | |
0db1af8a MG |
1144 | if (fileinfo) |
1145 | FreePool(fileinfo); | |
1146 | return efi_status; | |
f898777d MG |
1147 | } |
1148 | ||
20f6cde6 MG |
1149 | /* |
1150 | * Protocol entry point. If secure boot is enabled, verify that the provided | |
1151 | * buffer is signed with a trusted key. | |
1152 | */ | |
db54b0a4 | 1153 | EFI_STATUS shim_verify (void *buffer, UINT32 size) |
f4b24734 MG |
1154 | { |
1155 | EFI_STATUS status; | |
1156 | PE_COFF_LOADER_IMAGE_CONTEXT context; | |
1157 | ||
6279b58e MG |
1158 | if (!secure_mode()) |
1159 | return EFI_SUCCESS; | |
1160 | ||
ce78d2d2 | 1161 | status = read_header(buffer, size, &context); |
f4b24734 MG |
1162 | |
1163 | if (status != EFI_SUCCESS) | |
1164 | return status; | |
1165 | ||
0a6565c5 | 1166 | status = verify_buffer(buffer, size, &context); |
f4b24734 MG |
1167 | |
1168 | return status; | |
1169 | } | |
1170 | ||
20f6cde6 MG |
1171 | /* |
1172 | * Load and run an EFI executable | |
1173 | */ | |
cec6a0a9 | 1174 | EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath) |
822d089e | 1175 | { |
cec6a0a9 | 1176 | EFI_GUID loaded_image_protocol = LOADED_IMAGE_PROTOCOL; |
822d089e | 1177 | EFI_STATUS efi_status; |
cec6a0a9 | 1178 | EFI_LOADED_IMAGE *li, li_bak; |
cec6a0a9 | 1179 | EFI_DEVICE_PATH *path; |
1c595706 MG |
1180 | CHAR16 *PathName = NULL; |
1181 | void *sourcebuffer = NULL; | |
1182 | UINTN sourcesize = 0; | |
822d089e GCPL |
1183 | void *data = NULL; |
1184 | int datasize; | |
1185 | ||
20f6cde6 MG |
1186 | /* |
1187 | * We need to refer to the loaded image protocol on the running | |
1188 | * binary in order to find our path | |
1189 | */ | |
cec6a0a9 | 1190 | efi_status = uefi_call_wrapper(BS->HandleProtocol, 3, image_handle, |
ed711b02 | 1191 | &loaded_image_protocol, (void **)&li); |
cec6a0a9 GCPL |
1192 | |
1193 | if (efi_status != EFI_SUCCESS) { | |
1194 | Print(L"Unable to init protocol\n"); | |
1195 | return efi_status; | |
1196 | } | |
1197 | ||
20f6cde6 MG |
1198 | /* |
1199 | * Build a new path from the existing one plus the executable name | |
1200 | */ | |
cec6a0a9 GCPL |
1201 | efi_status = generate_path(li, ImagePath, &path, &PathName); |
1202 | ||
1203 | if (efi_status != EFI_SUCCESS) { | |
1204 | Print(L"Unable to generate path: %s\n", ImagePath); | |
1205 | goto done; | |
1206 | } | |
1207 | ||
1c595706 MG |
1208 | if (findNetboot(image_handle)) { |
1209 | efi_status = parseNetbootinfo(image_handle); | |
1210 | if (efi_status != EFI_SUCCESS) { | |
1211 | Print(L"Netboot parsing failed: %d\n", efi_status); | |
1212 | return EFI_PROTOCOL_ERROR; | |
1213 | } | |
1214 | efi_status = FetchNetbootimage(image_handle, &sourcebuffer, | |
1215 | &sourcesize); | |
1216 | if (efi_status != EFI_SUCCESS) { | |
1217 | Print(L"Unable to fetch TFTP image\n"); | |
1218 | return efi_status; | |
1219 | } | |
1220 | data = sourcebuffer; | |
1221 | datasize = sourcesize; | |
1222 | } else { | |
4ad234f1 MG |
1223 | /* |
1224 | * Read the new executable off disk | |
1225 | */ | |
1c595706 | 1226 | efi_status = load_image(li, &data, &datasize, PathName); |
822d089e | 1227 | |
1c595706 MG |
1228 | if (efi_status != EFI_SUCCESS) { |
1229 | Print(L"Failed to load image\n"); | |
1230 | goto done; | |
1231 | } | |
822d089e GCPL |
1232 | } |
1233 | ||
20f6cde6 MG |
1234 | /* |
1235 | * We need to modify the loaded image protocol entry before running | |
1236 | * the new binary, so back it up | |
1237 | */ | |
822d089e GCPL |
1238 | CopyMem(&li_bak, li, sizeof(li_bak)); |
1239 | ||
20f6cde6 MG |
1240 | /* |
1241 | * Verify and, if appropriate, relocate and execute the executable | |
1242 | */ | |
822d089e GCPL |
1243 | efi_status = handle_image(data, datasize, li); |
1244 | ||
1245 | if (efi_status != EFI_SUCCESS) { | |
1246 | Print(L"Failed to load image\n"); | |
1247 | CopyMem(li, &li_bak, sizeof(li_bak)); | |
1248 | goto done; | |
1249 | } | |
1250 | ||
20f6cde6 MG |
1251 | /* |
1252 | * The binary is trusted and relocated. Run it | |
1253 | */ | |
cbe21407 | 1254 | efi_status = uefi_call_wrapper(entry_point, 2, image_handle, systab); |
822d089e | 1255 | |
20f6cde6 MG |
1256 | /* |
1257 | * Restore our original loaded image values | |
1258 | */ | |
822d089e GCPL |
1259 | CopyMem(li, &li_bak, sizeof(li_bak)); |
1260 | done: | |
cbe21407 MG |
1261 | if (PathName) |
1262 | FreePool(PathName); | |
1263 | ||
1264 | if (data) | |
1265 | FreePool(data); | |
1266 | ||
822d089e GCPL |
1267 | return efi_status; |
1268 | } | |
1269 | ||
20f6cde6 MG |
1270 | /* |
1271 | * Load and run grub. If that fails because grub isn't trusted, load and | |
1272 | * run MokManager. | |
1273 | */ | |
db54b0a4 | 1274 | EFI_STATUS init_grub(EFI_HANDLE image_handle) |
b2fe1780 MG |
1275 | { |
1276 | EFI_STATUS efi_status; | |
f4b24734 | 1277 | |
6d6b0221 PJ |
1278 | if (should_use_fallback(image_handle)) |
1279 | efi_status = start_image(image_handle, FALLBACK); | |
1280 | else | |
1281 | efi_status = start_image(image_handle, second_stage); | |
b2fe1780 | 1282 | |
ef8c9962 MG |
1283 | if (efi_status != EFI_SUCCESS) |
1284 | efi_status = start_image(image_handle, MOK_MANAGER); | |
db54b0a4 MG |
1285 | |
1286 | return efi_status; | |
1287 | } | |
1288 | ||
20f6cde6 MG |
1289 | /* |
1290 | * Copy the boot-services only MokList variable to the runtime-accessible | |
1291 | * MokListRT variable. It's not marked NV, so the OS can't modify it. | |
1292 | */ | |
a903fb10 GCPL |
1293 | EFI_STATUS mirror_mok_list() |
1294 | { | |
1295 | EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; | |
1296 | EFI_STATUS efi_status; | |
1297 | UINT32 attributes; | |
1298 | void *Data = NULL; | |
1299 | UINTN DataSize = 0; | |
1300 | ||
1301 | efi_status = get_variable(L"MokList", shim_lock_guid, &attributes, | |
1302 | &DataSize, &Data); | |
1303 | ||
1304 | if (efi_status != EFI_SUCCESS) { | |
1305 | goto done; | |
1306 | } | |
1307 | ||
1308 | efi_status = uefi_call_wrapper(RT->SetVariable, 5, L"MokListRT", | |
1309 | &shim_lock_guid, | |
1310 | EFI_VARIABLE_BOOTSERVICE_ACCESS | |
1311 | | EFI_VARIABLE_RUNTIME_ACCESS, | |
1312 | DataSize, Data); | |
1313 | if (efi_status != EFI_SUCCESS) { | |
1314 | Print(L"Failed to set MokListRT %d\n", efi_status); | |
1315 | } | |
1316 | ||
1317 | done: | |
1318 | return efi_status; | |
1319 | } | |
1320 | ||
20f6cde6 MG |
1321 | /* |
1322 | * Check if a variable exists | |
1323 | */ | |
d5a2d9ea | 1324 | static BOOLEAN check_var(CHAR16 *varname) |
4b34567d GCPL |
1325 | { |
1326 | EFI_STATUS efi_status; | |
e470969e | 1327 | EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; |
1041805a | 1328 | UINTN size = sizeof(UINT32); |
801d1b93 | 1329 | UINT32 MokVar; |
e470969e GCPL |
1330 | UINT32 attributes; |
1331 | ||
d5a2d9ea | 1332 | efi_status = uefi_call_wrapper(RT->GetVariable, 5, varname, |
1041805a | 1333 | &shim_lock_guid, &attributes, |
d5a2d9ea | 1334 | &size, (void *)&MokVar); |
e470969e | 1335 | |
d5a2d9ea MG |
1336 | if (efi_status == EFI_SUCCESS || efi_status == EFI_BUFFER_TOO_SMALL) |
1337 | return TRUE; | |
4b34567d | 1338 | |
d5a2d9ea MG |
1339 | return FALSE; |
1340 | } | |
4b34567d | 1341 | |
20f6cde6 MG |
1342 | /* |
1343 | * If the OS has set any of these variables we need to drop into MOK and | |
1344 | * handle them appropriately | |
1345 | */ | |
d5a2d9ea MG |
1346 | EFI_STATUS check_mok_request(EFI_HANDLE image_handle) |
1347 | { | |
1348 | EFI_STATUS efi_status; | |
4b34567d | 1349 | |
d5a2d9ea | 1350 | if (check_var(L"MokNew") || check_var(L"MokSB") || |
92a136d8 GCPL |
1351 | check_var(L"MokPW") || check_var(L"MokAuth") || |
1352 | check_var(L"MokDel")) { | |
9272bc5b | 1353 | efi_status = start_image(image_handle, MOK_MANAGER); |
4b34567d | 1354 | |
9272bc5b MG |
1355 | if (efi_status != EFI_SUCCESS) { |
1356 | Print(L"Failed to start MokManager\n"); | |
1357 | return efi_status; | |
1358 | } | |
4b34567d | 1359 | } |
4b34567d | 1360 | |
9272bc5b | 1361 | return EFI_SUCCESS; |
4b34567d GCPL |
1362 | } |
1363 | ||
20f6cde6 MG |
1364 | /* |
1365 | * Verify that MokSBState is valid, and if appropriate set insecure mode | |
1366 | */ | |
1367 | ||
9eaadb0d MG |
1368 | static EFI_STATUS check_mok_sb (void) |
1369 | { | |
1370 | EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; | |
1371 | EFI_STATUS status = EFI_SUCCESS; | |
1372 | void *MokSBState = NULL; | |
1373 | UINTN MokSBStateSize = 0; | |
1374 | UINT32 attributes; | |
1375 | ||
1376 | status = get_variable(L"MokSBState", shim_lock_guid, &attributes, | |
1377 | &MokSBStateSize, &MokSBState); | |
1378 | ||
1379 | if (status != EFI_SUCCESS) | |
1380 | return EFI_ACCESS_DENIED; | |
1381 | ||
20f6cde6 MG |
1382 | /* |
1383 | * Delete and ignore the variable if it's been set from or could be | |
1384 | * modified by the OS | |
1385 | */ | |
9eaadb0d MG |
1386 | if (attributes & EFI_VARIABLE_RUNTIME_ACCESS) { |
1387 | Print(L"MokSBState is compromised! Clearing it\n"); | |
1388 | if (LibDeleteVariable(L"MokSBState", &shim_lock_guid) != EFI_SUCCESS) { | |
1389 | Print(L"Failed to erase MokSBState\n"); | |
1390 | } | |
1391 | status = EFI_ACCESS_DENIED; | |
1392 | } else { | |
1393 | if (*(UINT8 *)MokSBState == 1) { | |
1394 | insecure_mode = 1; | |
1395 | } | |
1396 | } | |
1397 | ||
1398 | return status; | |
4b34567d GCPL |
1399 | } |
1400 | ||
09e2c939 GCPL |
1401 | /* |
1402 | * Check the load options to specify the second stage loader | |
1403 | */ | |
1404 | EFI_STATUS set_second_stage (EFI_HANDLE image_handle) | |
1405 | { | |
1406 | EFI_STATUS status; | |
1407 | EFI_LOADED_IMAGE *li; | |
1408 | CHAR16 *start = NULL, *c; | |
1409 | int i, remaining_size = 0; | |
0283024e GCPL |
1410 | CHAR16 *loader_str = NULL; |
1411 | int loader_len = 0; | |
09e2c939 GCPL |
1412 | |
1413 | second_stage = DEFAULT_LOADER; | |
1414 | load_options = NULL; | |
1415 | load_options_size = 0; | |
1416 | ||
1417 | status = uefi_call_wrapper(BS->HandleProtocol, 3, image_handle, | |
1418 | &LoadedImageProtocol, (void **) &li); | |
1419 | if (status != EFI_SUCCESS) { | |
1420 | Print (L"Failed to get load options\n"); | |
1421 | return status; | |
1422 | } | |
1423 | ||
1424 | /* Expect a CHAR16 string with at least one CHAR16 */ | |
1425 | if (li->LoadOptionsSize < 4 || li->LoadOptionsSize % 2 != 0) { | |
1426 | return EFI_BAD_BUFFER_SIZE; | |
1427 | } | |
1428 | c = (CHAR16 *)(li->LoadOptions + (li->LoadOptionsSize - 2)); | |
1429 | if (*c != L'\0') { | |
1430 | return EFI_BAD_BUFFER_SIZE; | |
1431 | } | |
1432 | ||
0283024e GCPL |
1433 | /* |
1434 | * UEFI shell copies the whole line of the command into LoadOptions. | |
1435 | * We ignore the string before the first L' ', i.e. the name of this | |
1436 | * program. | |
1437 | */ | |
09e2c939 GCPL |
1438 | for (i = 0; i < li->LoadOptionsSize; i += 2) { |
1439 | c = (CHAR16 *)(li->LoadOptions + i); | |
1440 | if (*c == L' ') { | |
1441 | *c = L'\0'; | |
1442 | start = c + 1; | |
1443 | remaining_size = li->LoadOptionsSize - i - 2; | |
1444 | break; | |
1445 | } | |
1446 | } | |
1447 | ||
0283024e GCPL |
1448 | if (!start || remaining_size <= 0) |
1449 | return EFI_SUCCESS; | |
6e1bd3dc | 1450 | |
0283024e GCPL |
1451 | for (i = 0; start[i] != '\0'; i++) { |
1452 | if (start[i] == L' ' || start[i] == L'\0') | |
1453 | break; | |
1454 | loader_len++; | |
1455 | } | |
1456 | ||
1457 | /* | |
1458 | * Setup the name of the alternative loader and the LoadOptions for | |
1459 | * the loader | |
1460 | */ | |
1461 | if (loader_len > 0) { | |
1462 | loader_str = AllocatePool((loader_len + 1) * sizeof(CHAR16)); | |
1463 | if (!loader_str) { | |
1464 | Print(L"Failed to allocate loader string\n"); | |
1465 | return EFI_OUT_OF_RESOURCES; | |
1466 | } | |
1467 | for (i = 0; i < loader_len; i++) | |
1468 | loader_str[i] = start[i]; | |
1469 | loader_str[loader_len] = L'\0'; | |
1470 | ||
1471 | second_stage = loader_str; | |
09e2c939 GCPL |
1472 | load_options = start; |
1473 | load_options_size = remaining_size; | |
1474 | } | |
1475 | ||
1476 | return EFI_SUCCESS; | |
1477 | } | |
1478 | ||
db54b0a4 MG |
1479 | EFI_STATUS efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *passed_systab) |
1480 | { | |
1481 | EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; | |
1482 | static SHIM_LOCK shim_lock_interface; | |
1483 | EFI_HANDLE handle = NULL; | |
0a232ca9 | 1484 | EFI_STATUS efi_status; |
e75294e5 PJ |
1485 | UINT8 verbose_check; |
1486 | UINTN verbose_check_size; | |
1487 | UINT32 attributes; | |
1488 | EFI_GUID global_var = EFI_GLOBAL_VARIABLE; | |
db54b0a4 | 1489 | |
20f6cde6 MG |
1490 | /* |
1491 | * Set up the shim lock protocol so that grub and MokManager can | |
1492 | * call back in and use shim functions | |
1493 | */ | |
db54b0a4 | 1494 | shim_lock_interface.Verify = shim_verify; |
0a6565c5 MG |
1495 | shim_lock_interface.Hash = generate_hash; |
1496 | shim_lock_interface.Context = read_header; | |
db54b0a4 MG |
1497 | |
1498 | systab = passed_systab; | |
1499 | ||
20f6cde6 MG |
1500 | /* |
1501 | * Ensure that gnu-efi functions are available | |
1502 | */ | |
db54b0a4 MG |
1503 | InitializeLib(image_handle, systab); |
1504 | ||
e75294e5 PJ |
1505 | verbose_check_size = 1; |
1506 | efi_status = get_variable(L"SHIM_VERBOSE", global_var, &attributes, | |
1507 | &verbose_check_size, (void *)&verbose_check); | |
1508 | if (!EFI_ERROR(efi_status)) | |
1509 | verbose = verbose_check; | |
1510 | ||
09e2c939 GCPL |
1511 | /* Set the second stage loader */ |
1512 | set_second_stage (image_handle); | |
1513 | ||
20f6cde6 MG |
1514 | /* |
1515 | * Check whether the user has configured the system to run in | |
1516 | * insecure mode | |
1517 | */ | |
9eaadb0d MG |
1518 | check_mok_sb(); |
1519 | ||
20f6cde6 MG |
1520 | /* |
1521 | * Tell the user that we're in insecure mode if necessary | |
1522 | */ | |
9eaadb0d MG |
1523 | if (insecure_mode) { |
1524 | Print(L"Booting in insecure mode\n"); | |
1525 | uefi_call_wrapper(BS->Stall, 1, 2000000); | |
1526 | } | |
1527 | ||
20f6cde6 MG |
1528 | /* |
1529 | * Install the protocol | |
1530 | */ | |
1531 | uefi_call_wrapper(BS->InstallProtocolInterface, 4, &handle, | |
1532 | &shim_lock_guid, EFI_NATIVE_INTERFACE, | |
1533 | &shim_lock_interface); | |
1534 | ||
1535 | /* | |
1536 | * Enter MokManager if necessary | |
1537 | */ | |
4b34567d GCPL |
1538 | efi_status = check_mok_request(image_handle); |
1539 | ||
20f6cde6 MG |
1540 | /* |
1541 | * Copy the MOK list to a runtime variable so the kernel can make | |
1542 | * use of it | |
1543 | */ | |
a903fb10 GCPL |
1544 | efi_status = mirror_mok_list(); |
1545 | ||
20f6cde6 MG |
1546 | /* |
1547 | * Hand over control to the second stage bootloader | |
1548 | */ | |
db54b0a4 | 1549 | |
0a232ca9 MG |
1550 | efi_status = init_grub(image_handle); |
1551 | ||
20f6cde6 MG |
1552 | /* |
1553 | * If we're back here then clean everything up before exiting | |
1554 | */ | |
0a232ca9 MG |
1555 | uefi_call_wrapper(BS->UninstallProtocolInterface, 3, handle, |
1556 | &shim_lock_guid, &shim_lock_interface); | |
1557 | ||
0283024e GCPL |
1558 | /* |
1559 | * Free the space allocated for the alternative 2nd stage loader | |
1560 | */ | |
1561 | if (load_options_size > 0) | |
1562 | FreePool(second_stage); | |
1563 | ||
0a232ca9 | 1564 | return efi_status; |
b2fe1780 | 1565 | } |