]> git.proxmox.com Git - efi-boot-shim.git/blob - MokManager.c
Reallocate the DevPath space for the volume label
[efi-boot-shim.git] / MokManager.c
1 #include <efi.h>
2 #include <efilib.h>
3 #include <Library/BaseCryptLib.h>
4 #include <openssl/x509.h>
5 #include "shim.h"
6 #include "signature.h"
7 #include "PeImage.h"
8
9 #define PASSWORD_MAX 16
10 #define PASSWORD_MIN 8
11
12 #ifndef SHIM_VENDOR
13 #define SHIM_VENDOR L"Shim"
14 #endif
15
16 #define EFI_VARIABLE_APPEND_WRITE 0x00000040
17
18 struct menu_item {
19 CHAR16 *text;
20 INTN (* callback)(void *data, void *data2, void *data3);
21 void *data;
22 void *data2;
23 void *data3;
24 UINTN colour;
25 };
26
27 typedef struct {
28 UINT32 MokSize;
29 UINT8 *Mok;
30 } __attribute__ ((packed)) MokListNode;
31
32 static EFI_INPUT_KEY get_keystroke (void)
33 {
34 EFI_INPUT_KEY key;
35 UINTN EventIndex;
36
37 uefi_call_wrapper(BS->WaitForEvent, 3, 1, &ST->ConIn->WaitForKey,
38 &EventIndex);
39 uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, ST->ConIn, &key);
40
41 return key;
42 }
43
44 static EFI_STATUS get_sha1sum (void *Data, int DataSize, UINT8 *hash)
45 {
46 EFI_STATUS status;
47 unsigned int ctxsize;
48 void *ctx = NULL;
49
50 ctxsize = Sha1GetContextSize();
51 ctx = AllocatePool(ctxsize);
52
53 if (!ctx) {
54 Print(L"Unable to allocate memory for hash context\n");
55 return EFI_OUT_OF_RESOURCES;
56 }
57
58 if (!Sha1Init(ctx)) {
59 Print(L"Unable to initialise hash\n");
60 status = EFI_OUT_OF_RESOURCES;
61 goto done;
62 }
63
64 if (!(Sha1Update(ctx, Data, DataSize))) {
65 Print(L"Unable to generate hash\n");
66 status = EFI_OUT_OF_RESOURCES;
67 goto done;
68 }
69
70 if (!(Sha1Final(ctx, hash))) {
71 Print(L"Unable to finalise hash\n");
72 status = EFI_OUT_OF_RESOURCES;
73 goto done;
74 }
75
76 status = EFI_SUCCESS;
77 done:
78 return status;
79 }
80
81 static MokListNode *build_mok_list(UINT32 num, void *Data, UINTN DataSize) {
82 MokListNode *list;
83 EFI_SIGNATURE_LIST *CertList = Data;
84 EFI_SIGNATURE_DATA *Cert;
85 EFI_GUID CertType = EfiCertX509Guid;
86 EFI_GUID HashType = EfiHashSha256Guid;
87 UINTN dbsize = DataSize;
88 UINTN count = 0;
89
90 list = AllocatePool(sizeof(MokListNode) * num);
91
92 if (!list) {
93 Print(L"Unable to allocate MOK list\n");
94 return NULL;
95 }
96
97 while ((dbsize > 0) && (dbsize >= CertList->SignatureListSize)) {
98 if ((CompareGuid (&CertList->SignatureType, &CertType) != 0) &&
99 (CompareGuid (&CertList->SignatureType, &HashType) != 0)) {
100 dbsize -= CertList->SignatureListSize;
101 CertList = (EFI_SIGNATURE_LIST *)((UINT8 *) CertList +
102 CertList->SignatureSize);
103 continue;
104 }
105
106 if ((CompareGuid (&CertList->SignatureType, &HashType) == 0) &&
107 (CertList->SignatureSize != 48)) {
108 dbsize -= CertList->SignatureListSize;
109 CertList = (EFI_SIGNATURE_LIST *)((UINT8 *) CertList +
110 CertList->SignatureSize);
111 continue;
112 }
113
114 Cert = (EFI_SIGNATURE_DATA *) (((UINT8 *) CertList) +
115 sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize);
116
117 list[count].MokSize = CertList->SignatureSize;
118 list[count].Mok = (void *)Cert->SignatureData;
119
120 count++;
121 dbsize -= CertList->SignatureListSize;
122 CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList +
123 CertList->SignatureSize);
124 }
125
126 return list;
127 }
128
129 static void print_x509_name (X509_NAME *X509Name, CHAR16 *name)
130 {
131 char *str;
132
133 str = X509_NAME_oneline(X509Name, NULL, 0);
134 if (str) {
135 Print(L" %s:\n %a\n", name, str);
136 OPENSSL_free(str);
137 }
138 }
139
140 static const char *mon[12]= {
141 "Jan","Feb","Mar","Apr","May","Jun",
142 "Jul","Aug","Sep","Oct","Nov","Dec"
143 };
144
145 static void print_x509_GENERALIZEDTIME_time (ASN1_TIME *time, CHAR16 *time_string)
146 {
147 char *v;
148 int gmt = 0;
149 int i;
150 int y = 0,M = 0,d = 0,h = 0,m = 0,s = 0;
151 char *f = NULL;
152 int f_len = 0;
153
154 i=time->length;
155 v=(char *)time->data;
156
157 if (i < 12)
158 goto error;
159
160 if (v[i-1] == 'Z')
161 gmt=1;
162
163 for (i=0; i<12; i++) {
164 if ((v[i] > '9') || (v[i] < '0'))
165 goto error;
166 }
167
168 y = (v[0]-'0')*1000+(v[1]-'0')*100 + (v[2]-'0')*10+(v[3]-'0');
169 M = (v[4]-'0')*10+(v[5]-'0');
170
171 if ((M > 12) || (M < 1))
172 goto error;
173
174 d = (v[6]-'0')*10+(v[7]-'0');
175 h = (v[8]-'0')*10+(v[9]-'0');
176 m = (v[10]-'0')*10+(v[11]-'0');
177
178 if (time->length >= 14 &&
179 (v[12] >= '0') && (v[12] <= '9') &&
180 (v[13] >= '0') && (v[13] <= '9')) {
181 s = (v[12]-'0')*10+(v[13]-'0');
182 /* Check for fractions of seconds. */
183 if (time->length >= 15 && v[14] == '.') {
184 int l = time->length;
185 f = &v[14]; /* The decimal point. */
186 f_len = 1;
187 while (14 + f_len < l && f[f_len] >= '0' &&
188 f[f_len] <= '9')
189 ++f_len;
190 }
191 }
192
193 SPrint(time_string, 0, L"%a %2d %02d:%02d:%02d%.*a %d%a",
194 mon[M-1], d, h, m, s, f_len, f, y, (gmt)?" GMT":"");
195 error:
196 return;
197 }
198
199 static void print_x509_UTCTIME_time (ASN1_TIME *time, CHAR16 *time_string)
200 {
201 char *v;
202 int gmt=0;
203 int i;
204 int y = 0,M = 0,d = 0,h = 0,m = 0,s = 0;
205
206 i=time->length;
207 v=(char *)time->data;
208
209 if (i < 10)
210 goto error;
211
212 if (v[i-1] == 'Z')
213 gmt=1;
214
215 for (i=0; i<10; i++)
216 if ((v[i] > '9') || (v[i] < '0'))
217 goto error;
218
219 y = (v[0]-'0')*10+(v[1]-'0');
220
221 if (y < 50)
222 y+=100;
223
224 M = (v[2]-'0')*10+(v[3]-'0');
225
226 if ((M > 12) || (M < 1))
227 goto error;
228
229 d = (v[4]-'0')*10+(v[5]-'0');
230 h = (v[6]-'0')*10+(v[7]-'0');
231 m = (v[8]-'0')*10+(v[9]-'0');
232
233 if (time->length >=12 &&
234 (v[10] >= '0') && (v[10] <= '9') &&
235 (v[11] >= '0') && (v[11] <= '9'))
236 s = (v[10]-'0')*10+(v[11]-'0');
237
238 SPrint(time_string, 0, L"%a %2d %02d:%02d:%02d %d%a",
239 mon[M-1], d, h, m, s, y+1900, (gmt)?" GMT":"");
240 error:
241 return;
242 }
243
244 static void print_x509_time (ASN1_TIME *time, CHAR16 *name)
245 {
246 CHAR16 time_string[30];
247
248 if (time->type == V_ASN1_UTCTIME) {
249 print_x509_UTCTIME_time(time, time_string);
250 } else if (time->type == V_ASN1_GENERALIZEDTIME) {
251 print_x509_GENERALIZEDTIME_time(time, time_string);
252 } else {
253 time_string[0] = '\0';
254 }
255
256 Print(L" %s:\n %s\n", name, time_string);
257 }
258
259 static void show_x509_info (X509 *X509Cert)
260 {
261 ASN1_INTEGER *serial;
262 BIGNUM *bnser;
263 unsigned char hexbuf[30];
264 X509_NAME *X509Name;
265 ASN1_TIME *time;
266
267 serial = X509_get_serialNumber(X509Cert);
268 if (serial) {
269 int i, n;
270 bnser = ASN1_INTEGER_to_BN(serial, NULL);
271 n = BN_bn2bin(bnser, hexbuf);
272 Print(L" Serial Number:\n ");
273 for (i = 0; i < n-1; i++) {
274 Print(L"%02x:", hexbuf[i]);
275 }
276 Print(L"%02x\n", hexbuf[n-1]);
277 }
278
279 X509Name = X509_get_issuer_name(X509Cert);
280 if (X509Name) {
281 print_x509_name(X509Name, L"Issuer");
282 }
283
284 X509Name = X509_get_subject_name(X509Cert);
285 if (X509Name) {
286 print_x509_name(X509Name, L"Subject");
287 }
288
289 time = X509_get_notBefore(X509Cert);
290 if (time) {
291 print_x509_time(time, L"Validity from");
292 }
293
294 time = X509_get_notAfter(X509Cert);
295 if (time) {
296 print_x509_time(time, L"Validity till");
297 }
298 }
299
300 static void show_mok_info (void *Mok, UINTN MokSize)
301 {
302 EFI_STATUS efi_status;
303 UINT8 hash[SHA1_DIGEST_SIZE];
304 unsigned int i;
305 X509 *X509Cert;
306
307 if (!Mok || MokSize == 0)
308 return;
309
310 if (MokSize != 48) {
311 if (X509ConstructCertificate(Mok, MokSize, (UINT8 **) &X509Cert) &&
312 X509Cert != NULL) {
313 show_x509_info(X509Cert);
314 X509_free(X509Cert);
315 } else {
316 Print(L" Not a valid X509 certificate: %x\n\n",
317 ((UINT32 *)Mok)[0]);
318 return;
319 }
320 } else {
321 Print(L"SHA256 hash:\n ");
322 for (i = 0; i < SHA256_DIGEST_SIZE; i++) {
323 Print(L" %02x", ((UINT8 *)Mok)[i]);
324 if (i % 10 == 9)
325 Print(L"\n ");
326 }
327 Print(L"\n");
328 }
329 efi_status = get_sha1sum(Mok, MokSize, hash);
330
331 if (efi_status != EFI_SUCCESS) {
332 Print(L"Failed to compute MOK fingerprint\n");
333 return;
334 }
335
336 Print(L" Fingerprint (SHA1):\n ");
337 for (i = 0; i < SHA1_DIGEST_SIZE; i++) {
338 Print(L" %02x", hash[i]);
339 if (i % 10 == 9)
340 Print(L"\n ");
341 }
342 Print(L"\n");
343 }
344
345 static INTN get_number ()
346 {
347 EFI_INPUT_KEY input_key;
348 CHAR16 input[10];
349 int count = 0;
350
351 do {
352 input_key = get_keystroke();
353
354 if ((input_key.UnicodeChar < '0' ||
355 input_key.UnicodeChar > '9' ||
356 count >= 10) &&
357 input_key.UnicodeChar != CHAR_BACKSPACE) {
358 continue;
359 }
360
361 if (count == 0 && input_key.UnicodeChar == CHAR_BACKSPACE)
362 continue;
363
364 Print(L"%c", input_key.UnicodeChar);
365
366 if (input_key.UnicodeChar == CHAR_BACKSPACE) {
367 input[--count] = '\0';
368 continue;
369 }
370
371 input[count++] = input_key.UnicodeChar;
372 } while (input_key.UnicodeChar != CHAR_CARRIAGE_RETURN);
373
374 if (count == 0)
375 return -1;
376
377 input[count] = '\0';
378
379 return (INTN)Atoi(input);
380 }
381
382 static UINT8 list_keys (void *MokNew, UINTN MokNewSize)
383 {
384 UINT32 MokNum = 0;
385 MokListNode *keys = NULL;
386 INTN key_num = 0;
387 UINT8 initial = 1;
388 EFI_SIGNATURE_LIST *CertList = MokNew;
389 EFI_GUID CertType = EfiCertX509Guid;
390 EFI_GUID HashType = EfiHashSha256Guid;
391 UINTN dbsize = MokNewSize;
392
393 if (MokNewSize < (sizeof(EFI_SIGNATURE_LIST) +
394 sizeof(EFI_SIGNATURE_DATA))) {
395 Print(L"No keys\n");
396 Pause();
397 return 0;
398 }
399
400 while ((dbsize > 0) && (dbsize >= CertList->SignatureListSize)) {
401 if ((CompareGuid (&CertList->SignatureType, &CertType) != 0) &&
402 (CompareGuid (&CertList->SignatureType, &HashType) != 0)) {
403 Print(L"Doesn't look like a key or hash\n");
404 dbsize -= CertList->SignatureListSize;
405 CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList +
406 CertList->SignatureSize);
407 continue;
408 }
409
410 if ((CompareGuid (&CertList->SignatureType, &CertType) != 0) &&
411 (CertList->SignatureSize != 48)) {
412 Print(L"Doesn't look like a valid hash\n");
413 dbsize -= CertList->SignatureListSize;
414 CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList +
415 CertList->SignatureSize);
416 continue;
417 }
418
419 MokNum++;
420 dbsize -= CertList->SignatureListSize;
421 CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList +
422 CertList->SignatureSize);
423 }
424
425 keys = build_mok_list(MokNum, MokNew, MokNewSize);
426
427 if (!keys) {
428 Print(L"Failed to construct key list in MokNew\n");
429 return 0;
430 }
431
432 do {
433 uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
434 Print(L"Input the key number to show the details of the key or\n"
435 L"type \'0\' to continue\n\n");
436 Print(L"%d key(s) in the new key list\n\n", MokNum);
437
438 if (key_num > MokNum) {
439 Print(L"[Key %d]\n", key_num);
440 Print(L"No such key\n\n");
441 } else if (initial != 1 && key_num > 0){
442 Print(L"[Key %d]\n", key_num);
443 show_mok_info(keys[key_num-1].Mok, keys[key_num-1].MokSize);
444 }
445
446 Print(L"Key Number: ");
447
448 key_num = get_number();
449
450 Print(L"\n\n");
451
452 if (key_num == -1)
453 continue;
454
455 initial = 0;
456 } while (key_num != 0);
457
458 FreePool(keys);
459
460 return 1;
461 }
462
463 static UINT8 get_line (UINT32 *length, CHAR16 *line, UINT32 line_max, UINT8 show)
464 {
465 EFI_INPUT_KEY key;
466 int count = 0;
467
468 do {
469 key = get_keystroke();
470
471 if ((count >= line_max &&
472 key.UnicodeChar != CHAR_BACKSPACE) ||
473 key.UnicodeChar == CHAR_NULL ||
474 key.UnicodeChar == CHAR_TAB ||
475 key.UnicodeChar == CHAR_LINEFEED ||
476 key.UnicodeChar == CHAR_CARRIAGE_RETURN) {
477 continue;
478 }
479
480 if (count == 0 && key.UnicodeChar == CHAR_BACKSPACE) {
481 continue;
482 } else if (key.UnicodeChar == CHAR_BACKSPACE) {
483 if (show) {
484 Print(L"\b");
485 }
486 line[--count] = '\0';
487 continue;
488 }
489
490 if (show) {
491 Print(L"%c", key.UnicodeChar);
492 }
493
494 line[count++] = key.UnicodeChar;
495 } while (key.UnicodeChar != CHAR_CARRIAGE_RETURN);
496 Print(L"\n");
497
498 *length = count;
499
500 return 1;
501 }
502
503 static EFI_STATUS compute_pw_hash (void *MokNew, UINTN MokNewSize, CHAR16 *password,
504 UINT32 pw_length, UINT8 *hash)
505 {
506 EFI_STATUS status;
507 unsigned int ctxsize;
508 void *ctx = NULL;
509
510 ctxsize = Sha256GetContextSize();
511 ctx = AllocatePool(ctxsize);
512
513 if (!ctx) {
514 Print(L"Unable to allocate memory for hash context\n");
515 return EFI_OUT_OF_RESOURCES;
516 }
517
518 if (!Sha256Init(ctx)) {
519 Print(L"Unable to initialise hash\n");
520 status = EFI_OUT_OF_RESOURCES;
521 goto done;
522 }
523
524 if (MokNew && MokNewSize) {
525 if (!(Sha256Update(ctx, MokNew, MokNewSize))) {
526 Print(L"Unable to generate hash\n");
527 status = EFI_OUT_OF_RESOURCES;
528 goto done;
529 }
530 }
531
532 if (!(Sha256Update(ctx, password, pw_length * sizeof(CHAR16)))) {
533 Print(L"Unable to generate hash\n");
534 status = EFI_OUT_OF_RESOURCES;
535 goto done;
536 }
537
538 if (!(Sha256Final(ctx, hash))) {
539 Print(L"Unable to finalise hash\n");
540 status = EFI_OUT_OF_RESOURCES;
541 goto done;
542 }
543
544 status = EFI_SUCCESS;
545 done:
546 return status;
547 }
548
549 static EFI_STATUS store_keys (void *MokNew, UINTN MokNewSize, int authenticate)
550 {
551 EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
552 EFI_STATUS efi_status;
553 UINT8 hash[SHA256_DIGEST_SIZE];
554 UINT8 auth[SHA256_DIGEST_SIZE];
555 UINTN auth_size;
556 UINT32 attributes;
557 CHAR16 password[PASSWORD_MAX];
558 UINT32 pw_length;
559 UINT8 fail_count = 0;
560
561 if (authenticate) {
562 auth_size = SHA256_DIGEST_SIZE;
563 efi_status = uefi_call_wrapper(RT->GetVariable, 5, L"MokAuth",
564 &shim_lock_guid,
565 &attributes, &auth_size, auth);
566
567
568 if (efi_status != EFI_SUCCESS || auth_size != SHA256_DIGEST_SIZE) {
569 Print(L"Failed to get MokAuth %d\n", efi_status);
570 return efi_status;
571 }
572
573 while (fail_count < 3) {
574 Print(L"Password(%d-%d characters): ",
575 PASSWORD_MIN, PASSWORD_MAX);
576 get_line(&pw_length, password, PASSWORD_MAX, 0);
577
578 if (pw_length < 8) {
579 Print(L"At least %d characters for the password\n",
580 PASSWORD_MIN);
581 }
582
583 efi_status = compute_pw_hash(MokNew, MokNewSize, password,
584 pw_length, hash);
585
586 if (efi_status != EFI_SUCCESS) {
587 return efi_status;
588 }
589
590 if (CompareMem(auth, hash, SHA256_DIGEST_SIZE) != 0) {
591 Print(L"Password doesn't match\n");
592 fail_count++;
593 } else {
594 break;
595 }
596 }
597
598 if (fail_count >= 3)
599 return EFI_ACCESS_DENIED;
600 }
601
602 if (!MokNewSize) {
603 /* Delete MOK */
604 efi_status = uefi_call_wrapper(RT->SetVariable, 5, L"MokList",
605 &shim_lock_guid,
606 EFI_VARIABLE_NON_VOLATILE
607 | EFI_VARIABLE_BOOTSERVICE_ACCESS
608 | EFI_VARIABLE_APPEND_WRITE,
609 0, NULL);
610 } else {
611 /* Write new MOK */
612 efi_status = uefi_call_wrapper(RT->SetVariable, 5, L"MokList",
613 &shim_lock_guid,
614 EFI_VARIABLE_NON_VOLATILE
615 | EFI_VARIABLE_BOOTSERVICE_ACCESS
616 | EFI_VARIABLE_APPEND_WRITE,
617 MokNewSize, MokNew);
618 }
619
620 if (efi_status != EFI_SUCCESS) {
621 Print(L"Failed to set variable %d\n", efi_status);
622 return efi_status;
623 }
624
625 return EFI_SUCCESS;
626 }
627
628 static UINTN mok_enrollment_prompt (void *MokNew, UINTN MokNewSize, int auth) {
629 CHAR16 line[1];
630 UINT32 length;
631 EFI_STATUS efi_status;
632
633 do {
634 if (!list_keys(MokNew, MokNewSize)) {
635 return 0;
636 }
637
638 Print(L"Enroll the key(s)? (y/n): ");
639
640 get_line (&length, line, 1, 1);
641
642 if (line[0] == 'Y' || line[0] == 'y') {
643 efi_status = store_keys(MokNew, MokNewSize, auth);
644
645 if (efi_status != EFI_SUCCESS) {
646 Print(L"Failed to enroll keys\n");
647 return -1;
648 }
649 return 0;
650 }
651 } while (line[0] != 'N' && line[0] != 'n');
652 return -1;
653 }
654
655 static INTN mok_enrollment_prompt_callback (void *MokNew, void *data2,
656 void *data3) {
657 return mok_enrollment_prompt(MokNew, (UINTN)data2, TRUE);
658 }
659
660 static INTN mok_deletion_prompt (void *MokNew, void *data2, void *data3) {
661 CHAR16 line[1];
662 UINT32 length;
663 EFI_STATUS efi_status;
664
665 Print(L"Erase all stored keys? (y/N): ");
666
667 get_line (&length, line, 1, 1);
668
669 if (line[0] == 'Y' || line[0] == 'y') {
670 efi_status = store_keys(NULL, 0, TRUE);
671
672 if (efi_status != EFI_SUCCESS) {
673 Print(L"Failed to erase keys\n");
674 return -1;
675 }
676 }
677
678 return 0;
679 }
680
681 static UINTN draw_menu (struct menu_item *items, UINTN count) {
682 UINTN i;
683
684 uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
685
686 uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut,
687 EFI_WHITE | EFI_BACKGROUND_BLACK);
688
689 Print(L"%s UEFI key management\n\n", SHIM_VENDOR);
690
691 for (i = 0; i < count; i++) {
692 uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut,
693 items[i].colour | EFI_BACKGROUND_BLACK);
694 Print(L" %s\n", items[i].text);
695 }
696
697 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, 0);
698 uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, TRUE);
699
700 return 2;
701 }
702
703 static void free_menu (struct menu_item *items, UINTN count) {
704 UINTN i;
705
706 for (i=0; i<count; i++) {
707 if (items[i].text)
708 FreePool(items[i].text);
709 }
710
711 FreePool(items);
712 }
713
714 static void run_menu (struct menu_item *items, UINTN count, UINTN timeout) {
715 UINTN index, pos = 0, wait = 0, offset;
716 EFI_INPUT_KEY key;
717 EFI_STATUS status;
718
719 if (timeout)
720 wait = 10000000;
721
722 while (1) {
723 uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
724
725 offset = draw_menu (items, count);
726
727 uefi_call_wrapper(ST->ConOut->SetAttribute, 2,
728 ST->ConOut,
729 EFI_WHITE | EFI_BACKGROUND_BLACK);
730
731 if (timeout) {
732 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3,
733 ST->ConOut, 0, count + 1 + offset);
734 if (timeout > 1)
735 Print(L"Booting in %d seconds\n", timeout);
736 else
737 Print(L"Booting in %d second\n", timeout);
738 }
739
740 uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut,
741 0, pos + offset);
742 status = WaitForSingleEvent(ST->ConIn->WaitForKey, wait);
743
744 if (status == EFI_TIMEOUT) {
745 timeout--;
746 if (!timeout) {
747 free_menu(items, count);
748 return;
749 }
750 continue;
751 }
752
753 wait = 0;
754 timeout = 0;
755
756 uefi_call_wrapper(BS->WaitForEvent, 3, 1,
757 &ST->ConIn->WaitForKey, &index);
758 uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, ST->ConIn,
759 &key);
760
761 switch(key.ScanCode) {
762 case SCAN_UP:
763 if (pos == 0)
764 continue;
765 pos--;
766 continue;
767 break;
768 case SCAN_DOWN:
769 if (pos == (count - 1))
770 continue;
771 pos++;
772 continue;
773 break;
774 }
775
776 switch(key.UnicodeChar) {
777 case CHAR_LINEFEED:
778 case CHAR_CARRIAGE_RETURN:
779 if (items[pos].callback == NULL) {
780 free_menu(items, count);
781 return;
782 }
783
784 items[pos].callback(items[pos].data, items[pos].data2,
785 items[pos].data3);
786 draw_menu (items, count);
787 pos = 0;
788 break;
789 }
790 }
791 }
792
793 static UINTN verify_certificate(void *cert, UINTN size)
794 {
795 X509 *X509Cert;
796 if (!cert || size == 0)
797 return FALSE;
798
799 if (!(X509ConstructCertificate(cert, size, (UINT8 **) &X509Cert)) ||
800 X509Cert == NULL) {
801 Print(L"Invalid X509 certificate\n");
802 Pause();
803 return FALSE;
804 }
805
806 X509_free(X509Cert);
807 return TRUE;
808 }
809
810 static INTN file_callback (void *data, void *data2, void *data3) {
811 EFI_FILE_INFO *buffer = NULL;
812 UINTN buffersize = 0, mokbuffersize;
813 EFI_STATUS status;
814 EFI_FILE *file;
815 CHAR16 *filename = data;
816 EFI_FILE *parent = data2;
817 BOOLEAN hash = !!data3;
818 EFI_GUID file_info_guid = EFI_FILE_INFO_ID;
819 EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
820 EFI_SIGNATURE_LIST *CertList;
821 EFI_SIGNATURE_DATA *CertData;
822 void *mokbuffer = NULL;
823
824 status = uefi_call_wrapper(parent->Open, 5, parent, &file, filename,
825 EFI_FILE_MODE_READ, 0);
826
827 if (status != EFI_SUCCESS)
828 return 1;
829
830 status = uefi_call_wrapper(file->GetInfo, 4, file, &file_info_guid,
831 &buffersize, buffer);
832
833 if (status == EFI_BUFFER_TOO_SMALL) {
834 buffer = AllocatePool(buffersize);
835 status = uefi_call_wrapper(file->GetInfo, 4, file,
836 &file_info_guid, &buffersize,
837 buffer);
838 }
839
840 if (!buffer)
841 return 0;
842
843 buffersize = buffer->FileSize;
844
845 if (hash) {
846 void *binary;
847 UINT8 sha256[SHA256_DIGEST_SIZE];
848 UINT8 sha1[SHA1_DIGEST_SIZE];
849 SHIM_LOCK *shim_lock;
850 EFI_GUID shim_guid = SHIM_LOCK_GUID;
851 PE_COFF_LOADER_IMAGE_CONTEXT context;
852
853 status = LibLocateProtocol(&shim_guid, (VOID **)&shim_lock);
854
855 if (status != EFI_SUCCESS)
856 goto out;
857
858 mokbuffersize = sizeof(EFI_SIGNATURE_LIST) + sizeof(EFI_GUID) +
859 SHA256_DIGEST_SIZE;
860
861 mokbuffer = AllocatePool(mokbuffersize);
862
863 if (!mokbuffer)
864 goto out;
865
866 binary = AllocatePool(buffersize);
867
868 status = uefi_call_wrapper(file->Read, 3, file, &buffersize,
869 binary);
870
871 if (status != EFI_SUCCESS)
872 goto out;
873
874 status = shim_lock->Context(binary, buffersize, &context);
875
876 if (status != EFI_SUCCESS)
877 goto out;
878
879 status = shim_lock->Hash(binary, buffersize, &context, sha256,
880 sha1);
881
882 if (status != EFI_SUCCESS)
883 goto out;
884
885 CertList = mokbuffer;
886 CertList->SignatureType = EfiHashSha256Guid;
887 CertList->SignatureSize = 16 + SHA256_DIGEST_SIZE;
888 CertData = (EFI_SIGNATURE_DATA *)(((UINT8 *)mokbuffer) +
889 sizeof(EFI_SIGNATURE_LIST));
890 CopyMem(CertData->SignatureData, sha256, SHA256_DIGEST_SIZE);
891 } else {
892 mokbuffersize = buffersize + sizeof(EFI_SIGNATURE_LIST) +
893 sizeof(EFI_GUID);
894 mokbuffer = AllocatePool(mokbuffersize);
895
896 if (!mokbuffer)
897 goto out;
898
899 CertList = mokbuffer;
900 CertList->SignatureType = EfiCertX509Guid;
901 CertList->SignatureSize = 16 + buffersize;
902 status = uefi_call_wrapper(file->Read, 3, file, &buffersize,
903 mokbuffer + sizeof(EFI_SIGNATURE_LIST) + 16);
904
905 if (status != EFI_SUCCESS)
906 goto out;
907 CertData = (EFI_SIGNATURE_DATA *)(((UINT8 *)mokbuffer) +
908 sizeof(EFI_SIGNATURE_LIST));
909 }
910
911 CertList->SignatureListSize = mokbuffersize;
912 CertList->SignatureHeaderSize = 0;
913 CertData->SignatureOwner = shim_lock_guid;
914
915 if (!hash) {
916 if (!verify_certificate(CertData->SignatureData, buffersize))
917 goto out;
918 }
919
920 mok_enrollment_prompt(mokbuffer, mokbuffersize, FALSE);
921 out:
922 if (buffer)
923 FreePool(buffer);
924
925 if (mokbuffer)
926 FreePool(mokbuffer);
927
928 return 0;
929 }
930
931 static INTN directory_callback (void *data, void *data2, void *data3) {
932 EFI_FILE_INFO *buffer = NULL;
933 UINTN buffersize = 0;
934 EFI_STATUS status;
935 UINTN dircount = 0, i = 0;
936 struct menu_item *dircontent;
937 EFI_FILE *dir;
938 CHAR16 *filename = data;
939 EFI_FILE *root = data2;
940
941 status = uefi_call_wrapper(root->Open, 5, root, &dir, filename,
942 EFI_FILE_MODE_READ, 0);
943
944 if (status != EFI_SUCCESS)
945 return 1;
946
947 while (1) {
948 status = uefi_call_wrapper(dir->Read, 3, dir, &buffersize,
949 buffer);
950
951 if (status == EFI_BUFFER_TOO_SMALL) {
952 buffer = AllocatePool(buffersize);
953 status = uefi_call_wrapper(dir->Read, 3, dir,
954 &buffersize, buffer);
955 }
956
957 if (status != EFI_SUCCESS)
958 return 1;
959
960 if (!buffersize)
961 break;
962
963 if ((StrCmp(buffer->FileName, L".") == 0) ||
964 (StrCmp(buffer->FileName, L"..") == 0))
965 continue;
966
967 dircount++;
968
969 FreePool(buffer);
970 buffersize = 0;
971 }
972
973 dircount++;
974
975 dircontent = AllocatePool(sizeof(struct menu_item) * dircount);
976
977 dircontent[0].text = StrDuplicate(L"..");
978 dircontent[0].callback = NULL;
979 dircontent[0].colour = EFI_YELLOW;
980 i++;
981
982 uefi_call_wrapper(dir->SetPosition, 2, dir, 0);
983
984 while (1) {
985 status = uefi_call_wrapper(dir->Read, 3, dir, &buffersize,
986 buffer);
987
988 if (status == EFI_BUFFER_TOO_SMALL) {
989 buffer = AllocatePool(buffersize);
990 status = uefi_call_wrapper(dir->Read, 3, dir,
991 &buffersize, buffer);
992 }
993
994 if (status != EFI_SUCCESS)
995 return 1;
996
997 if (!buffersize)
998 break;
999
1000 if ((StrCmp(buffer->FileName, L".") == 0) ||
1001 (StrCmp(buffer->FileName, L"..") == 0))
1002 continue;
1003
1004 if (buffer->Attribute & EFI_FILE_DIRECTORY) {
1005 dircontent[i].text = StrDuplicate(buffer->FileName);
1006 dircontent[i].callback = directory_callback;
1007 dircontent[i].data = dircontent[i].text;
1008 dircontent[i].data2 = dir;
1009 dircontent[i].data3 = data3;
1010 dircontent[i].colour = EFI_YELLOW;
1011 } else {
1012 dircontent[i].text = StrDuplicate(buffer->FileName);
1013 dircontent[i].callback = file_callback;
1014 dircontent[i].data = dircontent[i].text;
1015 dircontent[i].data2 = dir;
1016 dircontent[i].data3 = data3;
1017 dircontent[i].colour = EFI_WHITE;
1018 }
1019
1020 i++;
1021 FreePool(buffer);
1022 buffersize = 0;
1023 buffer = NULL;
1024 }
1025
1026 run_menu(dircontent, dircount, 0);
1027
1028 return 0;
1029 }
1030
1031 static INTN filesystem_callback (void *data, void *data2, void *data3) {
1032 EFI_FILE_INFO *buffer = NULL;
1033 UINTN buffersize = 0;
1034 EFI_STATUS status;
1035 UINTN dircount = 0, i = 0;
1036 struct menu_item *dircontent;
1037 EFI_FILE *root = data;
1038
1039 uefi_call_wrapper(root->SetPosition, 2, root, 0);
1040
1041 while (1) {
1042 status = uefi_call_wrapper(root->Read, 3, root, &buffersize,
1043 buffer);
1044
1045 if (status == EFI_BUFFER_TOO_SMALL) {
1046 buffer = AllocatePool(buffersize);
1047 status = uefi_call_wrapper(root->Read, 3, root,
1048 &buffersize, buffer);
1049 }
1050
1051 if (status != EFI_SUCCESS)
1052 return 1;
1053
1054 if (!buffersize)
1055 break;
1056
1057 if ((StrCmp(buffer->FileName, L".") == 0) ||
1058 (StrCmp(buffer->FileName, L"..") == 0))
1059 continue;
1060
1061 dircount++;
1062
1063 FreePool(buffer);
1064 buffersize = 0;
1065 }
1066
1067 dircount++;
1068
1069 dircontent = AllocatePool(sizeof(struct menu_item) * dircount);
1070
1071 dircontent[0].text = StrDuplicate(L"Return to filesystem list");
1072 dircontent[0].callback = NULL;
1073 dircontent[0].colour = EFI_YELLOW;
1074 i++;
1075
1076 uefi_call_wrapper(root->SetPosition, 2, root, 0);
1077
1078 while (1) {
1079 status = uefi_call_wrapper(root->Read, 3, root, &buffersize,
1080 buffer);
1081
1082 if (status == EFI_BUFFER_TOO_SMALL) {
1083 buffer = AllocatePool(buffersize);
1084 status = uefi_call_wrapper(root->Read, 3, root,
1085 &buffersize, buffer);
1086 }
1087
1088 if (status != EFI_SUCCESS)
1089 return 1;
1090
1091 if (!buffersize)
1092 break;
1093
1094 if ((StrCmp(buffer->FileName, L".") == 0) ||
1095 (StrCmp(buffer->FileName, L"..") == 0))
1096 continue;
1097
1098 if (buffer->Attribute & EFI_FILE_DIRECTORY) {
1099 dircontent[i].text = StrDuplicate(buffer->FileName);
1100 dircontent[i].callback = directory_callback;
1101 dircontent[i].data = dircontent[i].text;
1102 dircontent[i].data2 = root;
1103 dircontent[i].data3 = data3;
1104 dircontent[i].colour = EFI_YELLOW;
1105 } else {
1106 dircontent[i].text = StrDuplicate(buffer->FileName);
1107 dircontent[i].callback = file_callback;
1108 dircontent[i].data = dircontent[i].text;
1109 dircontent[i].data2 = root;
1110 dircontent[i].data3 = data3;
1111 dircontent[i].colour = EFI_WHITE;
1112 }
1113
1114 i++;
1115 FreePool(buffer);
1116 buffer = NULL;
1117 buffersize = 0;
1118 }
1119
1120 run_menu(dircontent, dircount, 0);
1121
1122 return 0;
1123 }
1124
1125 static INTN find_fs (void *data, void *data2, void *data3) {
1126 EFI_GUID fs_guid = SIMPLE_FILE_SYSTEM_PROTOCOL;
1127 UINTN count, i;
1128 UINTN OldSize, NewSize;
1129 EFI_HANDLE **filesystem_handles;
1130 struct menu_item *filesystems;
1131
1132 uefi_call_wrapper(BS->LocateHandleBuffer, 5, ByProtocol, &fs_guid,
1133 NULL, &count, &filesystem_handles);
1134
1135 if (!count || !filesystem_handles) {
1136 Print(L"No filesystems?\n");
1137 return 1;
1138 }
1139
1140 count++;
1141
1142 filesystems = AllocatePool(sizeof(struct menu_item) * count);
1143
1144 filesystems[0].text = StrDuplicate(L"Exit");
1145 filesystems[0].callback = NULL;
1146 filesystems[0].colour = EFI_YELLOW;
1147
1148 for (i=1; i<count; i++) {
1149 EFI_HANDLE *fs = filesystem_handles[i-1];
1150 EFI_FILE_IO_INTERFACE *fs_interface;
1151 EFI_DEVICE_PATH *path;
1152 EFI_FILE *root;
1153 EFI_STATUS status;
1154 CHAR16 *VolumeLabel = NULL;
1155 EFI_FILE_SYSTEM_INFO *buffer = NULL;
1156 UINTN buffersize = 0;
1157 EFI_GUID file_info_guid = EFI_FILE_INFO_ID;
1158
1159 status = uefi_call_wrapper(BS->HandleProtocol, 3, fs, &fs_guid,
1160 &fs_interface);
1161
1162 if (status != EFI_SUCCESS || !fs_interface)
1163 continue;
1164
1165 path = DevicePathFromHandle(fs);
1166
1167 status = uefi_call_wrapper(fs_interface->OpenVolume, 2,
1168 fs_interface, &root);
1169
1170 if (status != EFI_SUCCESS || !root)
1171 continue;
1172
1173 status = uefi_call_wrapper(root->GetInfo, 4, root,
1174 &file_info_guid, &buffersize,
1175 buffer);
1176
1177 if (status == EFI_BUFFER_TOO_SMALL) {
1178 buffer = AllocatePool(buffersize);
1179 status = uefi_call_wrapper(root->GetInfo, 4, root,
1180 &file_info_guid,
1181 &buffersize, buffer);
1182 }
1183
1184 if (status == EFI_SUCCESS)
1185 VolumeLabel = buffer->VolumeLabel;
1186
1187 if (path)
1188 filesystems[i].text = DevicePathToStr(path);
1189 else
1190 filesystems[i].text = StrDuplicate(L"Unknown device\n");
1191 if (VolumeLabel) {
1192 OldSize = (StrLen(filesystems[i].text) + 1) * sizeof(CHAR16);
1193 NewSize = OldSize + StrLen(VolumeLabel) * sizeof(CHAR16);
1194 filesystems[i].text = ReallocatePool(filesystems[i].text,
1195 OldSize, NewSize);
1196 StrCat(filesystems[i].text, VolumeLabel);
1197 }
1198
1199 if (buffersize)
1200 FreePool(buffer);
1201
1202 filesystems[i].data = root;
1203 filesystems[i].data2 = NULL;
1204 filesystems[i].data3 = data3;
1205 filesystems[i].callback = filesystem_callback;
1206 filesystems[i].colour = EFI_YELLOW;
1207 }
1208
1209 uefi_call_wrapper(BS->FreePool, 1, filesystem_handles);
1210
1211 run_menu(filesystems, count, 0);
1212
1213 return 0;
1214 }
1215
1216 static EFI_STATUS enter_mok_menu(EFI_HANDLE image_handle, void *MokNew,
1217 UINTN MokNewSize)
1218 {
1219 struct menu_item *menu_item;
1220 UINT32 MokAuth = 0;
1221 UINTN menucount = 0;
1222 EFI_STATUS efi_status;
1223 EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
1224 UINT8 auth[SHA256_DIGEST_SIZE];
1225 UINTN auth_size = SHA256_DIGEST_SIZE;
1226 UINT32 attributes;
1227
1228 efi_status = uefi_call_wrapper(RT->GetVariable, 5, L"MokAuth",
1229 &shim_lock_guid,
1230 &attributes, &auth_size, auth);
1231
1232 if ((efi_status == EFI_SUCCESS) && (auth_size == SHA256_DIGEST_SIZE))
1233 MokAuth = 1;
1234
1235 if (MokNew || MokAuth)
1236 menu_item = AllocateZeroPool(sizeof(struct menu_item) * 4);
1237 else
1238 menu_item = AllocateZeroPool(sizeof(struct menu_item) * 3);
1239
1240 if (!menu_item)
1241 return EFI_OUT_OF_RESOURCES;
1242
1243 menu_item[0].text = StrDuplicate(L"Continue boot");
1244 menu_item[0].colour = EFI_WHITE;
1245 menu_item[0].callback = NULL;
1246
1247 menucount++;
1248
1249 if (MokNew || MokAuth) {
1250 if (!MokNew) {
1251 menu_item[1].text = StrDuplicate(L"Delete MOK");
1252 menu_item[1].colour = EFI_WHITE;
1253 menu_item[1].callback = mok_deletion_prompt;
1254 } else {
1255 menu_item[1].text = StrDuplicate(L"Enroll MOK");
1256 menu_item[1].colour = EFI_WHITE;
1257 menu_item[1].data = MokNew;
1258 menu_item[1].data2 = (void *)MokNewSize;
1259 menu_item[1].callback = mok_enrollment_prompt_callback;
1260 }
1261 menucount++;
1262 }
1263
1264 menu_item[menucount].text = StrDuplicate(L"Enroll key from disk");
1265 menu_item[menucount].colour = EFI_WHITE;
1266 menu_item[menucount].callback = find_fs;
1267 menu_item[menucount].data3 = (void *)FALSE;
1268
1269 menucount++;
1270
1271 menu_item[menucount].text = StrDuplicate(L"Enroll hash from disk");
1272 menu_item[menucount].colour = EFI_WHITE;
1273 menu_item[menucount].callback = find_fs;
1274 menu_item[menucount].data3 = (void *)TRUE;
1275
1276 menucount++;
1277
1278 run_menu(menu_item, menucount, 10);
1279
1280 uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
1281
1282 return 0;
1283 }
1284
1285 static EFI_STATUS check_mok_request(EFI_HANDLE image_handle)
1286 {
1287 EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
1288 UINTN MokNewSize = 0;
1289 void *MokNew = NULL;
1290
1291 MokNew = LibGetVariableAndSize(L"MokNew", &shim_lock_guid, &MokNewSize);
1292
1293 enter_mok_menu(image_handle, MokNew, MokNewSize);
1294
1295 if (MokNew) {
1296 if (LibDeleteVariable(L"MokNew", &shim_lock_guid) != EFI_SUCCESS) {
1297 Print(L"Failed to delete MokNew\n");
1298 }
1299 FreePool (MokNew);
1300 }
1301 LibDeleteVariable(L"MokAuth", &shim_lock_guid);
1302
1303 return EFI_SUCCESS;
1304 }
1305
1306 EFI_STATUS efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *systab)
1307 {
1308 EFI_STATUS efi_status;
1309
1310 InitializeLib(image_handle, systab);
1311
1312 efi_status = check_mok_request(image_handle);
1313
1314 return efi_status;
1315 }