]>
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" |
1c595706 | 41 | #include "netboot.h" |
ef8c9962 | 42 | #include "shim_cert.h" |
cbef697a | 43 | #include "replacements.h" |
6e1bd3dc | 44 | #include "ucs2.h" |
b2fe1780 | 45 | |
53862dda | 46 | #include "guid.h" |
7f0208a0 | 47 | #include "variables.h" |
53862dda | 48 | #include "efiauthenticated.h" |
59dcd9d1 | 49 | #include "security_policy.h" |
bc71a15e | 50 | #include "console.h" |
0fb089ee | 51 | #include "version.h" |
53862dda | 52 | |
6d6b0221 | 53 | #define FALLBACK L"\\fallback.efi" |
4b34567d | 54 | #define MOK_MANAGER L"\\MokManager.efi" |
f898777d | 55 | |
7f055335 MG |
56 | static EFI_SYSTEM_TABLE *systab; |
57 | static EFI_STATUS (EFIAPI *entry_point) (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table); | |
58 | ||
09e2c939 GCPL |
59 | static CHAR16 *second_stage; |
60 | static void *load_options; | |
61 | static UINT32 load_options_size; | |
e50cfe37 GCPL |
62 | static UINT8 in_protocol; |
63 | ||
64 | #define perror(fmt, ...) ({ \ | |
65 | UINTN __perror_ret = 0; \ | |
66 | if (in_protocol) \ | |
67 | __perror_ret = Print((fmt), ##__VA_ARGS__); \ | |
68 | __perror_ret; \ | |
69 | }) | |
09e2c939 | 70 | |
cb59de38 PJ |
71 | EFI_GUID SHIM_LOCK_GUID = { 0x605dab50, 0xe046, 0x4300, {0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23} }; |
72 | ||
f898777d MG |
73 | /* |
74 | * The vendor certificate used for validating the second stage loader | |
75 | */ | |
a1f28635 PJ |
76 | extern struct { |
77 | UINT32 vendor_cert_size; | |
78 | UINT32 vendor_dbx_size; | |
79 | UINT32 vendor_cert_offset; | |
80 | UINT32 vendor_dbx_offset; | |
81 | } cert_table; | |
82 | ||
83 | UINT32 vendor_cert_size; | |
84 | UINT32 vendor_dbx_size; | |
85 | UINT8 *vendor_cert; | |
86 | UINT8 *vendor_dbx; | |
b2fe1780 | 87 | |
cbef697a PJ |
88 | /* |
89 | * indicator of how an image has been verified | |
90 | */ | |
91 | verification_method_t verification_method; | |
92 | int loader_is_participating; | |
93 | ||
c13fc2f7 MG |
94 | #define EFI_IMAGE_SECURITY_DATABASE_GUID { 0xd719b2cb, 0x3d3a, 0x4596, { 0xa3, 0xbc, 0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f }} |
95 | ||
e60f1181 | 96 | UINT8 user_insecure_mode; |
47ebeb62 | 97 | UINT8 ignore_db; |
9eaadb0d | 98 | |
c16548d0 MG |
99 | typedef enum { |
100 | DATA_FOUND, | |
101 | DATA_NOT_FOUND, | |
102 | VAR_NOT_FOUND | |
103 | } CHECK_STATUS; | |
104 | ||
13422973 GCPL |
105 | typedef struct { |
106 | UINT32 MokSize; | |
107 | UINT8 *Mok; | |
108 | } MokListNode; | |
109 | ||
f898777d MG |
110 | /* |
111 | * Perform basic bounds checking of the intra-image pointers | |
112 | */ | |
47a9d2c9 | 113 | static void *ImageAddress (void *image, unsigned int size, unsigned int address) |
b2fe1780 | 114 | { |
f898777d MG |
115 | if (address > size) |
116 | return NULL; | |
9d56c38f | 117 | |
f898777d MG |
118 | return image + address; |
119 | } | |
9d56c38f | 120 | |
f898777d MG |
121 | /* |
122 | * Perform the actual relocation | |
123 | */ | |
7db60bd8 MG |
124 | static EFI_STATUS relocate_coff (PE_COFF_LOADER_IMAGE_CONTEXT *context, |
125 | void *data) | |
f898777d MG |
126 | { |
127 | EFI_IMAGE_BASE_RELOCATION *RelocBase, *RelocBaseEnd; | |
128 | UINT64 Adjust; | |
129 | UINT16 *Reloc, *RelocEnd; | |
130 | char *Fixup, *FixupBase, *FixupData = NULL; | |
131 | UINT16 *Fixup16; | |
132 | UINT32 *Fixup32; | |
133 | UINT64 *Fixup64; | |
134 | int size = context->ImageSize; | |
7db60bd8 | 135 | void *ImageEnd = (char *)data + size; |
9d56c38f | 136 | |
b6a12d99 | 137 | #if __LP64__ |
7db60bd8 | 138 | context->PEHdr->Pe32Plus.OptionalHeader.ImageBase = (UINT64)data; |
b6a12d99 M |
139 | #else |
140 | context->PEHdr->Pe32.OptionalHeader.ImageBase = (UINT32)data; | |
141 | #endif | |
7f055335 | 142 | |
f898777d | 143 | if (context->NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC) { |
e50cfe37 | 144 | perror(L"Image has no relocation entry\n"); |
f898777d | 145 | return EFI_UNSUPPORTED; |
7f055335 MG |
146 | } |
147 | ||
4ca60879 AB |
148 | if (!context->RelocDir->Size) |
149 | return EFI_SUCCESS; | |
150 | ||
7db60bd8 MG |
151 | RelocBase = ImageAddress(data, size, context->RelocDir->VirtualAddress); |
152 | RelocBaseEnd = ImageAddress(data, size, context->RelocDir->VirtualAddress + context->RelocDir->Size - 1); | |
9d56c38f | 153 | |
f898777d | 154 | if (!RelocBase || !RelocBaseEnd) { |
e50cfe37 | 155 | perror(L"Reloc table overflows binary\n"); |
f898777d | 156 | return EFI_UNSUPPORTED; |
9d56c38f MG |
157 | } |
158 | ||
b6a12d99 | 159 | Adjust = (UINTN)data - context->ImageAddress; |
9d56c38f | 160 | |
a3beb2a6 PJ |
161 | if (Adjust == 0) |
162 | return EFI_SUCCESS; | |
163 | ||
f898777d MG |
164 | while (RelocBase < RelocBaseEnd) { |
165 | Reloc = (UINT16 *) ((char *) RelocBase + sizeof (EFI_IMAGE_BASE_RELOCATION)); | |
9d56c38f | 166 | |
a3beb2a6 | 167 | if ((RelocBase->SizeOfBlock == 0) || (RelocBase->SizeOfBlock > context->RelocDir->Size)) { |
e50cfe37 | 168 | perror(L"Reloc block size is invalid\n"); |
a3beb2a6 PJ |
169 | return EFI_UNSUPPORTED; |
170 | } | |
171 | ||
172 | RelocEnd = (UINT16 *) ((char *) RelocBase + RelocBase->SizeOfBlock); | |
7db60bd8 | 173 | if ((void *)RelocEnd < data || (void *)RelocEnd > ImageEnd) { |
e50cfe37 | 174 | perror(L"Reloc entry overflows binary\n"); |
f898777d | 175 | return EFI_UNSUPPORTED; |
9d56c38f MG |
176 | } |
177 | ||
7db60bd8 | 178 | FixupBase = ImageAddress(data, size, RelocBase->VirtualAddress); |
f898777d | 179 | if (!FixupBase) { |
e50cfe37 | 180 | perror(L"Invalid fixupbase\n"); |
f898777d | 181 | return EFI_UNSUPPORTED; |
9d56c38f | 182 | } |
9d56c38f | 183 | |
f898777d MG |
184 | while (Reloc < RelocEnd) { |
185 | Fixup = FixupBase + (*Reloc & 0xFFF); | |
186 | switch ((*Reloc) >> 12) { | |
187 | case EFI_IMAGE_REL_BASED_ABSOLUTE: | |
188 | break; | |
9d56c38f | 189 | |
f898777d MG |
190 | case EFI_IMAGE_REL_BASED_HIGH: |
191 | Fixup16 = (UINT16 *) Fixup; | |
192 | *Fixup16 = (UINT16) (*Fixup16 + ((UINT16) ((UINT32) Adjust >> 16))); | |
193 | if (FixupData != NULL) { | |
194 | *(UINT16 *) FixupData = *Fixup16; | |
195 | FixupData = FixupData + sizeof (UINT16); | |
196 | } | |
197 | break; | |
9d56c38f | 198 | |
f898777d MG |
199 | case EFI_IMAGE_REL_BASED_LOW: |
200 | Fixup16 = (UINT16 *) Fixup; | |
201 | *Fixup16 = (UINT16) (*Fixup16 + (UINT16) Adjust); | |
202 | if (FixupData != NULL) { | |
203 | *(UINT16 *) FixupData = *Fixup16; | |
204 | FixupData = FixupData + sizeof (UINT16); | |
205 | } | |
206 | break; | |
b2fe1780 | 207 | |
f898777d MG |
208 | case EFI_IMAGE_REL_BASED_HIGHLOW: |
209 | Fixup32 = (UINT32 *) Fixup; | |
210 | *Fixup32 = *Fixup32 + (UINT32) Adjust; | |
211 | if (FixupData != NULL) { | |
212 | FixupData = ALIGN_POINTER (FixupData, sizeof (UINT32)); | |
213 | *(UINT32 *)FixupData = *Fixup32; | |
214 | FixupData = FixupData + sizeof (UINT32); | |
215 | } | |
216 | break; | |
b2fe1780 | 217 | |
f898777d MG |
218 | case EFI_IMAGE_REL_BASED_DIR64: |
219 | Fixup64 = (UINT64 *) Fixup; | |
220 | *Fixup64 = *Fixup64 + (UINT64) Adjust; | |
221 | if (FixupData != NULL) { | |
222 | FixupData = ALIGN_POINTER (FixupData, sizeof(UINT64)); | |
223 | *(UINT64 *)(FixupData) = *Fixup64; | |
224 | FixupData = FixupData + sizeof(UINT64); | |
225 | } | |
226 | break; | |
b2fe1780 | 227 | |
f898777d | 228 | default: |
e50cfe37 | 229 | perror(L"Unknown relocation\n"); |
f898777d MG |
230 | return EFI_UNSUPPORTED; |
231 | } | |
232 | Reloc += 1; | |
0e6b0195 | 233 | } |
f898777d | 234 | RelocBase = (EFI_IMAGE_BASE_RELOCATION *) RelocEnd; |
7f055335 | 235 | } |
b2fe1780 MG |
236 | |
237 | return EFI_SUCCESS; | |
238 | } | |
239 | ||
b8070380 GCPL |
240 | static BOOLEAN verify_x509(UINT8 *Cert, UINTN CertSize) |
241 | { | |
242 | UINTN length; | |
243 | ||
244 | if (!Cert || CertSize < 4) | |
245 | return FALSE; | |
246 | ||
247 | /* | |
248 | * A DER encoding x509 certificate starts with SEQUENCE(0x30), | |
249 | * the number of length bytes, and the number of value bytes. | |
250 | * The size of a x509 certificate is usually between 127 bytes | |
251 | * and 64KB. For convenience, assume the number of value bytes | |
252 | * is 2, i.e. the second byte is 0x82. | |
253 | */ | |
254 | if (Cert[0] != 0x30 || Cert[1] != 0x82) | |
255 | return FALSE; | |
256 | ||
257 | length = Cert[2]<<8 | Cert[3]; | |
258 | if (length != (CertSize - 4)) | |
259 | return FALSE; | |
260 | ||
261 | return TRUE; | |
262 | } | |
263 | ||
5f0a358b PJ |
264 | static CHECK_STATUS check_db_cert_in_ram(EFI_SIGNATURE_LIST *CertList, |
265 | UINTN dbsize, | |
266 | WIN_CERTIFICATE_EFI_PKCS *data, | |
267 | UINT8 *hash) | |
3df68c18 | 268 | { |
3df68c18 | 269 | EFI_SIGNATURE_DATA *Cert; |
b8070380 | 270 | UINTN CertSize; |
c13fc2f7 | 271 | BOOLEAN IsFound = FALSE; |
53862dda | 272 | EFI_GUID CertType = X509_GUID; |
c16548d0 | 273 | |
c16548d0 | 274 | while ((dbsize > 0) && (dbsize >= CertList->SignatureListSize)) { |
c13fc2f7 | 275 | if (CompareGuid (&CertList->SignatureType, &CertType) == 0) { |
c16548d0 | 276 | Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize); |
b8070380 GCPL |
277 | CertSize = CertList->SignatureSize - sizeof(EFI_GUID); |
278 | if (verify_x509(Cert->SignatureData, CertSize)) { | |
c16548d0 MG |
279 | IsFound = AuthenticodeVerify (data->CertData, |
280 | data->Hdr.dwLength - sizeof(data->Hdr), | |
281 | Cert->SignatureData, | |
b8070380 | 282 | CertSize, |
c16548d0 | 283 | hash, SHA256_DIGEST_SIZE); |
7430b901 | 284 | if (IsFound) |
b8070380 GCPL |
285 | return DATA_FOUND; |
286 | } else if (verbose) { | |
287 | console_notify(L"Not a DER encoding x.509 Certificate"); | |
c16548d0 | 288 | } |
c16548d0 MG |
289 | } |
290 | ||
291 | dbsize -= CertList->SignatureListSize; | |
292 | CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize); | |
293 | } | |
294 | ||
c16548d0 MG |
295 | return DATA_NOT_FOUND; |
296 | } | |
297 | ||
5f0a358b PJ |
298 | static CHECK_STATUS check_db_cert(CHAR16 *dbname, EFI_GUID guid, |
299 | WIN_CERTIFICATE_EFI_PKCS *data, UINT8 *hash) | |
c16548d0 | 300 | { |
5f0a358b | 301 | CHECK_STATUS rc; |
c16548d0 | 302 | EFI_STATUS efi_status; |
c16548d0 | 303 | EFI_SIGNATURE_LIST *CertList; |
c16548d0 | 304 | UINTN dbsize = 0; |
7f0208a0 | 305 | UINT8 *db; |
3df68c18 | 306 | |
7f0208a0 | 307 | efi_status = get_variable(dbname, &db, &dbsize, guid); |
3df68c18 | 308 | |
5f0a358b | 309 | if (efi_status != EFI_SUCCESS) |
c16548d0 | 310 | return VAR_NOT_FOUND; |
3df68c18 | 311 | |
7f0208a0 | 312 | CertList = (EFI_SIGNATURE_LIST *)db; |
3df68c18 | 313 | |
5f0a358b PJ |
314 | rc = check_db_cert_in_ram(CertList, dbsize, data, hash); |
315 | ||
316 | FreePool(db); | |
317 | ||
318 | return rc; | |
319 | } | |
320 | ||
20f6cde6 MG |
321 | /* |
322 | * Check a hash against an EFI_SIGNATURE_LIST in a buffer | |
323 | */ | |
5f0a358b PJ |
324 | static CHECK_STATUS check_db_hash_in_ram(EFI_SIGNATURE_LIST *CertList, |
325 | UINTN dbsize, UINT8 *data, | |
326 | int SignatureSize, EFI_GUID CertType) | |
327 | { | |
328 | EFI_SIGNATURE_DATA *Cert; | |
329 | UINTN CertCount, Index; | |
330 | BOOLEAN IsFound = FALSE; | |
331 | ||
c16548d0 | 332 | while ((dbsize > 0) && (dbsize >= CertList->SignatureListSize)) { |
d71240bf | 333 | CertCount = (CertList->SignatureListSize -sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize; |
3df68c18 | 334 | Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize); |
c13fc2f7 | 335 | if (CompareGuid(&CertList->SignatureType, &CertType) == 0) { |
3df68c18 | 336 | for (Index = 0; Index < CertCount; Index++) { |
c16548d0 | 337 | if (CompareMem (Cert->SignatureData, data, SignatureSize) == 0) { |
3df68c18 MG |
338 | // |
339 | // Find the signature in database. | |
340 | // | |
341 | IsFound = TRUE; | |
342 | break; | |
343 | } | |
344 | ||
345 | Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) Cert + CertList->SignatureSize); | |
346 | } | |
347 | if (IsFound) { | |
348 | break; | |
349 | } | |
350 | } | |
351 | ||
c16548d0 | 352 | dbsize -= CertList->SignatureListSize; |
3df68c18 MG |
353 | CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize); |
354 | } | |
355 | ||
3df68c18 | 356 | if (IsFound) |
c16548d0 MG |
357 | return DATA_FOUND; |
358 | ||
359 | return DATA_NOT_FOUND; | |
360 | } | |
361 | ||
20f6cde6 MG |
362 | /* |
363 | * Check a hash against an EFI_SIGNATURE_LIST in a UEFI variable | |
364 | */ | |
5f0a358b PJ |
365 | static CHECK_STATUS check_db_hash(CHAR16 *dbname, EFI_GUID guid, UINT8 *data, |
366 | int SignatureSize, EFI_GUID CertType) | |
367 | { | |
368 | EFI_STATUS efi_status; | |
369 | EFI_SIGNATURE_LIST *CertList; | |
5f0a358b | 370 | UINTN dbsize = 0; |
7f0208a0 | 371 | UINT8 *db; |
5f0a358b | 372 | |
7f0208a0 | 373 | efi_status = get_variable(dbname, &db, &dbsize, guid); |
5f0a358b PJ |
374 | |
375 | if (efi_status != EFI_SUCCESS) { | |
376 | return VAR_NOT_FOUND; | |
377 | } | |
378 | ||
7f0208a0 | 379 | CertList = (EFI_SIGNATURE_LIST *)db; |
5f0a358b PJ |
380 | |
381 | CHECK_STATUS rc = check_db_hash_in_ram(CertList, dbsize, data, | |
382 | SignatureSize, CertType); | |
383 | FreePool(db); | |
384 | return rc; | |
385 | ||
386 | } | |
387 | ||
20f6cde6 MG |
388 | /* |
389 | * Check whether the binary signature or hash are present in dbx or the | |
390 | * built-in blacklist | |
391 | */ | |
ce6a5748 MG |
392 | static EFI_STATUS check_blacklist (WIN_CERTIFICATE_EFI_PKCS *cert, |
393 | UINT8 *sha256hash, UINT8 *sha1hash) | |
c16548d0 | 394 | { |
0a6565c5 | 395 | EFI_GUID secure_var = EFI_IMAGE_SECURITY_DATABASE_GUID; |
92888645 | 396 | EFI_SIGNATURE_LIST *dbx = (EFI_SIGNATURE_LIST *)vendor_dbx; |
0a6565c5 | 397 | |
92888645 | 398 | if (check_db_hash_in_ram(dbx, vendor_dbx_size, sha256hash, |
53862dda | 399 | SHA256_DIGEST_SIZE, EFI_CERT_SHA256_GUID) == |
8b7685b2 | 400 | DATA_FOUND) |
5f0a358b | 401 | return EFI_ACCESS_DENIED; |
92888645 | 402 | if (check_db_hash_in_ram(dbx, vendor_dbx_size, sha1hash, |
53862dda | 403 | SHA1_DIGEST_SIZE, EFI_CERT_SHA1_GUID) == |
8b7685b2 | 404 | DATA_FOUND) |
5f0a358b | 405 | return EFI_ACCESS_DENIED; |
8044a321 PJ |
406 | if (cert && check_db_cert_in_ram(dbx, vendor_dbx_size, cert, |
407 | sha256hash) == DATA_FOUND) | |
5f0a358b PJ |
408 | return EFI_ACCESS_DENIED; |
409 | ||
0a6565c5 | 410 | if (check_db_hash(L"dbx", secure_var, sha256hash, SHA256_DIGEST_SIZE, |
53862dda | 411 | EFI_CERT_SHA256_GUID) == DATA_FOUND) |
c16548d0 | 412 | return EFI_ACCESS_DENIED; |
0a6565c5 | 413 | if (check_db_hash(L"dbx", secure_var, sha1hash, SHA1_DIGEST_SIZE, |
53862dda | 414 | EFI_CERT_SHA1_GUID) == DATA_FOUND) |
ce6a5748 | 415 | return EFI_ACCESS_DENIED; |
8044a321 PJ |
416 | if (cert && check_db_cert(L"dbx", secure_var, cert, sha256hash) == |
417 | DATA_FOUND) | |
3df68c18 MG |
418 | return EFI_ACCESS_DENIED; |
419 | ||
420 | return EFI_SUCCESS; | |
421 | } | |
422 | ||
cbef697a PJ |
423 | static void update_verification_method(verification_method_t method) |
424 | { | |
425 | if (verification_method == VERIFIED_BY_NOTHING) | |
426 | verification_method = method; | |
427 | } | |
428 | ||
20f6cde6 MG |
429 | /* |
430 | * Check whether the binary signature or hash are present in db or MokList | |
431 | */ | |
ce6a5748 MG |
432 | static EFI_STATUS check_whitelist (WIN_CERTIFICATE_EFI_PKCS *cert, |
433 | UINT8 *sha256hash, UINT8 *sha1hash) | |
b2058cf8 | 434 | { |
0a6565c5 MG |
435 | EFI_GUID secure_var = EFI_IMAGE_SECURITY_DATABASE_GUID; |
436 | EFI_GUID shim_var = SHIM_LOCK_GUID; | |
437 | ||
47ebeb62 JB |
438 | if (!ignore_db) { |
439 | if (check_db_hash(L"db", secure_var, sha256hash, SHA256_DIGEST_SIZE, | |
440 | EFI_CERT_SHA256_GUID) == DATA_FOUND) { | |
441 | update_verification_method(VERIFIED_BY_HASH); | |
442 | return EFI_SUCCESS; | |
443 | } | |
444 | if (check_db_hash(L"db", secure_var, sha1hash, SHA1_DIGEST_SIZE, | |
445 | EFI_CERT_SHA1_GUID) == DATA_FOUND) { | |
446 | verification_method = VERIFIED_BY_HASH; | |
447 | update_verification_method(VERIFIED_BY_HASH); | |
448 | return EFI_SUCCESS; | |
449 | } | |
8044a321 PJ |
450 | if (cert && check_db_cert(L"db", secure_var, cert, sha256hash) |
451 | == DATA_FOUND) { | |
47ebeb62 JB |
452 | verification_method = VERIFIED_BY_CERT; |
453 | update_verification_method(VERIFIED_BY_CERT); | |
454 | return EFI_SUCCESS; | |
455 | } | |
cbef697a | 456 | } |
47ebeb62 | 457 | |
0a6565c5 | 458 | if (check_db_hash(L"MokList", shim_var, sha256hash, SHA256_DIGEST_SIZE, |
cbef697a PJ |
459 | EFI_CERT_SHA256_GUID) == DATA_FOUND) { |
460 | verification_method = VERIFIED_BY_HASH; | |
461 | update_verification_method(VERIFIED_BY_HASH); | |
0a6565c5 | 462 | return EFI_SUCCESS; |
cbef697a | 463 | } |
8044a321 PJ |
464 | if (cert && check_db_cert(L"MokList", shim_var, cert, sha256hash) == |
465 | DATA_FOUND) { | |
cbef697a PJ |
466 | verification_method = VERIFIED_BY_CERT; |
467 | update_verification_method(VERIFIED_BY_CERT); | |
b2058cf8 | 468 | return EFI_SUCCESS; |
cbef697a | 469 | } |
b2058cf8 | 470 | |
cbef697a | 471 | update_verification_method(VERIFIED_BY_NOTHING); |
b2058cf8 MG |
472 | return EFI_ACCESS_DENIED; |
473 | } | |
474 | ||
6279b58e MG |
475 | /* |
476 | * Check whether we're in Secure Boot and user mode | |
477 | */ | |
478 | ||
479 | static BOOLEAN secure_mode (void) | |
480 | { | |
e60f1181 | 481 | if (user_insecure_mode) |
9eaadb0d MG |
482 | return FALSE; |
483 | ||
7a72592b | 484 | if (variable_is_secureboot() != 1) { |
e50cfe37 | 485 | if (verbose && !in_protocol) |
95c6743e | 486 | console_notify(L"Secure boot not enabled"); |
6b1f8796 PJ |
487 | return FALSE; |
488 | } | |
6279b58e | 489 | |
9ea3d9b4 PJ |
490 | /* If we /do/ have "SecureBoot", but /don't/ have "SetupMode", |
491 | * then the implementation is bad, but we assume that secure boot is | |
492 | * enabled according to the status of "SecureBoot". If we have both | |
493 | * of them, then "SetupMode" may tell us additional data, and we need | |
494 | * to consider it. | |
495 | */ | |
496 | if (variable_is_setupmode(0) == 1) { | |
e50cfe37 | 497 | if (verbose && !in_protocol) |
95c6743e | 498 | console_notify(L"Platform is in setup mode"); |
6279b58e MG |
499 | return FALSE; |
500 | } | |
501 | ||
502 | return TRUE; | |
503 | } | |
504 | ||
f898777d | 505 | /* |
f394b22e | 506 | * Calculate the SHA1 and SHA256 hashes of a binary |
f898777d | 507 | */ |
f394b22e | 508 | |
47a9d2c9 | 509 | static EFI_STATUS generate_hash (char *data, int datasize_in, |
f394b22e MG |
510 | PE_COFF_LOADER_IMAGE_CONTEXT *context, |
511 | UINT8 *sha256hash, UINT8 *sha1hash) | |
512 | ||
7f055335 | 513 | { |
ce6a5748 | 514 | unsigned int sha256ctxsize, sha1ctxsize; |
47a9d2c9 | 515 | unsigned int size = datasize_in; |
ce6a5748 | 516 | void *sha256ctx = NULL, *sha1ctx = NULL; |
7f055335 MG |
517 | char *hashbase; |
518 | unsigned int hashsize; | |
7f055335 | 519 | unsigned int SumOfBytesHashed, SumOfSectionBytes; |
3df68c18 | 520 | unsigned int index, pos; |
47a9d2c9 | 521 | unsigned int datasize; |
7f055335 | 522 | EFI_IMAGE_SECTION_HEADER *Section; |
0db1af8a | 523 | EFI_IMAGE_SECTION_HEADER *SectionHeader = NULL; |
f394b22e | 524 | EFI_STATUS status = EFI_SUCCESS; |
16a83563 PJ |
525 | EFI_IMAGE_DOS_HEADER *DosHdr = (void *)data; |
526 | unsigned int PEHdr_offset = 0; | |
7f055335 | 527 | |
47a9d2c9 | 528 | if (datasize_in < 0) { |
e50cfe37 | 529 | perror(L"Invalid data size\n"); |
47a9d2c9 KC |
530 | return EFI_INVALID_PARAMETER; |
531 | } | |
532 | size = datasize = (unsigned int)datasize_in; | |
533 | ||
16a83563 PJ |
534 | if (datasize <= sizeof (*DosHdr) || |
535 | DosHdr->e_magic != EFI_IMAGE_DOS_SIGNATURE) { | |
e50cfe37 | 536 | perror(L"Invalid signature\n"); |
16a83563 PJ |
537 | return EFI_INVALID_PARAMETER; |
538 | } | |
539 | PEHdr_offset = DosHdr->e_lfanew; | |
540 | ||
541 | sha256ctxsize = Sha256GetContextSize(); | |
542 | sha256ctx = AllocatePool(sha256ctxsize); | |
543 | ||
544 | sha1ctxsize = Sha1GetContextSize(); | |
545 | sha1ctx = AllocatePool(sha1ctxsize); | |
546 | ||
ce6a5748 | 547 | if (!sha256ctx || !sha1ctx) { |
e50cfe37 | 548 | perror(L"Unable to allocate memory for hash context\n"); |
7f055335 MG |
549 | return EFI_OUT_OF_RESOURCES; |
550 | } | |
551 | ||
ce6a5748 | 552 | if (!Sha256Init(sha256ctx) || !Sha1Init(sha1ctx)) { |
e50cfe37 | 553 | perror(L"Unable to initialise hash\n"); |
7f055335 MG |
554 | status = EFI_OUT_OF_RESOURCES; |
555 | goto done; | |
556 | } | |
557 | ||
558 | /* Hash start to checksum */ | |
7db60bd8 | 559 | hashbase = data; |
7f055335 MG |
560 | hashsize = (char *)&context->PEHdr->Pe32.OptionalHeader.CheckSum - |
561 | hashbase; | |
562 | ||
ce6a5748 MG |
563 | if (!(Sha256Update(sha256ctx, hashbase, hashsize)) || |
564 | !(Sha1Update(sha1ctx, hashbase, hashsize))) { | |
e50cfe37 | 565 | perror(L"Unable to generate hash\n"); |
7f055335 MG |
566 | status = EFI_OUT_OF_RESOURCES; |
567 | goto done; | |
568 | } | |
569 | ||
570 | /* Hash post-checksum to start of certificate table */ | |
571 | hashbase = (char *)&context->PEHdr->Pe32.OptionalHeader.CheckSum + | |
572 | sizeof (int); | |
573 | hashsize = (char *)context->SecDir - hashbase; | |
574 | ||
ce6a5748 MG |
575 | if (!(Sha256Update(sha256ctx, hashbase, hashsize)) || |
576 | !(Sha1Update(sha1ctx, hashbase, hashsize))) { | |
e50cfe37 | 577 | perror(L"Unable to generate hash\n"); |
7f055335 MG |
578 | status = EFI_OUT_OF_RESOURCES; |
579 | goto done; | |
580 | } | |
581 | ||
582 | /* Hash end of certificate table to end of image header */ | |
b6a12d99 | 583 | #if __LP64__ |
7f055335 MG |
584 | hashbase = (char *) &context->PEHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1]; |
585 | hashsize = context->PEHdr->Pe32Plus.OptionalHeader.SizeOfHeaders - | |
7db60bd8 | 586 | (int) ((char *) (&context->PEHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1]) - data); |
b6a12d99 M |
587 | #else |
588 | hashbase = (char *) &context->PEHdr->Pe32.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1]; | |
589 | hashsize = context->PEHdr->Pe32.OptionalHeader.SizeOfHeaders - | |
590 | (int) ((char *) (&context->PEHdr->Pe32.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1]) - data); | |
591 | #endif | |
0db1af8a | 592 | |
ce6a5748 MG |
593 | if (!(Sha256Update(sha256ctx, hashbase, hashsize)) || |
594 | !(Sha1Update(sha1ctx, hashbase, hashsize))) { | |
e50cfe37 | 595 | perror(L"Unable to generate hash\n"); |
7f055335 MG |
596 | status = EFI_OUT_OF_RESOURCES; |
597 | goto done; | |
598 | } | |
599 | ||
f898777d | 600 | /* Sort sections */ |
b6a12d99 | 601 | #if __LP64__ |
7f055335 | 602 | SumOfBytesHashed = context->PEHdr->Pe32Plus.OptionalHeader.SizeOfHeaders; |
b6a12d99 M |
603 | #else |
604 | SumOfBytesHashed = context->PEHdr->Pe32.OptionalHeader.SizeOfHeaders; | |
605 | #endif | |
7f055335 | 606 | |
47a9d2c9 | 607 | /* Validate section locations and sizes */ |
a63d665f | 608 | for (index = 0, SumOfSectionBytes = 0; index < context->PEHdr->Pe32.FileHeader.NumberOfSections; index++) { |
47a9d2c9 KC |
609 | EFI_IMAGE_SECTION_HEADER *SectionPtr; |
610 | ||
611 | /* Validate SectionPtr is within image */ | |
612 | SectionPtr = ImageAddress(data, datasize, | |
16a83563 | 613 | PEHdr_offset + |
47a9d2c9 KC |
614 | sizeof (UINT32) + |
615 | sizeof (EFI_IMAGE_FILE_HEADER) + | |
616 | context->PEHdr->Pe32.FileHeader.SizeOfOptionalHeader + | |
617 | (index * sizeof(*SectionPtr))); | |
618 | if (!SectionPtr) { | |
e50cfe37 | 619 | perror(L"Malformed section %d\n", index); |
47a9d2c9 KC |
620 | status = EFI_INVALID_PARAMETER; |
621 | goto done; | |
622 | } | |
623 | /* Validate section size is within image. */ | |
624 | if (SectionPtr->SizeOfRawData > | |
625 | datasize - SumOfBytesHashed - SumOfSectionBytes) { | |
e50cfe37 | 626 | perror(L"Malformed section %d size\n", index); |
47a9d2c9 KC |
627 | status = EFI_INVALID_PARAMETER; |
628 | goto done; | |
629 | } | |
630 | SumOfSectionBytes += SectionPtr->SizeOfRawData; | |
7f055335 MG |
631 | } |
632 | ||
633 | SectionHeader = (EFI_IMAGE_SECTION_HEADER *) AllocateZeroPool (sizeof (EFI_IMAGE_SECTION_HEADER) * context->PEHdr->Pe32.FileHeader.NumberOfSections); | |
634 | if (SectionHeader == NULL) { | |
e50cfe37 | 635 | perror(L"Unable to allocate section header\n"); |
7f055335 MG |
636 | status = EFI_OUT_OF_RESOURCES; |
637 | goto done; | |
638 | } | |
639 | ||
47a9d2c9 | 640 | /* Already validated above */ |
16a83563 PJ |
641 | Section = ImageAddress(data, datasize, |
642 | PEHdr_offset + | |
643 | sizeof (UINT32) + | |
47a9d2c9 KC |
644 | sizeof (EFI_IMAGE_FILE_HEADER) + |
645 | context->PEHdr->Pe32.FileHeader.SizeOfOptionalHeader); | |
646 | ||
7f055335 MG |
647 | /* Sort the section headers */ |
648 | for (index = 0; index < context->PEHdr->Pe32.FileHeader.NumberOfSections; index++) { | |
649 | pos = index; | |
650 | while ((pos > 0) && (Section->PointerToRawData < SectionHeader[pos - 1].PointerToRawData)) { | |
651 | CopyMem (&SectionHeader[pos], &SectionHeader[pos - 1], sizeof (EFI_IMAGE_SECTION_HEADER)); | |
652 | pos--; | |
653 | } | |
654 | CopyMem (&SectionHeader[pos], Section, sizeof (EFI_IMAGE_SECTION_HEADER)); | |
655 | Section += 1; | |
656 | } | |
657 | ||
658 | /* Hash the sections */ | |
659 | for (index = 0; index < context->PEHdr->Pe32.FileHeader.NumberOfSections; index++) { | |
660 | Section = &SectionHeader[index]; | |
661 | if (Section->SizeOfRawData == 0) { | |
662 | continue; | |
663 | } | |
cf718e19 | 664 | hashbase = ImageAddress(data, size, Section->PointerToRawData); |
7f055335 | 665 | |
f898777d | 666 | if (!hashbase) { |
e50cfe37 | 667 | perror(L"Malformed section header\n"); |
cbe21407 MG |
668 | status = EFI_INVALID_PARAMETER; |
669 | goto done; | |
f898777d MG |
670 | } |
671 | ||
47a9d2c9 KC |
672 | /* Verify hashsize within image. */ |
673 | if (Section->SizeOfRawData > | |
674 | datasize - Section->PointerToRawData) { | |
e50cfe37 | 675 | perror(L"Malformed section raw size %d\n", index); |
47a9d2c9 KC |
676 | status = EFI_INVALID_PARAMETER; |
677 | goto done; | |
678 | } | |
679 | hashsize = (unsigned int) Section->SizeOfRawData; | |
680 | ||
ce6a5748 MG |
681 | if (!(Sha256Update(sha256ctx, hashbase, hashsize)) || |
682 | !(Sha1Update(sha1ctx, hashbase, hashsize))) { | |
e50cfe37 | 683 | perror(L"Unable to generate hash\n"); |
7f055335 MG |
684 | status = EFI_OUT_OF_RESOURCES; |
685 | goto done; | |
686 | } | |
687 | SumOfBytesHashed += Section->SizeOfRawData; | |
688 | } | |
689 | ||
690 | /* Hash all remaining data */ | |
47a9d2c9 | 691 | if (datasize > SumOfBytesHashed) { |
7db60bd8 | 692 | hashbase = data + SumOfBytesHashed; |
7f055335 | 693 | hashsize = (unsigned int)( |
47a9d2c9 | 694 | datasize - |
b6a12d99 | 695 | #if __LP64__ |
7f055335 | 696 | context->PEHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size - |
b6a12d99 M |
697 | #else |
698 | context->PEHdr->Pe32.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size - | |
699 | #endif | |
7f055335 MG |
700 | SumOfBytesHashed); |
701 | ||
ce6a5748 MG |
702 | if (!(Sha256Update(sha256ctx, hashbase, hashsize)) || |
703 | !(Sha1Update(sha1ctx, hashbase, hashsize))) { | |
e50cfe37 | 704 | perror(L"Unable to generate hash\n"); |
7f055335 MG |
705 | status = EFI_OUT_OF_RESOURCES; |
706 | goto done; | |
707 | } | |
708 | } | |
709 | ||
ce6a5748 MG |
710 | if (!(Sha256Final(sha256ctx, sha256hash)) || |
711 | !(Sha1Final(sha1ctx, sha1hash))) { | |
e50cfe37 | 712 | perror(L"Unable to finalise hash\n"); |
7f055335 MG |
713 | status = EFI_OUT_OF_RESOURCES; |
714 | goto done; | |
715 | } | |
716 | ||
f394b22e MG |
717 | done: |
718 | if (SectionHeader) | |
719 | FreePool(SectionHeader); | |
720 | if (sha1ctx) | |
721 | FreePool(sha1ctx); | |
722 | if (sha256ctx) | |
723 | FreePool(sha256ctx); | |
724 | ||
725 | return status; | |
726 | } | |
727 | ||
20f6cde6 MG |
728 | /* |
729 | * Ensure that the MOK database hasn't been set or modified from an OS | |
730 | */ | |
0a6565c5 MG |
731 | static EFI_STATUS verify_mok (void) { |
732 | EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; | |
733 | EFI_STATUS status = EFI_SUCCESS; | |
7f0208a0 | 734 | UINT8 *MokListData = NULL; |
0a6565c5 MG |
735 | UINTN MokListDataSize = 0; |
736 | UINT32 attributes; | |
737 | ||
7f0208a0 GCPL |
738 | status = get_variable_attr(L"MokList", &MokListData, &MokListDataSize, |
739 | shim_lock_guid, &attributes); | |
0a6565c5 | 740 | |
42426e6e | 741 | if (!EFI_ERROR(status) && attributes & EFI_VARIABLE_RUNTIME_ACCESS) { |
e50cfe37 | 742 | perror(L"MokList is compromised!\nErase all keys in MokList!\n"); |
0a6565c5 | 743 | if (LibDeleteVariable(L"MokList", &shim_lock_guid) != EFI_SUCCESS) { |
e50cfe37 | 744 | perror(L"Failed to erase MokList\n"); |
42426e6e | 745 | return EFI_ACCESS_DENIED; |
0a6565c5 | 746 | } |
0a6565c5 MG |
747 | } |
748 | ||
ca2e00d0 GCPL |
749 | if (MokListData) |
750 | FreePool(MokListData); | |
751 | ||
0a6565c5 MG |
752 | return EFI_SUCCESS; |
753 | } | |
754 | ||
f394b22e MG |
755 | /* |
756 | * Check that the signature is valid and matches the binary | |
757 | */ | |
758 | static EFI_STATUS verify_buffer (char *data, int datasize, | |
0a6565c5 | 759 | PE_COFF_LOADER_IMAGE_CONTEXT *context) |
f394b22e | 760 | { |
f394b22e MG |
761 | UINT8 sha256hash[SHA256_DIGEST_SIZE]; |
762 | UINT8 sha1hash[SHA1_DIGEST_SIZE]; | |
763 | EFI_STATUS status = EFI_ACCESS_DENIED; | |
8044a321 | 764 | WIN_CERTIFICATE_EFI_PKCS *cert = NULL; |
f394b22e MG |
765 | unsigned int size = datasize; |
766 | ||
8044a321 PJ |
767 | if (context->SecDir->Size != 0) { |
768 | cert = ImageAddress (data, size, | |
769 | context->SecDir->VirtualAddress); | |
832e5161 | 770 | |
8044a321 | 771 | if (!cert) { |
e50cfe37 | 772 | perror(L"Certificate located outside the image\n"); |
8044a321 PJ |
773 | return EFI_INVALID_PARAMETER; |
774 | } | |
f394b22e | 775 | |
8044a321 PJ |
776 | if (cert->Hdr.wCertificateType != |
777 | WIN_CERT_TYPE_PKCS_SIGNED_DATA) { | |
e50cfe37 | 778 | perror(L"Unsupported certificate type %x\n", |
8044a321 PJ |
779 | cert->Hdr.wCertificateType); |
780 | return EFI_UNSUPPORTED; | |
781 | } | |
f394b22e MG |
782 | } |
783 | ||
784 | status = generate_hash(data, datasize, context, sha256hash, sha1hash); | |
785 | ||
786 | if (status != EFI_SUCCESS) | |
787 | return status; | |
788 | ||
20f6cde6 MG |
789 | /* |
790 | * Check that the MOK database hasn't been modified | |
791 | */ | |
42426e6e AB |
792 | status = verify_mok(); |
793 | if (status != EFI_SUCCESS) | |
794 | return status; | |
0a6565c5 | 795 | |
20f6cde6 MG |
796 | /* |
797 | * Ensure that the binary isn't blacklisted | |
798 | */ | |
ce6a5748 | 799 | status = check_blacklist(cert, sha256hash, sha1hash); |
3df68c18 MG |
800 | |
801 | if (status != EFI_SUCCESS) { | |
e50cfe37 | 802 | perror(L"Binary is blacklisted\n"); |
f394b22e | 803 | return status; |
3df68c18 MG |
804 | } |
805 | ||
20f6cde6 MG |
806 | /* |
807 | * Check whether the binary is whitelisted in any of the firmware | |
808 | * databases | |
809 | */ | |
0a6565c5 | 810 | status = check_whitelist(cert, sha256hash, sha1hash); |
4ab978a3 | 811 | if (status == EFI_SUCCESS) |
0a6565c5 | 812 | return status; |
b2058cf8 | 813 | |
8044a321 PJ |
814 | if (cert) { |
815 | /* | |
816 | * Check against the shim build key | |
817 | */ | |
818 | if (AuthenticodeVerify(cert->CertData, | |
ef8c9962 MG |
819 | context->SecDir->Size - sizeof(cert->Hdr), |
820 | shim_cert, sizeof(shim_cert), sha256hash, | |
821 | SHA256_DIGEST_SIZE)) { | |
8044a321 PJ |
822 | status = EFI_SUCCESS; |
823 | return status; | |
824 | } | |
ef8c9962 MG |
825 | |
826 | ||
8044a321 PJ |
827 | /* |
828 | * And finally, check against shim's built-in key | |
829 | */ | |
830 | if (AuthenticodeVerify(cert->CertData, | |
13422973 | 831 | context->SecDir->Size - sizeof(cert->Hdr), |
ce6a5748 | 832 | vendor_cert, vendor_cert_size, sha256hash, |
13422973 | 833 | SHA256_DIGEST_SIZE)) { |
8044a321 PJ |
834 | status = EFI_SUCCESS; |
835 | return status; | |
836 | } | |
13422973 GCPL |
837 | } |
838 | ||
13422973 | 839 | status = EFI_ACCESS_DENIED; |
7f055335 | 840 | |
f898777d MG |
841 | return status; |
842 | } | |
b2fe1780 | 843 | |
f898777d MG |
844 | /* |
845 | * Read the binary header and grab appropriate information from it | |
846 | */ | |
ce78d2d2 | 847 | static EFI_STATUS read_header(void *data, unsigned int datasize, |
f898777d MG |
848 | PE_COFF_LOADER_IMAGE_CONTEXT *context) |
849 | { | |
7db60bd8 MG |
850 | EFI_IMAGE_DOS_HEADER *DosHdr = data; |
851 | EFI_IMAGE_OPTIONAL_HEADER_UNION *PEHdr = data; | |
b6a12d99 | 852 | unsigned long HeaderWithoutDataDir, SectionHeaderOffset, OptHeaderSize; |
b2fe1780 | 853 | |
cbe21407 | 854 | if (datasize < sizeof(EFI_IMAGE_DOS_HEADER)) { |
e50cfe37 | 855 | perror(L"Invalid image\n"); |
cbe21407 MG |
856 | return EFI_UNSUPPORTED; |
857 | } | |
858 | ||
f898777d | 859 | if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) |
7db60bd8 | 860 | PEHdr = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)((char *)data + DosHdr->e_lfanew); |
b6a12d99 M |
861 | #if __LP64__ |
862 | context->NumberOfRvaAndSizes = PEHdr->Pe32Plus.OptionalHeader.NumberOfRvaAndSizes; | |
863 | context->SizeOfHeaders = PEHdr->Pe32Plus.OptionalHeader.SizeOfHeaders; | |
864 | context->ImageSize = PEHdr->Pe32Plus.OptionalHeader.SizeOfImage; | |
865 | OptHeaderSize = sizeof(EFI_IMAGE_OPTIONAL_HEADER64); | |
866 | #else | |
867 | context->NumberOfRvaAndSizes = PEHdr->Pe32.OptionalHeader.NumberOfRvaAndSizes; | |
868 | context->SizeOfHeaders = PEHdr->Pe32.OptionalHeader.SizeOfHeaders; | |
869 | context->ImageSize = (UINT64)PEHdr->Pe32.OptionalHeader.SizeOfImage; | |
870 | OptHeaderSize = sizeof(EFI_IMAGE_OPTIONAL_HEADER32); | |
871 | #endif | |
872 | context->NumberOfSections = PEHdr->Pe32.FileHeader.NumberOfSections; | |
b2fe1780 | 873 | |
b6a12d99 | 874 | if (EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES < context->NumberOfRvaAndSizes) { |
e50cfe37 | 875 | perror(L"Image header too small\n"); |
7de74e67 PJ |
876 | return EFI_UNSUPPORTED; |
877 | } | |
878 | ||
b6a12d99 | 879 | HeaderWithoutDataDir = OptHeaderSize |
7de74e67 | 880 | - sizeof (EFI_IMAGE_DATA_DIRECTORY) * EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES; |
b6a12d99 M |
881 | if (((UINT32)PEHdr->Pe32.FileHeader.SizeOfOptionalHeader - HeaderWithoutDataDir) != |
882 | context->NumberOfRvaAndSizes * sizeof (EFI_IMAGE_DATA_DIRECTORY)) { | |
e50cfe37 | 883 | perror(L"Image header overflows data directory\n"); |
7de74e67 PJ |
884 | return EFI_UNSUPPORTED; |
885 | } | |
886 | ||
887 | SectionHeaderOffset = DosHdr->e_lfanew | |
888 | + sizeof (UINT32) | |
889 | + sizeof (EFI_IMAGE_FILE_HEADER) | |
b6a12d99 M |
890 | + PEHdr->Pe32.FileHeader.SizeOfOptionalHeader; |
891 | if (((UINT32)context->ImageSize - SectionHeaderOffset) / EFI_IMAGE_SIZEOF_SECTION_HEADER | |
892 | <= context->NumberOfSections) { | |
e50cfe37 | 893 | perror(L"Image sections overflow image size\n"); |
7de74e67 PJ |
894 | return EFI_UNSUPPORTED; |
895 | } | |
896 | ||
b6a12d99 M |
897 | if ((context->SizeOfHeaders - SectionHeaderOffset) / EFI_IMAGE_SIZEOF_SECTION_HEADER |
898 | < (UINT32)context->NumberOfSections) { | |
e50cfe37 | 899 | perror(L"Image sections overflow section headers\n"); |
7de74e67 PJ |
900 | return EFI_UNSUPPORTED; |
901 | } | |
902 | ||
cbe21407 | 903 | if ((((UINT8 *)PEHdr - (UINT8 *)data) + sizeof(EFI_IMAGE_OPTIONAL_HEADER_UNION)) > datasize) { |
e50cfe37 | 904 | perror(L"Invalid image\n"); |
cbe21407 MG |
905 | return EFI_UNSUPPORTED; |
906 | } | |
907 | ||
f898777d | 908 | if (PEHdr->Te.Signature != EFI_IMAGE_NT_SIGNATURE) { |
e50cfe37 | 909 | perror(L"Unsupported image type\n"); |
f898777d MG |
910 | return EFI_UNSUPPORTED; |
911 | } | |
b2fe1780 | 912 | |
f898777d | 913 | if (PEHdr->Pe32.FileHeader.Characteristics & EFI_IMAGE_FILE_RELOCS_STRIPPED) { |
e50cfe37 | 914 | perror(L"Unsupported image - Relocations have been stripped\n"); |
f898777d MG |
915 | return EFI_UNSUPPORTED; |
916 | } | |
b2fe1780 | 917 | |
f898777d | 918 | context->PEHdr = PEHdr; |
b6a12d99 | 919 | #if __LP64__ |
f898777d | 920 | context->ImageAddress = PEHdr->Pe32Plus.OptionalHeader.ImageBase; |
f898777d MG |
921 | context->EntryPoint = PEHdr->Pe32Plus.OptionalHeader.AddressOfEntryPoint; |
922 | context->RelocDir = &PEHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC]; | |
f898777d | 923 | context->SecDir = (EFI_IMAGE_DATA_DIRECTORY *) &PEHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]; |
b6a12d99 M |
924 | #else |
925 | context->ImageAddress = PEHdr->Pe32.OptionalHeader.ImageBase; | |
926 | context->EntryPoint = PEHdr->Pe32.OptionalHeader.AddressOfEntryPoint; | |
927 | context->RelocDir = &PEHdr->Pe32.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC]; | |
928 | context->SecDir = (EFI_IMAGE_DATA_DIRECTORY *) &PEHdr->Pe32.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]; | |
929 | #endif | |
930 | context->FirstSection = (EFI_IMAGE_SECTION_HEADER *)((char *)PEHdr + PEHdr->Pe32.FileHeader.SizeOfOptionalHeader + sizeof(UINT32) + sizeof(EFI_IMAGE_FILE_HEADER)); | |
b2fe1780 | 931 | |
cbe21407 | 932 | if (context->ImageSize < context->SizeOfHeaders) { |
e50cfe37 | 933 | perror(L"Invalid image\n"); |
cbe21407 | 934 | return EFI_UNSUPPORTED; |
b2fe1780 MG |
935 | } |
936 | ||
47a9d2c9 KC |
937 | if ((unsigned long)((UINT8 *)context->SecDir - (UINT8 *)data) > |
938 | (datasize - sizeof(EFI_IMAGE_DATA_DIRECTORY))) { | |
e50cfe37 | 939 | perror(L"Invalid image\n"); |
cbe21407 MG |
940 | return EFI_UNSUPPORTED; |
941 | } | |
942 | ||
ce78d2d2 | 943 | if (context->SecDir->VirtualAddress >= datasize) { |
e50cfe37 | 944 | perror(L"Malformed security header\n"); |
f898777d MG |
945 | return EFI_INVALID_PARAMETER; |
946 | } | |
f898777d | 947 | return EFI_SUCCESS; |
b2fe1780 MG |
948 | } |
949 | ||
00c84188 PJ |
950 | static const UINT16 machine_type = |
951 | #if defined(__x86_64__) | |
952 | IMAGE_FILE_MACHINE_X64; | |
953 | #elif defined(__aarch64__) | |
954 | IMAGE_FILE_MACHINE_ARM64; | |
955 | #elif defined(__arm__) | |
956 | IMAGE_FILE_MACHINE_ARMTHUMB_MIXED; | |
957 | #elif defined(__i386__) || defined(__i486__) || defined(__i686__) | |
958 | IMAGE_FILE_MACHINE_I386; | |
959 | #elif defined(__ia64__) | |
960 | IMAGE_FILE_MACHINE_IA64; | |
961 | #else | |
962 | #error this architecture is not supported by shim | |
963 | #endif | |
e50cfe37 | 964 | |
f898777d MG |
965 | /* |
966 | * Once the image has been loaded it needs to be validated and relocated | |
967 | */ | |
000c565c GCPL |
968 | static EFI_STATUS handle_image (void *data, unsigned int datasize, |
969 | EFI_LOADED_IMAGE *li) | |
b2fe1780 MG |
970 | { |
971 | EFI_STATUS efi_status; | |
972 | char *buffer; | |
47a9d2c9 KC |
973 | int i; |
974 | unsigned int size; | |
b2fe1780 | 975 | EFI_IMAGE_SECTION_HEADER *Section; |
0e6b0195 | 976 | char *base, *end; |
b2fe1780 | 977 | PE_COFF_LOADER_IMAGE_CONTEXT context; |
7f055335 | 978 | |
20f6cde6 MG |
979 | /* |
980 | * The binary header contains relevant context and section pointers | |
981 | */ | |
ce78d2d2 | 982 | efi_status = read_header(data, datasize, &context); |
b2fe1780 | 983 | if (efi_status != EFI_SUCCESS) { |
e50cfe37 | 984 | perror(L"Failed to read header: %r\n", efi_status); |
b2fe1780 MG |
985 | return efi_status; |
986 | } | |
987 | ||
00c84188 PJ |
988 | if (context.PEHdr->Pe32.FileHeader.Machine != machine_type) { |
989 | perror(L"Image is for a different architecture\n"); | |
990 | return EFI_UNSUPPORTED; | |
991 | } | |
992 | ||
20f6cde6 MG |
993 | /* |
994 | * We only need to verify the binary if we're in secure mode | |
995 | */ | |
6279b58e | 996 | if (secure_mode ()) { |
0a6565c5 | 997 | efi_status = verify_buffer(data, datasize, &context); |
7f055335 | 998 | |
4ab978a3 PJ |
999 | if (EFI_ERROR(efi_status)) { |
1000 | console_error(L"Verification failed", efi_status); | |
6279b58e | 1001 | return efi_status; |
4ab978a3 PJ |
1002 | } else { |
1003 | if (verbose) | |
1004 | console_notify(L"Verification succeeded"); | |
6279b58e | 1005 | } |
7f055335 MG |
1006 | } |
1007 | ||
b2fe1780 MG |
1008 | buffer = AllocatePool(context.ImageSize); |
1009 | ||
0e6b0195 | 1010 | if (!buffer) { |
e50cfe37 | 1011 | perror(L"Failed to allocate image buffer\n"); |
0e6b0195 MG |
1012 | return EFI_OUT_OF_RESOURCES; |
1013 | } | |
1014 | ||
7db60bd8 | 1015 | CopyMem(buffer, data, context.SizeOfHeaders); |
b2fe1780 | 1016 | |
20f6cde6 MG |
1017 | /* |
1018 | * Copy the executable's sections to their desired offsets | |
1019 | */ | |
b2fe1780 | 1020 | Section = context.FirstSection; |
4ca60879 AB |
1021 | for (i = 0; i < context.NumberOfSections; i++, Section++) { |
1022 | if (Section->Characteristics & 0x02000000) | |
1023 | /* section has EFI_IMAGE_SCN_MEM_DISCARDABLE attr set */ | |
1024 | continue; | |
1025 | ||
b2fe1780 MG |
1026 | size = Section->Misc.VirtualSize; |
1027 | ||
1028 | if (size > Section->SizeOfRawData) | |
1029 | size = Section->SizeOfRawData; | |
1030 | ||
1031 | base = ImageAddress (buffer, context.ImageSize, Section->VirtualAddress); | |
1032 | end = ImageAddress (buffer, context.ImageSize, Section->VirtualAddress + size - 1); | |
1033 | ||
1034 | if (!base || !end) { | |
e50cfe37 | 1035 | perror(L"Invalid section size\n"); |
b2fe1780 | 1036 | return EFI_UNSUPPORTED; |
7de74e67 PJ |
1037 | } |
1038 | ||
1039 | if (Section->VirtualAddress < context.SizeOfHeaders || | |
1040 | Section->PointerToRawData < context.SizeOfHeaders) { | |
e50cfe37 | 1041 | perror(L"Section is inside image headers\n"); |
7de74e67 | 1042 | return EFI_UNSUPPORTED; |
b2fe1780 MG |
1043 | } |
1044 | ||
b2fe1780 | 1045 | if (Section->SizeOfRawData > 0) |
7db60bd8 | 1046 | CopyMem(base, data + Section->PointerToRawData, size); |
b2fe1780 MG |
1047 | |
1048 | if (size < Section->Misc.VirtualSize) | |
1049 | ZeroMem (base + size, Section->Misc.VirtualSize - size); | |
b2fe1780 MG |
1050 | } |
1051 | ||
20f6cde6 MG |
1052 | /* |
1053 | * Run the relocation fixups | |
1054 | */ | |
7db60bd8 | 1055 | efi_status = relocate_coff(&context, buffer); |
b2fe1780 MG |
1056 | |
1057 | if (efi_status != EFI_SUCCESS) { | |
e50cfe37 | 1058 | perror(L"Relocation failed: %r\n", efi_status); |
0db1af8a | 1059 | FreePool(buffer); |
b2fe1780 MG |
1060 | return efi_status; |
1061 | } | |
1062 | ||
0e6b0195 | 1063 | entry_point = ImageAddress(buffer, context.ImageSize, context.EntryPoint); |
20f6cde6 MG |
1064 | /* |
1065 | * grub needs to know its location and size in memory, so fix up | |
1066 | * the loaded image protocol values | |
1067 | */ | |
5fe882ba MG |
1068 | li->ImageBase = buffer; |
1069 | li->ImageSize = context.ImageSize; | |
1070 | ||
09e2c939 GCPL |
1071 | /* Pass the load options to the second stage loader */ |
1072 | li->LoadOptions = load_options; | |
1073 | li->LoadOptionsSize = load_options_size; | |
1074 | ||
0e6b0195 | 1075 | if (!entry_point) { |
e50cfe37 | 1076 | perror(L"Invalid entry point\n"); |
0db1af8a | 1077 | FreePool(buffer); |
0e6b0195 MG |
1078 | return EFI_UNSUPPORTED; |
1079 | } | |
b2fe1780 MG |
1080 | |
1081 | return EFI_SUCCESS; | |
1082 | } | |
1083 | ||
6d6b0221 PJ |
1084 | static int |
1085 | should_use_fallback(EFI_HANDLE image_handle) | |
1086 | { | |
1087 | EFI_GUID loaded_image_protocol = LOADED_IMAGE_PROTOCOL; | |
1088 | EFI_LOADED_IMAGE *li; | |
6d6b0221 | 1089 | unsigned int pathlen = 0; |
fe8527aa | 1090 | CHAR16 *bootpath = NULL; |
6d6b0221 | 1091 | EFI_FILE_IO_INTERFACE *fio = NULL; |
35b0b55b PJ |
1092 | EFI_FILE *vh; |
1093 | EFI_FILE *fh; | |
6d6b0221 | 1094 | EFI_STATUS rc; |
fe8527aa | 1095 | int ret = 0; |
6d6b0221 PJ |
1096 | |
1097 | rc = uefi_call_wrapper(BS->HandleProtocol, 3, image_handle, | |
1098 | &loaded_image_protocol, (void **)&li); | |
c9d11306 | 1099 | if (EFI_ERROR(rc)) { |
e50cfe37 | 1100 | perror(L"Could not get image for bootx64.efi: %r\n", rc); |
6d6b0221 | 1101 | return 0; |
c9d11306 | 1102 | } |
6d6b0221 | 1103 | |
2e7fc28d | 1104 | bootpath = DevicePathToStr(li->FilePath); |
6d6b0221 PJ |
1105 | |
1106 | /* Check the beginning of the string and the end, to avoid | |
1107 | * caring about which arch this is. */ | |
1108 | /* I really don't know why, but sometimes bootpath gives us | |
1109 | * L"\\EFI\\BOOT\\/BOOTX64.EFI". So just handle that here... | |
1110 | */ | |
1111 | if (StrnCaseCmp(bootpath, L"\\EFI\\BOOT\\BOOT", 14) && | |
1112 | StrnCaseCmp(bootpath, L"\\EFI\\BOOT\\/BOOT", 15)) | |
fe8527aa | 1113 | goto error; |
2e7fc28d | 1114 | |
6d6b0221 PJ |
1115 | pathlen = StrLen(bootpath); |
1116 | if (pathlen < 5 || StrCaseCmp(bootpath + pathlen - 4, L".EFI")) | |
fe8527aa | 1117 | goto error; |
6d6b0221 | 1118 | |
6d6b0221 | 1119 | rc = uefi_call_wrapper(BS->HandleProtocol, 3, li->DeviceHandle, |
35b0b55b | 1120 | &FileSystemProtocol, (void **)&fio); |
c9d11306 | 1121 | if (EFI_ERROR(rc)) { |
e50cfe37 | 1122 | perror(L"Could not get fio for li->DeviceHandle: %r\n", rc); |
fe8527aa | 1123 | goto error; |
c9d11306 | 1124 | } |
e50cfe37 | 1125 | |
6d6b0221 | 1126 | rc = uefi_call_wrapper(fio->OpenVolume, 2, fio, &vh); |
c9d11306 | 1127 | if (EFI_ERROR(rc)) { |
e50cfe37 | 1128 | perror(L"Could not open fio volume: %r\n", rc); |
fe8527aa | 1129 | goto error; |
c9d11306 | 1130 | } |
6d6b0221 PJ |
1131 | |
1132 | rc = uefi_call_wrapper(vh->Open, 5, vh, &fh, L"\\EFI\\BOOT" FALLBACK, | |
5bb3e64e | 1133 | EFI_FILE_MODE_READ, 0); |
6d6b0221 | 1134 | if (EFI_ERROR(rc)) { |
b32a3ce1 PJ |
1135 | /* Do not print the error here - this is an acceptable case |
1136 | * for removable media, where we genuinely don't want | |
1137 | * fallback.efi to exist. | |
1138 | * Print(L"Could not open \"\\EFI\\BOOT%s\": %d\n", FALLBACK, | |
1139 | * rc); | |
1140 | */ | |
6d6b0221 | 1141 | uefi_call_wrapper(vh->Close, 1, vh); |
fe8527aa | 1142 | goto error; |
6d6b0221 PJ |
1143 | } |
1144 | uefi_call_wrapper(fh->Close, 1, fh); | |
1145 | uefi_call_wrapper(vh->Close, 1, vh); | |
1146 | ||
fe8527aa GCPL |
1147 | ret = 1; |
1148 | error: | |
1149 | if (bootpath) | |
1150 | FreePool(bootpath); | |
1151 | ||
1152 | return ret; | |
6d6b0221 PJ |
1153 | } |
1154 | ||
20f6cde6 MG |
1155 | /* |
1156 | * Generate the path of an executable given shim's path and the name | |
1157 | * of the executable | |
1158 | */ | |
822d089e | 1159 | static EFI_STATUS generate_path(EFI_LOADED_IMAGE *li, CHAR16 *ImagePath, |
c9022560 | 1160 | CHAR16 **PathName) |
f898777d | 1161 | { |
0db1af8a | 1162 | EFI_DEVICE_PATH *devpath; |
47a9d2c9 KC |
1163 | unsigned int i; |
1164 | int j, last = -1; | |
db54b0a4 MG |
1165 | unsigned int pathlen = 0; |
1166 | EFI_STATUS efi_status = EFI_SUCCESS; | |
bc6aaefa | 1167 | CHAR16 *bootpath; |
f898777d | 1168 | |
0db1af8a | 1169 | devpath = li->FilePath; |
f898777d | 1170 | |
bc6aaefa | 1171 | bootpath = DevicePathToStr(devpath); |
f898777d | 1172 | |
bc6aaefa | 1173 | pathlen = StrLen(bootpath); |
f898777d | 1174 | |
f9f81a22 GCPL |
1175 | /* |
1176 | * DevicePathToStr() concatenates two nodes with '/'. | |
1177 | * Convert '/' to '\\'. | |
1178 | */ | |
1179 | for (i = 0; i < pathlen; i++) { | |
1180 | if (bootpath[i] == '/') | |
1181 | bootpath[i] = '\\'; | |
1182 | } | |
436afcc2 | 1183 | |
bc6aaefa | 1184 | for (i=pathlen; i>0; i--) { |
436afcc2 GCPL |
1185 | if (bootpath[i] == '\\' && bootpath[i-1] == '\\') |
1186 | bootpath[i] = '/'; | |
1187 | else if (last == -1 && bootpath[i] == '\\') | |
1188 | last = i; | |
1189 | } | |
1190 | ||
1191 | if (last == -1 && bootpath[0] == '\\') | |
1192 | last = 0; | |
1193 | bootpath[last+1] = '\0'; | |
1194 | ||
1195 | if (last > 0) { | |
1196 | for (i = 0, j = 0; bootpath[i] != '\0'; i++) { | |
1197 | if (bootpath[i] != '/') { | |
1198 | bootpath[j] = bootpath[i]; | |
1199 | j++; | |
1200 | } | |
1201 | } | |
1202 | bootpath[j] = '\0'; | |
f898777d MG |
1203 | } |
1204 | ||
f9f81a22 GCPL |
1205 | while (*ImagePath == '\\') |
1206 | ImagePath++; | |
00ced0c1 | 1207 | |
822d089e | 1208 | *PathName = AllocatePool(StrSize(bootpath) + StrSize(ImagePath)); |
f898777d | 1209 | |
db54b0a4 | 1210 | if (!*PathName) { |
e50cfe37 | 1211 | perror(L"Failed to allocate path buffer\n"); |
0db1af8a MG |
1212 | efi_status = EFI_OUT_OF_RESOURCES; |
1213 | goto error; | |
f898777d MG |
1214 | } |
1215 | ||
bc6aaefa | 1216 | *PathName[0] = '\0'; |
155a76bb PJ |
1217 | if (StrnCaseCmp(bootpath, ImagePath, StrLen(bootpath))) |
1218 | StrCat(*PathName, bootpath); | |
822d089e | 1219 | StrCat(*PathName, ImagePath); |
db54b0a4 | 1220 | |
db54b0a4 | 1221 | error: |
f9f81a22 GCPL |
1222 | FreePool(bootpath); |
1223 | ||
db54b0a4 MG |
1224 | return efi_status; |
1225 | } | |
1226 | ||
1227 | /* | |
20f6cde6 | 1228 | * Open the second stage bootloader and read it into a buffer |
db54b0a4 | 1229 | */ |
822d089e GCPL |
1230 | static EFI_STATUS load_image (EFI_LOADED_IMAGE *li, void **data, |
1231 | int *datasize, CHAR16 *PathName) | |
db54b0a4 MG |
1232 | { |
1233 | EFI_GUID simple_file_system_protocol = SIMPLE_FILE_SYSTEM_PROTOCOL; | |
1234 | EFI_GUID file_info_id = EFI_FILE_INFO_ID; | |
1235 | EFI_STATUS efi_status; | |
1236 | EFI_HANDLE device; | |
1237 | EFI_FILE_INFO *fileinfo = NULL; | |
1238 | EFI_FILE_IO_INTERFACE *drive; | |
1239 | EFI_FILE *root, *grub; | |
6eb1eca4 | 1240 | UINTN buffersize = sizeof(EFI_FILE_INFO); |
db54b0a4 MG |
1241 | |
1242 | device = li->DeviceHandle; | |
f898777d | 1243 | |
20f6cde6 MG |
1244 | /* |
1245 | * Open the device | |
1246 | */ | |
0db1af8a | 1247 | efi_status = uefi_call_wrapper(BS->HandleProtocol, 3, device, |
ed711b02 MG |
1248 | &simple_file_system_protocol, |
1249 | (void **)&drive); | |
f898777d | 1250 | |
0db1af8a | 1251 | if (efi_status != EFI_SUCCESS) { |
e50cfe37 | 1252 | perror(L"Failed to find fs: %r\n", efi_status); |
0db1af8a MG |
1253 | goto error; |
1254 | } | |
f898777d | 1255 | |
0db1af8a MG |
1256 | efi_status = uefi_call_wrapper(drive->OpenVolume, 2, drive, &root); |
1257 | ||
1258 | if (efi_status != EFI_SUCCESS) { | |
e50cfe37 | 1259 | perror(L"Failed to open fs: %r\n", efi_status); |
0db1af8a MG |
1260 | goto error; |
1261 | } | |
f898777d | 1262 | |
20f6cde6 MG |
1263 | /* |
1264 | * And then open the file | |
1265 | */ | |
f898777d MG |
1266 | efi_status = uefi_call_wrapper(root->Open, 5, root, &grub, PathName, |
1267 | EFI_FILE_MODE_READ, 0); | |
1268 | ||
1269 | if (efi_status != EFI_SUCCESS) { | |
e50cfe37 | 1270 | perror(L"Failed to open %s - %r\n", PathName, efi_status); |
0db1af8a MG |
1271 | goto error; |
1272 | } | |
1273 | ||
1274 | fileinfo = AllocatePool(buffersize); | |
1275 | ||
1276 | if (!fileinfo) { | |
e50cfe37 | 1277 | perror(L"Unable to allocate file info buffer\n"); |
0db1af8a MG |
1278 | efi_status = EFI_OUT_OF_RESOURCES; |
1279 | goto error; | |
f898777d MG |
1280 | } |
1281 | ||
20f6cde6 MG |
1282 | /* |
1283 | * Find out how big the file is in order to allocate the storage | |
1284 | * buffer | |
1285 | */ | |
f898777d MG |
1286 | efi_status = uefi_call_wrapper(grub->GetInfo, 4, grub, &file_info_id, |
1287 | &buffersize, fileinfo); | |
1288 | ||
1289 | if (efi_status == EFI_BUFFER_TOO_SMALL) { | |
cbe21407 | 1290 | FreePool(fileinfo); |
f898777d MG |
1291 | fileinfo = AllocatePool(buffersize); |
1292 | if (!fileinfo) { | |
e50cfe37 | 1293 | perror(L"Unable to allocate file info buffer\n"); |
0db1af8a MG |
1294 | efi_status = EFI_OUT_OF_RESOURCES; |
1295 | goto error; | |
f898777d MG |
1296 | } |
1297 | efi_status = uefi_call_wrapper(grub->GetInfo, 4, grub, | |
1298 | &file_info_id, &buffersize, | |
1299 | fileinfo); | |
1300 | } | |
1301 | ||
1302 | if (efi_status != EFI_SUCCESS) { | |
e50cfe37 | 1303 | perror(L"Unable to get file info: %r\n", efi_status); |
0db1af8a | 1304 | goto error; |
f898777d MG |
1305 | } |
1306 | ||
1307 | buffersize = fileinfo->FileSize; | |
0db1af8a | 1308 | |
7db60bd8 | 1309 | *data = AllocatePool(buffersize); |
f898777d | 1310 | |
7db60bd8 | 1311 | if (!*data) { |
e50cfe37 | 1312 | perror(L"Unable to allocate file buffer\n"); |
0db1af8a MG |
1313 | efi_status = EFI_OUT_OF_RESOURCES; |
1314 | goto error; | |
f898777d | 1315 | } |
20f6cde6 MG |
1316 | |
1317 | /* | |
1318 | * Perform the actual read | |
1319 | */ | |
f898777d | 1320 | efi_status = uefi_call_wrapper(grub->Read, 3, grub, &buffersize, |
7db60bd8 | 1321 | *data); |
f898777d | 1322 | |
0db1af8a MG |
1323 | if (efi_status == EFI_BUFFER_TOO_SMALL) { |
1324 | FreePool(*data); | |
1325 | *data = AllocatePool(buffersize); | |
1326 | efi_status = uefi_call_wrapper(grub->Read, 3, grub, | |
1327 | &buffersize, *data); | |
f898777d MG |
1328 | } |
1329 | ||
1330 | if (efi_status != EFI_SUCCESS) { | |
e50cfe37 | 1331 | perror(L"Unexpected return from initial read: %r, buffersize %x\n", efi_status, buffersize); |
0db1af8a | 1332 | goto error; |
f898777d MG |
1333 | } |
1334 | ||
7db60bd8 | 1335 | *datasize = buffersize; |
f898777d | 1336 | |
cbe21407 MG |
1337 | FreePool(fileinfo); |
1338 | ||
f898777d | 1339 | return EFI_SUCCESS; |
0db1af8a MG |
1340 | error: |
1341 | if (*data) { | |
1342 | FreePool(*data); | |
1343 | *data = NULL; | |
1344 | } | |
6f161626 | 1345 | |
0db1af8a MG |
1346 | if (fileinfo) |
1347 | FreePool(fileinfo); | |
1348 | return efi_status; | |
f898777d MG |
1349 | } |
1350 | ||
20f6cde6 MG |
1351 | /* |
1352 | * Protocol entry point. If secure boot is enabled, verify that the provided | |
1353 | * buffer is signed with a trusted key. | |
1354 | */ | |
db54b0a4 | 1355 | EFI_STATUS shim_verify (void *buffer, UINT32 size) |
f4b24734 MG |
1356 | { |
1357 | EFI_STATUS status; | |
1358 | PE_COFF_LOADER_IMAGE_CONTEXT context; | |
1359 | ||
cbef697a | 1360 | loader_is_participating = 1; |
e50cfe37 | 1361 | in_protocol = 1; |
cbef697a | 1362 | |
6279b58e MG |
1363 | if (!secure_mode()) |
1364 | return EFI_SUCCESS; | |
1365 | ||
ce78d2d2 | 1366 | status = read_header(buffer, size, &context); |
f4b24734 MG |
1367 | |
1368 | if (status != EFI_SUCCESS) | |
e50cfe37 | 1369 | goto done; |
f4b24734 | 1370 | |
0a6565c5 | 1371 | status = verify_buffer(buffer, size, &context); |
e50cfe37 GCPL |
1372 | done: |
1373 | in_protocol = 0; | |
1374 | return status; | |
1375 | } | |
1376 | ||
1377 | static EFI_STATUS shim_hash (char *data, int datasize, | |
1378 | PE_COFF_LOADER_IMAGE_CONTEXT *context, | |
1379 | UINT8 *sha256hash, UINT8 *sha1hash) | |
1380 | { | |
1381 | EFI_STATUS status; | |
1382 | ||
1383 | in_protocol = 1; | |
1384 | status = generate_hash(data, datasize, context, sha256hash, sha1hash); | |
1385 | in_protocol = 0; | |
1386 | ||
1387 | return status; | |
1388 | } | |
1389 | ||
1390 | static EFI_STATUS shim_read_header(void *data, unsigned int datasize, | |
1391 | PE_COFF_LOADER_IMAGE_CONTEXT *context) | |
1392 | { | |
1393 | EFI_STATUS status; | |
1394 | ||
1395 | in_protocol = 1; | |
1396 | status = read_header(data, datasize, context); | |
1397 | in_protocol = 0; | |
f4b24734 MG |
1398 | |
1399 | return status; | |
1400 | } | |
1401 | ||
20f6cde6 MG |
1402 | /* |
1403 | * Load and run an EFI executable | |
1404 | */ | |
cec6a0a9 | 1405 | EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath) |
822d089e | 1406 | { |
cec6a0a9 | 1407 | EFI_GUID loaded_image_protocol = LOADED_IMAGE_PROTOCOL; |
822d089e | 1408 | EFI_STATUS efi_status; |
cec6a0a9 | 1409 | EFI_LOADED_IMAGE *li, li_bak; |
1c595706 MG |
1410 | CHAR16 *PathName = NULL; |
1411 | void *sourcebuffer = NULL; | |
fbc486b5 | 1412 | UINT64 sourcesize = 0; |
822d089e GCPL |
1413 | void *data = NULL; |
1414 | int datasize; | |
1415 | ||
20f6cde6 MG |
1416 | /* |
1417 | * We need to refer to the loaded image protocol on the running | |
1418 | * binary in order to find our path | |
1419 | */ | |
cec6a0a9 | 1420 | efi_status = uefi_call_wrapper(BS->HandleProtocol, 3, image_handle, |
ed711b02 | 1421 | &loaded_image_protocol, (void **)&li); |
cec6a0a9 GCPL |
1422 | |
1423 | if (efi_status != EFI_SUCCESS) { | |
e50cfe37 | 1424 | perror(L"Unable to init protocol\n"); |
cec6a0a9 GCPL |
1425 | return efi_status; |
1426 | } | |
1427 | ||
20f6cde6 MG |
1428 | /* |
1429 | * Build a new path from the existing one plus the executable name | |
1430 | */ | |
c9022560 | 1431 | efi_status = generate_path(li, ImagePath, &PathName); |
cec6a0a9 GCPL |
1432 | |
1433 | if (efi_status != EFI_SUCCESS) { | |
e50cfe37 | 1434 | perror(L"Unable to generate path %s: %r\n", ImagePath, efi_status); |
cec6a0a9 GCPL |
1435 | goto done; |
1436 | } | |
1437 | ||
da49ac6d | 1438 | if (findNetboot(li->DeviceHandle)) { |
1c595706 MG |
1439 | efi_status = parseNetbootinfo(image_handle); |
1440 | if (efi_status != EFI_SUCCESS) { | |
e50cfe37 | 1441 | perror(L"Netboot parsing failed: %r\n", efi_status); |
1c595706 MG |
1442 | return EFI_PROTOCOL_ERROR; |
1443 | } | |
1444 | efi_status = FetchNetbootimage(image_handle, &sourcebuffer, | |
1445 | &sourcesize); | |
1446 | if (efi_status != EFI_SUCCESS) { | |
e50cfe37 | 1447 | perror(L"Unable to fetch TFTP image: %r\n", efi_status); |
1c595706 MG |
1448 | return efi_status; |
1449 | } | |
1450 | data = sourcebuffer; | |
1451 | datasize = sourcesize; | |
1452 | } else { | |
4ad234f1 MG |
1453 | /* |
1454 | * Read the new executable off disk | |
1455 | */ | |
1c595706 | 1456 | efi_status = load_image(li, &data, &datasize, PathName); |
822d089e | 1457 | |
1c595706 | 1458 | if (efi_status != EFI_SUCCESS) { |
e50cfe37 | 1459 | perror(L"Failed to load image %s: %r\n", PathName, efi_status); |
1c595706 MG |
1460 | goto done; |
1461 | } | |
822d089e GCPL |
1462 | } |
1463 | ||
20f6cde6 MG |
1464 | /* |
1465 | * We need to modify the loaded image protocol entry before running | |
1466 | * the new binary, so back it up | |
1467 | */ | |
822d089e GCPL |
1468 | CopyMem(&li_bak, li, sizeof(li_bak)); |
1469 | ||
20f6cde6 MG |
1470 | /* |
1471 | * Verify and, if appropriate, relocate and execute the executable | |
1472 | */ | |
822d089e GCPL |
1473 | efi_status = handle_image(data, datasize, li); |
1474 | ||
1475 | if (efi_status != EFI_SUCCESS) { | |
e50cfe37 | 1476 | perror(L"Failed to load image: %r\n", efi_status); |
822d089e GCPL |
1477 | CopyMem(li, &li_bak, sizeof(li_bak)); |
1478 | goto done; | |
1479 | } | |
1480 | ||
cbef697a PJ |
1481 | loader_is_participating = 0; |
1482 | ||
20f6cde6 MG |
1483 | /* |
1484 | * The binary is trusted and relocated. Run it | |
1485 | */ | |
cbe21407 | 1486 | efi_status = uefi_call_wrapper(entry_point, 2, image_handle, systab); |
822d089e | 1487 | |
20f6cde6 MG |
1488 | /* |
1489 | * Restore our original loaded image values | |
1490 | */ | |
822d089e GCPL |
1491 | CopyMem(li, &li_bak, sizeof(li_bak)); |
1492 | done: | |
cbe21407 MG |
1493 | if (PathName) |
1494 | FreePool(PathName); | |
1495 | ||
1496 | if (data) | |
1497 | FreePool(data); | |
1498 | ||
822d089e GCPL |
1499 | return efi_status; |
1500 | } | |
1501 | ||
20f6cde6 MG |
1502 | /* |
1503 | * Load and run grub. If that fails because grub isn't trusted, load and | |
1504 | * run MokManager. | |
1505 | */ | |
db54b0a4 | 1506 | EFI_STATUS init_grub(EFI_HANDLE image_handle) |
b2fe1780 MG |
1507 | { |
1508 | EFI_STATUS efi_status; | |
f4b24734 | 1509 | |
6d6b0221 PJ |
1510 | if (should_use_fallback(image_handle)) |
1511 | efi_status = start_image(image_handle, FALLBACK); | |
1512 | else | |
1513 | efi_status = start_image(image_handle, second_stage); | |
b2fe1780 | 1514 | |
ef8c9962 MG |
1515 | if (efi_status != EFI_SUCCESS) |
1516 | efi_status = start_image(image_handle, MOK_MANAGER); | |
db54b0a4 MG |
1517 | |
1518 | return efi_status; | |
1519 | } | |
1520 | ||
20f6cde6 MG |
1521 | /* |
1522 | * Copy the boot-services only MokList variable to the runtime-accessible | |
1523 | * MokListRT variable. It's not marked NV, so the OS can't modify it. | |
1524 | */ | |
a903fb10 GCPL |
1525 | EFI_STATUS mirror_mok_list() |
1526 | { | |
1527 | EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; | |
1528 | EFI_STATUS efi_status; | |
7f0208a0 | 1529 | UINT8 *Data = NULL; |
a903fb10 | 1530 | UINTN DataSize = 0; |
4185c7d6 PJ |
1531 | void *FullData = NULL; |
1532 | UINTN FullDataSize = 0; | |
1533 | EFI_SIGNATURE_LIST *CertList = NULL; | |
1534 | EFI_SIGNATURE_DATA *CertData = NULL; | |
1535 | uint8_t *p = NULL; | |
a903fb10 | 1536 | |
7f0208a0 | 1537 | efi_status = get_variable(L"MokList", &Data, &DataSize, shim_lock_guid); |
4185c7d6 PJ |
1538 | if (efi_status != EFI_SUCCESS) |
1539 | DataSize = 0; | |
1540 | ||
1541 | FullDataSize = DataSize | |
1542 | + sizeof (*CertList) | |
1543 | + sizeof (EFI_GUID) | |
1544 | + vendor_cert_size | |
1545 | ; | |
1546 | FullData = AllocatePool(FullDataSize); | |
1547 | if (!FullData) { | |
e50cfe37 | 1548 | perror(L"Failed to allocate space for MokListRT\n"); |
4185c7d6 PJ |
1549 | return EFI_OUT_OF_RESOURCES; |
1550 | } | |
1551 | p = FullData; | |
a903fb10 | 1552 | |
4185c7d6 PJ |
1553 | if (efi_status == EFI_SUCCESS && DataSize > 0) { |
1554 | CopyMem(p, Data, DataSize); | |
1555 | p += DataSize; | |
a903fb10 | 1556 | } |
4185c7d6 PJ |
1557 | CertList = (EFI_SIGNATURE_LIST *)p; |
1558 | p += sizeof (*CertList); | |
1559 | CertData = (EFI_SIGNATURE_DATA *)p; | |
1560 | p += sizeof (EFI_GUID); | |
1561 | ||
1562 | CertList->SignatureType = EFI_CERT_X509_GUID; | |
1563 | CertList->SignatureListSize = vendor_cert_size | |
1564 | + sizeof (*CertList) | |
1565 | + sizeof (*CertData) | |
1566 | -1; | |
1567 | CertList->SignatureHeaderSize = 0; | |
1568 | CertList->SignatureSize = vendor_cert_size + sizeof (EFI_GUID); | |
1569 | ||
1570 | CertData->SignatureOwner = SHIM_LOCK_GUID; | |
1571 | CopyMem(p, vendor_cert, vendor_cert_size); | |
a903fb10 GCPL |
1572 | |
1573 | efi_status = uefi_call_wrapper(RT->SetVariable, 5, L"MokListRT", | |
1574 | &shim_lock_guid, | |
1575 | EFI_VARIABLE_BOOTSERVICE_ACCESS | |
1576 | | EFI_VARIABLE_RUNTIME_ACCESS, | |
4185c7d6 | 1577 | FullDataSize, FullData); |
a903fb10 | 1578 | if (efi_status != EFI_SUCCESS) { |
e50cfe37 | 1579 | perror(L"Failed to set MokListRT: %r\n", efi_status); |
a903fb10 GCPL |
1580 | } |
1581 | ||
a903fb10 GCPL |
1582 | return efi_status; |
1583 | } | |
1584 | ||
20f6cde6 MG |
1585 | /* |
1586 | * Check if a variable exists | |
1587 | */ | |
d5a2d9ea | 1588 | static BOOLEAN check_var(CHAR16 *varname) |
4b34567d GCPL |
1589 | { |
1590 | EFI_STATUS efi_status; | |
e470969e | 1591 | EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; |
1041805a | 1592 | UINTN size = sizeof(UINT32); |
801d1b93 | 1593 | UINT32 MokVar; |
e470969e GCPL |
1594 | UINT32 attributes; |
1595 | ||
d5a2d9ea | 1596 | efi_status = uefi_call_wrapper(RT->GetVariable, 5, varname, |
1041805a | 1597 | &shim_lock_guid, &attributes, |
d5a2d9ea | 1598 | &size, (void *)&MokVar); |
e470969e | 1599 | |
d5a2d9ea MG |
1600 | if (efi_status == EFI_SUCCESS || efi_status == EFI_BUFFER_TOO_SMALL) |
1601 | return TRUE; | |
4b34567d | 1602 | |
d5a2d9ea MG |
1603 | return FALSE; |
1604 | } | |
4b34567d | 1605 | |
20f6cde6 MG |
1606 | /* |
1607 | * If the OS has set any of these variables we need to drop into MOK and | |
1608 | * handle them appropriately | |
1609 | */ | |
d5a2d9ea MG |
1610 | EFI_STATUS check_mok_request(EFI_HANDLE image_handle) |
1611 | { | |
1612 | EFI_STATUS efi_status; | |
4b34567d | 1613 | |
d5a2d9ea | 1614 | if (check_var(L"MokNew") || check_var(L"MokSB") || |
92a136d8 | 1615 | check_var(L"MokPW") || check_var(L"MokAuth") || |
47ebeb62 | 1616 | check_var(L"MokDel") || check_var(L"MokDB")) { |
9272bc5b | 1617 | efi_status = start_image(image_handle, MOK_MANAGER); |
4b34567d | 1618 | |
9272bc5b | 1619 | if (efi_status != EFI_SUCCESS) { |
e50cfe37 | 1620 | perror(L"Failed to start MokManager: %r\n", efi_status); |
9272bc5b MG |
1621 | return efi_status; |
1622 | } | |
4b34567d | 1623 | } |
4b34567d | 1624 | |
9272bc5b | 1625 | return EFI_SUCCESS; |
4b34567d GCPL |
1626 | } |
1627 | ||
20f6cde6 MG |
1628 | /* |
1629 | * Verify that MokSBState is valid, and if appropriate set insecure mode | |
1630 | */ | |
1631 | ||
9eaadb0d MG |
1632 | static EFI_STATUS check_mok_sb (void) |
1633 | { | |
1634 | EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; | |
1635 | EFI_STATUS status = EFI_SUCCESS; | |
875eb1b9 GCPL |
1636 | UINT8 MokSBState; |
1637 | UINTN MokSBStateSize = sizeof(MokSBState); | |
9eaadb0d MG |
1638 | UINT32 attributes; |
1639 | ||
e60f1181 | 1640 | user_insecure_mode = 0; |
0948ac09 PJ |
1641 | ignore_db = 0; |
1642 | ||
875eb1b9 GCPL |
1643 | status = uefi_call_wrapper(RT->GetVariable, 5, L"MokSBState", &shim_lock_guid, |
1644 | &attributes, &MokSBStateSize, &MokSBState); | |
9eaadb0d MG |
1645 | if (status != EFI_SUCCESS) |
1646 | return EFI_ACCESS_DENIED; | |
1647 | ||
20f6cde6 MG |
1648 | /* |
1649 | * Delete and ignore the variable if it's been set from or could be | |
1650 | * modified by the OS | |
1651 | */ | |
9eaadb0d | 1652 | if (attributes & EFI_VARIABLE_RUNTIME_ACCESS) { |
e50cfe37 | 1653 | perror(L"MokSBState is compromised! Clearing it\n"); |
9eaadb0d | 1654 | if (LibDeleteVariable(L"MokSBState", &shim_lock_guid) != EFI_SUCCESS) { |
e50cfe37 | 1655 | perror(L"Failed to erase MokSBState\n"); |
9eaadb0d MG |
1656 | } |
1657 | status = EFI_ACCESS_DENIED; | |
1658 | } else { | |
875eb1b9 | 1659 | if (MokSBState == 1) { |
e60f1181 | 1660 | user_insecure_mode = 1; |
9eaadb0d MG |
1661 | } |
1662 | } | |
1663 | ||
1664 | return status; | |
4b34567d GCPL |
1665 | } |
1666 | ||
47ebeb62 JB |
1667 | /* |
1668 | * Verify that MokDBState is valid, and if appropriate set ignore db mode | |
1669 | */ | |
1670 | ||
1671 | static EFI_STATUS check_mok_db (void) | |
1672 | { | |
1673 | EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; | |
1674 | EFI_STATUS status = EFI_SUCCESS; | |
875eb1b9 GCPL |
1675 | UINT8 MokDBState; |
1676 | UINTN MokDBStateSize = sizeof(MokDBStateSize); | |
47ebeb62 JB |
1677 | UINT32 attributes; |
1678 | ||
875eb1b9 GCPL |
1679 | status = uefi_call_wrapper(RT->GetVariable, 5, L"MokDBState", &shim_lock_guid, |
1680 | &attributes, &MokDBStateSize, &MokDBState); | |
47ebeb62 JB |
1681 | if (status != EFI_SUCCESS) |
1682 | return EFI_ACCESS_DENIED; | |
1683 | ||
1684 | ignore_db = 0; | |
1685 | ||
1686 | /* | |
1687 | * Delete and ignore the variable if it's been set from or could be | |
1688 | * modified by the OS | |
1689 | */ | |
1690 | if (attributes & EFI_VARIABLE_RUNTIME_ACCESS) { | |
e50cfe37 | 1691 | perror(L"MokDBState is compromised! Clearing it\n"); |
47ebeb62 | 1692 | if (LibDeleteVariable(L"MokDBState", &shim_lock_guid) != EFI_SUCCESS) { |
e50cfe37 | 1693 | perror(L"Failed to erase MokDBState\n"); |
47ebeb62 JB |
1694 | } |
1695 | status = EFI_ACCESS_DENIED; | |
1696 | } else { | |
875eb1b9 | 1697 | if (MokDBState == 1) { |
47ebeb62 JB |
1698 | ignore_db = 1; |
1699 | } | |
1700 | } | |
1701 | ||
47ebeb62 JB |
1702 | return status; |
1703 | } | |
1704 | ||
1705 | static EFI_STATUS mok_ignore_db() | |
1706 | { | |
1707 | EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; | |
1708 | EFI_STATUS efi_status = EFI_SUCCESS; | |
1709 | UINT8 Data = 1; | |
1710 | UINTN DataSize = sizeof(UINT8); | |
1711 | ||
1712 | check_mok_db(); | |
1713 | ||
1714 | if (ignore_db) { | |
1715 | efi_status = uefi_call_wrapper(RT->SetVariable, 5, L"MokIgnoreDB", | |
1716 | &shim_lock_guid, | |
1717 | EFI_VARIABLE_BOOTSERVICE_ACCESS | |
1718 | | EFI_VARIABLE_RUNTIME_ACCESS, | |
1719 | DataSize, (void *)&Data); | |
1720 | if (efi_status != EFI_SUCCESS) { | |
e50cfe37 | 1721 | perror(L"Failed to set MokIgnoreDB: %r\n", efi_status); |
47ebeb62 JB |
1722 | } |
1723 | } | |
1724 | ||
1725 | return efi_status; | |
1726 | ||
1727 | } | |
1728 | ||
09e2c939 GCPL |
1729 | /* |
1730 | * Check the load options to specify the second stage loader | |
1731 | */ | |
1732 | EFI_STATUS set_second_stage (EFI_HANDLE image_handle) | |
1733 | { | |
1734 | EFI_STATUS status; | |
1735 | EFI_LOADED_IMAGE *li; | |
1736 | CHAR16 *start = NULL, *c; | |
47a9d2c9 KC |
1737 | unsigned int i; |
1738 | int remaining_size = 0; | |
0283024e | 1739 | CHAR16 *loader_str = NULL; |
47a9d2c9 | 1740 | unsigned int loader_len = 0; |
09e2c939 GCPL |
1741 | |
1742 | second_stage = DEFAULT_LOADER; | |
1743 | load_options = NULL; | |
1744 | load_options_size = 0; | |
1745 | ||
1746 | status = uefi_call_wrapper(BS->HandleProtocol, 3, image_handle, | |
1747 | &LoadedImageProtocol, (void **) &li); | |
1748 | if (status != EFI_SUCCESS) { | |
e50cfe37 | 1749 | perror (L"Failed to get load options: %r\n", status); |
09e2c939 GCPL |
1750 | return status; |
1751 | } | |
1752 | ||
1753 | /* Expect a CHAR16 string with at least one CHAR16 */ | |
1754 | if (li->LoadOptionsSize < 4 || li->LoadOptionsSize % 2 != 0) { | |
1755 | return EFI_BAD_BUFFER_SIZE; | |
1756 | } | |
1757 | c = (CHAR16 *)(li->LoadOptions + (li->LoadOptionsSize - 2)); | |
1758 | if (*c != L'\0') { | |
1759 | return EFI_BAD_BUFFER_SIZE; | |
1760 | } | |
1761 | ||
0283024e GCPL |
1762 | /* |
1763 | * UEFI shell copies the whole line of the command into LoadOptions. | |
1764 | * We ignore the string before the first L' ', i.e. the name of this | |
1765 | * program. | |
1766 | */ | |
09e2c939 GCPL |
1767 | for (i = 0; i < li->LoadOptionsSize; i += 2) { |
1768 | c = (CHAR16 *)(li->LoadOptions + i); | |
1769 | if (*c == L' ') { | |
1770 | *c = L'\0'; | |
1771 | start = c + 1; | |
1772 | remaining_size = li->LoadOptionsSize - i - 2; | |
1773 | break; | |
1774 | } | |
1775 | } | |
1776 | ||
0283024e GCPL |
1777 | if (!start || remaining_size <= 0) |
1778 | return EFI_SUCCESS; | |
6e1bd3dc | 1779 | |
0283024e GCPL |
1780 | for (i = 0; start[i] != '\0'; i++) { |
1781 | if (start[i] == L' ' || start[i] == L'\0') | |
1782 | break; | |
1783 | loader_len++; | |
1784 | } | |
1785 | ||
1786 | /* | |
1787 | * Setup the name of the alternative loader and the LoadOptions for | |
1788 | * the loader | |
1789 | */ | |
1790 | if (loader_len > 0) { | |
1791 | loader_str = AllocatePool((loader_len + 1) * sizeof(CHAR16)); | |
1792 | if (!loader_str) { | |
e50cfe37 | 1793 | perror(L"Failed to allocate loader string\n"); |
0283024e GCPL |
1794 | return EFI_OUT_OF_RESOURCES; |
1795 | } | |
1796 | for (i = 0; i < loader_len; i++) | |
1797 | loader_str[i] = start[i]; | |
1798 | loader_str[loader_len] = L'\0'; | |
1799 | ||
1800 | second_stage = loader_str; | |
09e2c939 GCPL |
1801 | load_options = start; |
1802 | load_options_size = remaining_size; | |
1803 | } | |
1804 | ||
1805 | return EFI_SUCCESS; | |
1806 | } | |
1807 | ||
cf90edff PJ |
1808 | static SHIM_LOCK shim_lock_interface; |
1809 | static EFI_HANDLE shim_lock_handle; | |
1810 | ||
1811 | EFI_STATUS | |
1812 | install_shim_protocols(void) | |
1813 | { | |
1814 | EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; | |
1815 | EFI_STATUS efi_status; | |
1816 | /* | |
1817 | * Install the protocol | |
1818 | */ | |
1819 | efi_status = uefi_call_wrapper(BS->InstallProtocolInterface, 4, | |
1820 | &shim_lock_handle, &shim_lock_guid, | |
1821 | EFI_NATIVE_INTERFACE, &shim_lock_interface); | |
1822 | if (EFI_ERROR(efi_status)) { | |
1823 | console_error(L"Could not install security protocol", | |
1824 | efi_status); | |
1825 | return efi_status; | |
1826 | } | |
1827 | ||
1828 | #if defined(OVERRIDE_SECURITY_POLICY) | |
1829 | /* | |
1830 | * Install the security protocol hook | |
1831 | */ | |
1832 | security_policy_install(shim_verify); | |
1833 | #endif | |
1834 | ||
1835 | return EFI_SUCCESS; | |
1836 | } | |
1837 | ||
1838 | void | |
1839 | uninstall_shim_protocols(void) | |
db54b0a4 MG |
1840 | { |
1841 | EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; | |
cf90edff PJ |
1842 | #if defined(OVERRIDE_SECURITY_POLICY) |
1843 | /* | |
1844 | * Clean up the security protocol hook | |
1845 | */ | |
1846 | security_policy_uninstall(); | |
1847 | #endif | |
1848 | ||
1849 | /* | |
1850 | * If we're back here then clean everything up before exiting | |
1851 | */ | |
1852 | uefi_call_wrapper(BS->UninstallProtocolInterface, 3, shim_lock_handle, | |
1853 | &shim_lock_guid, &shim_lock_interface); | |
1854 | } | |
1855 | ||
1856 | EFI_STATUS efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *passed_systab) | |
1857 | { | |
0a232ca9 | 1858 | EFI_STATUS efi_status; |
db54b0a4 | 1859 | |
cbef697a PJ |
1860 | verification_method = VERIFIED_BY_NOTHING; |
1861 | ||
a1f28635 PJ |
1862 | vendor_cert_size = cert_table.vendor_cert_size; |
1863 | vendor_dbx_size = cert_table.vendor_dbx_size; | |
1864 | vendor_cert = (UINT8 *)&cert_table + cert_table.vendor_cert_offset; | |
1865 | vendor_dbx = (UINT8 *)&cert_table + cert_table.vendor_dbx_offset; | |
1866 | ||
20f6cde6 MG |
1867 | /* |
1868 | * Set up the shim lock protocol so that grub and MokManager can | |
1869 | * call back in and use shim functions | |
1870 | */ | |
db54b0a4 | 1871 | shim_lock_interface.Verify = shim_verify; |
e50cfe37 GCPL |
1872 | shim_lock_interface.Hash = shim_hash; |
1873 | shim_lock_interface.Context = shim_read_header; | |
db54b0a4 MG |
1874 | |
1875 | systab = passed_systab; | |
1876 | ||
20f6cde6 MG |
1877 | /* |
1878 | * Ensure that gnu-efi functions are available | |
1879 | */ | |
db54b0a4 MG |
1880 | InitializeLib(image_handle, systab); |
1881 | ||
bc71a15e | 1882 | setup_console(1); |
4ab978a3 | 1883 | setup_verbosity(); |
bc71a15e | 1884 | |
4ab978a3 | 1885 | dprinta(shim_version); |
0fb089ee | 1886 | |
09e2c939 GCPL |
1887 | /* Set the second stage loader */ |
1888 | set_second_stage (image_handle); | |
1889 | ||
20f6cde6 MG |
1890 | /* |
1891 | * Check whether the user has configured the system to run in | |
1892 | * insecure mode | |
1893 | */ | |
9eaadb0d MG |
1894 | check_mok_sb(); |
1895 | ||
20f6cde6 MG |
1896 | /* |
1897 | * Tell the user that we're in insecure mode if necessary | |
1898 | */ | |
e60f1181 | 1899 | if (user_insecure_mode) { |
9eaadb0d MG |
1900 | Print(L"Booting in insecure mode\n"); |
1901 | uefi_call_wrapper(BS->Stall, 1, 2000000); | |
e60f1181 | 1902 | } else if (secure_mode()) { |
7c1f49da MG |
1903 | if (vendor_cert_size || vendor_dbx_size) { |
1904 | /* | |
1905 | * If shim includes its own certificates then ensure | |
1906 | * that anything it boots has performed some | |
1907 | * validation of the next image. | |
1908 | */ | |
1909 | hook_system_services(systab); | |
1910 | loader_is_participating = 0; | |
1911 | } | |
9eaadb0d MG |
1912 | } |
1913 | ||
cf90edff PJ |
1914 | efi_status = install_shim_protocols(); |
1915 | if (EFI_ERROR(efi_status)) | |
1d563059 | 1916 | return efi_status; |
59dcd9d1 | 1917 | |
20f6cde6 MG |
1918 | /* |
1919 | * Enter MokManager if necessary | |
1920 | */ | |
4b34567d GCPL |
1921 | efi_status = check_mok_request(image_handle); |
1922 | ||
20f6cde6 MG |
1923 | /* |
1924 | * Copy the MOK list to a runtime variable so the kernel can make | |
1925 | * use of it | |
1926 | */ | |
a903fb10 GCPL |
1927 | efi_status = mirror_mok_list(); |
1928 | ||
47ebeb62 JB |
1929 | /* |
1930 | * Create the runtime MokIgnoreDB variable so the kernel can make | |
1931 | * use of it | |
1932 | */ | |
1933 | efi_status = mok_ignore_db(); | |
1934 | ||
20f6cde6 MG |
1935 | /* |
1936 | * Hand over control to the second stage bootloader | |
1937 | */ | |
db54b0a4 | 1938 | |
0a232ca9 MG |
1939 | efi_status = init_grub(image_handle); |
1940 | ||
cf90edff | 1941 | uninstall_shim_protocols(); |
59dcd9d1 | 1942 | /* |
98a99578 | 1943 | * Remove our hooks from system services. |
59dcd9d1 | 1944 | */ |
98a99578 | 1945 | unhook_system_services(); |
59dcd9d1 | 1946 | |
0283024e GCPL |
1947 | /* |
1948 | * Free the space allocated for the alternative 2nd stage loader | |
1949 | */ | |
1950 | if (load_options_size > 0) | |
1951 | FreePool(second_stage); | |
1952 | ||
bc71a15e PJ |
1953 | setup_console(0); |
1954 | ||
0a232ca9 | 1955 | return efi_status; |
b2fe1780 | 1956 | } |