]> git.proxmox.com Git - efi-boot-shim.git/blob - MokManager.c
More tips for the MOK password
[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
7 #define PASSWORD_MAX 16
8 #define PASSWORD_MIN 8
9
10 typedef struct {
11 UINT32 MokSize;
12 UINT8 *Mok;
13 } MokListNode;
14
15 static EFI_STATUS get_variable (CHAR16 *name, EFI_GUID guid, UINT32 *attributes,
16 UINTN *size, void **buffer)
17 {
18 EFI_STATUS efi_status;
19 char allocate = !(*size);
20
21 efi_status = uefi_call_wrapper(RT->GetVariable, 5, name, &guid,
22 attributes, size, buffer);
23
24 if (efi_status != EFI_BUFFER_TOO_SMALL || !allocate) {
25 return efi_status;
26 }
27
28 if (allocate)
29 *buffer = AllocatePool(*size);
30
31 if (!*buffer) {
32 Print(L"Unable to allocate variable buffer\n");
33 return EFI_OUT_OF_RESOURCES;
34 }
35
36 efi_status = uefi_call_wrapper(RT->GetVariable, 5, name, &guid,
37 attributes, size, *buffer);
38
39 return efi_status;
40 }
41
42 static EFI_STATUS delete_variable (CHAR16 *name, EFI_GUID guid)
43 {
44 EFI_STATUS efi_status;
45
46 efi_status = uefi_call_wrapper(RT->SetVariable, 5, name, &guid,
47 0, 0, (UINT8 *)NULL);
48
49 return efi_status;
50 }
51
52 static EFI_INPUT_KEY get_keystroke (void)
53 {
54 EFI_INPUT_KEY key;
55 UINTN EventIndex;
56
57 uefi_call_wrapper(BS->WaitForEvent, 3, 1, &ST->ConIn->WaitForKey,
58 &EventIndex);
59 uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, ST->ConIn, &key);
60
61 return key;
62 }
63
64 static EFI_STATUS get_sha256sum (void *Data, int DataSize, UINT8 *hash)
65 {
66 EFI_STATUS status;
67 unsigned int ctxsize;
68 void *ctx = NULL;
69
70 ctxsize = Sha256GetContextSize();
71 ctx = AllocatePool(ctxsize);
72
73 if (!ctx) {
74 Print(L"Unable to allocate memory for hash context\n");
75 return EFI_OUT_OF_RESOURCES;
76 }
77
78 if (!Sha256Init(ctx)) {
79 Print(L"Unable to initialise hash\n");
80 status = EFI_OUT_OF_RESOURCES;
81 goto done;
82 }
83
84 if (!(Sha256Update(ctx, Data, DataSize))) {
85 Print(L"Unable to generate hash\n");
86 status = EFI_OUT_OF_RESOURCES;
87 goto done;
88 }
89
90 if (!(Sha256Final(ctx, hash))) {
91 Print(L"Unable to finalise hash\n");
92 status = EFI_OUT_OF_RESOURCES;
93 goto done;
94 }
95
96 status = EFI_SUCCESS;
97 done:
98 return status;
99 }
100
101 static MokListNode *build_mok_list(UINT32 num, void *Data, UINTN DataSize) {
102 MokListNode *list;
103 INT64 remain = DataSize;
104 int i;
105 void *ptr;
106
107 if (DataSize < sizeof(UINT32))
108 return NULL;
109
110 list = AllocatePool(sizeof(MokListNode) * num);
111
112 if (!list) {
113 Print(L"Unable to allocate MOK list\n");
114 return NULL;
115 }
116
117 ptr = Data;
118 for (i = 0; i < num; i++) {
119 CopyMem(&list[i].MokSize, ptr, sizeof(UINT32));
120 remain -= sizeof(UINT32) + list[i].MokSize;
121
122 if (remain < 0) {
123 Print(L"the list was corrupted\n");
124 FreePool(list);
125 return NULL;
126 }
127
128 ptr += sizeof(UINT32);
129 list[i].Mok = ptr;
130 ptr += list[i].MokSize;
131 }
132
133 return list;
134 }
135
136 static void print_x509_name (X509_NAME *X509Name, CHAR16 *name)
137 {
138 char *str;
139
140 str = X509_NAME_oneline(X509Name, NULL, 0);
141 if (str) {
142 Print(L" %s:\n %a\n", name, str);
143 OPENSSL_free(str);
144 }
145 }
146
147 static const char *mon[12]= {
148 "Jan","Feb","Mar","Apr","May","Jun",
149 "Jul","Aug","Sep","Oct","Nov","Dec"
150 };
151
152 static void print_x509_GENERALIZEDTIME_time (ASN1_TIME *time, CHAR16 *time_string)
153 {
154 char *v;
155 int gmt = 0;
156 int i;
157 int y = 0,M = 0,d = 0,h = 0,m = 0,s = 0;
158 char *f = NULL;
159 int f_len = 0;
160
161 i=time->length;
162 v=(char *)time->data;
163
164 if (i < 12)
165 goto error;
166
167 if (v[i-1] == 'Z')
168 gmt=1;
169
170 for (i=0; i<12; i++) {
171 if ((v[i] > '9') || (v[i] < '0'))
172 goto error;
173 }
174
175 y = (v[0]-'0')*1000+(v[1]-'0')*100 + (v[2]-'0')*10+(v[3]-'0');
176 M = (v[4]-'0')*10+(v[5]-'0');
177
178 if ((M > 12) || (M < 1))
179 goto error;
180
181 d = (v[6]-'0')*10+(v[7]-'0');
182 h = (v[8]-'0')*10+(v[9]-'0');
183 m = (v[10]-'0')*10+(v[11]-'0');
184
185 if (time->length >= 14 &&
186 (v[12] >= '0') && (v[12] <= '9') &&
187 (v[13] >= '0') && (v[13] <= '9')) {
188 s = (v[12]-'0')*10+(v[13]-'0');
189 /* Check for fractions of seconds. */
190 if (time->length >= 15 && v[14] == '.') {
191 int l = time->length;
192 f = &v[14]; /* The decimal point. */
193 f_len = 1;
194 while (14 + f_len < l && f[f_len] >= '0' &&
195 f[f_len] <= '9')
196 ++f_len;
197 }
198 }
199
200 SPrint(time_string, 0, L"%a %2d %02d:%02d:%02d%.*a %d%a",
201 mon[M-1], d, h, m, s, f_len, f, y, (gmt)?" GMT":"");
202 error:
203 return;
204 }
205
206 static void print_x509_UTCTIME_time (ASN1_TIME *time, CHAR16 *time_string)
207 {
208 char *v;
209 int gmt=0;
210 int i;
211 int y = 0,M = 0,d = 0,h = 0,m = 0,s = 0;
212
213 i=time->length;
214 v=(char *)time->data;
215
216 if (i < 10)
217 goto error;
218
219 if (v[i-1] == 'Z')
220 gmt=1;
221
222 for (i=0; i<10; i++)
223 if ((v[i] > '9') || (v[i] < '0'))
224 goto error;
225
226 y = (v[0]-'0')*10+(v[1]-'0');
227
228 if (y < 50)
229 y+=100;
230
231 M = (v[2]-'0')*10+(v[3]-'0');
232
233 if ((M > 12) || (M < 1))
234 goto error;
235
236 d = (v[4]-'0')*10+(v[5]-'0');
237 h = (v[6]-'0')*10+(v[7]-'0');
238 m = (v[8]-'0')*10+(v[9]-'0');
239
240 if (time->length >=12 &&
241 (v[10] >= '0') && (v[10] <= '9') &&
242 (v[11] >= '0') && (v[11] <= '9'))
243 s = (v[10]-'0')*10+(v[11]-'0');
244
245 SPrint(time_string, 0, L"%a %2d %02d:%02d:%02d %d%a",
246 mon[M-1], d, h, m, s, y+1900, (gmt)?" GMT":"");
247 error:
248 return;
249 }
250
251 static void print_x509_time (ASN1_TIME *time, CHAR16 *name)
252 {
253 CHAR16 time_string[30];
254
255 if (time->type == V_ASN1_UTCTIME) {
256 print_x509_UTCTIME_time(time, time_string);
257 } else if (time->type == V_ASN1_GENERALIZEDTIME) {
258 print_x509_GENERALIZEDTIME_time(time, time_string);
259 } else {
260 time_string[0] = '\0';
261 }
262
263 Print(L" %s:\n %s\n", name, time_string);
264 }
265
266 static void show_x509_info (X509 *X509Cert)
267 {
268 ASN1_INTEGER *serial;
269 BIGNUM *bnser;
270 unsigned char hexbuf[30];
271 X509_NAME *X509Name;
272 ASN1_TIME *time;
273
274 serial = X509_get_serialNumber(X509Cert);
275 if (serial) {
276 int i, n;
277 bnser = ASN1_INTEGER_to_BN(serial, NULL);
278 n = BN_bn2bin(bnser, hexbuf);
279 Print(L" Serial Number:\n ");
280 for (i = 0; i < n-1; i++) {
281 Print(L"%02x:", hexbuf[i]);
282 }
283 Print(L"%02x\n", hexbuf[n-1]);
284 }
285
286 X509Name = X509_get_issuer_name(X509Cert);
287 if (X509Name) {
288 print_x509_name(X509Name, L"Issuer");
289 }
290
291 X509Name = X509_get_subject_name(X509Cert);
292 if (X509Name) {
293 print_x509_name(X509Name, L"Subject");
294 }
295
296 time = X509_get_notBefore(X509Cert);
297 if (time) {
298 print_x509_time(time, L"Validity from");
299 }
300
301 time = X509_get_notAfter(X509Cert);
302 if (time) {
303 print_x509_time(time, L"Validity till");
304 }
305 }
306
307 static void show_mok_info (void *Mok, UINTN MokSize)
308 {
309 EFI_STATUS efi_status;
310 UINT8 hash[SHA256_DIGEST_SIZE];
311 unsigned int i;
312 X509 *X509Cert;
313
314 if (!Mok || MokSize == 0)
315 return;
316
317 if (X509ConstructCertificate(Mok, MokSize, (UINT8 **) &X509Cert) &&
318 X509Cert != NULL) {
319 show_x509_info(X509Cert);
320 X509_free(X509Cert);
321 } else {
322 Print(L" Not a valid X509 certificate\n\n");
323 return;
324 }
325
326 efi_status = get_sha256sum(Mok, MokSize, hash);
327
328 if (efi_status != EFI_SUCCESS) {
329 Print(L"Failed to compute MOK fingerprint\n");
330 return;
331 }
332
333 Print(L" Fingerprint (SHA256):\n ");
334 for (i = 0; i < SHA256_DIGEST_SIZE; i++) {
335 Print(L" %02x", hash[i]);
336 if (i % 16 == 15)
337 Print(L"\n ");
338 }
339 Print(L"\n");
340 }
341
342 static INTN get_number ()
343 {
344 EFI_INPUT_KEY input_key;
345 CHAR16 input[10];
346 int count = 0;
347
348 do {
349 input_key = get_keystroke();
350
351 if ((input_key.UnicodeChar < '0' ||
352 input_key.UnicodeChar > '9' ||
353 count >= 10) &&
354 input_key.UnicodeChar != CHAR_BACKSPACE) {
355 continue;
356 }
357
358 if (count == 0 && input_key.UnicodeChar == CHAR_BACKSPACE)
359 continue;
360
361 Print(L"%c", input_key.UnicodeChar);
362
363 if (input_key.UnicodeChar == CHAR_BACKSPACE) {
364 input[--count] = '\0';
365 continue;
366 }
367
368 input[count++] = input_key.UnicodeChar;
369 } while (input_key.UnicodeChar != CHAR_CARRIAGE_RETURN);
370
371 if (count == 0)
372 return -1;
373
374 input[count] = '\0';
375
376 return (INTN)Atoi(input);
377 }
378
379 static UINT8 list_keys (void *MokNew, UINTN MokNewSize)
380 {
381 UINT32 MokNum;
382 MokListNode *keys = NULL;
383 INTN key_num = 0;
384 UINT8 initial = 1;
385
386 CopyMem(&MokNum, MokNew, sizeof(UINT32));
387 if (MokNum == 0) {
388 Print(L"No key exists\n");
389 return 0;
390 }
391
392 keys = build_mok_list(MokNum,
393 (void *)MokNew + sizeof(UINT32),
394 MokNewSize - sizeof(UINT32));
395
396 if (!keys) {
397 Print(L"Failed to construct key list in MokNew\n");
398 return 0;
399 }
400
401 do {
402 uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
403 Print(L"Input the key number to show the details of the key or\n"
404 L"type \'0\' to continue\n\n");
405 Print(L"%d key(s) in the new key list\n\n", MokNum);
406
407 if (key_num > MokNum) {
408 Print(L"No such key\n\n");
409 } else if (initial != 1){
410 Print(L"[Key %d]\n", key_num);
411 show_mok_info(keys[key_num-1].Mok, keys[key_num-1].MokSize);
412 }
413
414 Print(L"Key Number: ");
415
416 key_num = get_number();
417
418 Print(L"\n\n");
419
420 if (key_num == -1)
421 continue;
422
423 initial = 0;
424 } while (key_num != 0);
425
426 FreePool(keys);
427
428 return 1;
429 }
430
431 static UINT8 mok_enrollment_prompt (void *MokNew, UINTN MokNewSize)
432 {
433 EFI_INPUT_KEY key;
434
435 do {
436 if (!list_keys(MokNew, MokNewSize)) {
437 return 0;
438 }
439
440 Print(L"Enroll the key(s) or list the key(s) again? (y/n/l): ");
441
442 key = get_keystroke();
443 Print(L"%c\n", key.UnicodeChar);
444
445 if (key.UnicodeChar == 'Y' || key.UnicodeChar == 'y') {
446 return 1;
447 }
448 } while (key.UnicodeChar == 'L' || key.UnicodeChar == 'l');
449
450 Print(L"Abort\n");
451
452 return 0;
453 }
454
455 static UINT8 mok_deletion_prompt () {
456 EFI_INPUT_KEY key;
457
458 Print(L"Erase all stored keys? (y/N): ");
459
460 key = get_keystroke();
461 Print(L"%c\n", key.UnicodeChar);
462
463 if (key.UnicodeChar == 'Y' || key.UnicodeChar == 'y') {
464 return 1;
465 }
466
467 Print(L"Abort\n");
468
469 return 0;
470 }
471
472 static UINT8 get_password (UINT32 *length, CHAR16 *password)
473 {
474 EFI_INPUT_KEY key;
475 CHAR16 input[PASSWORD_MAX];
476 int count = 0;
477
478 do {
479 key = get_keystroke();
480
481 if ((count >= PASSWORD_MAX &&
482 key.UnicodeChar != CHAR_BACKSPACE) ||
483 key.UnicodeChar == CHAR_NULL ||
484 key.UnicodeChar == CHAR_TAB ||
485 key.UnicodeChar == CHAR_LINEFEED ||
486 key.UnicodeChar == CHAR_CARRIAGE_RETURN) {
487 continue;
488 }
489
490 if (count == 0 && key.UnicodeChar == CHAR_BACKSPACE) {
491 continue;
492 } else if (key.UnicodeChar == CHAR_BACKSPACE) {
493 Print(L"%c", CHAR_BACKSPACE);
494 input[--count] = '\0';
495 continue;
496 }
497
498 input[count++] = key.UnicodeChar;
499 } while (key.UnicodeChar != CHAR_CARRIAGE_RETURN);
500 Print(L"\n");
501
502 *length = count;
503 CopyMem(password, input, count * sizeof(CHAR16));
504
505 return 1;
506 }
507
508 static EFI_STATUS compute_pw_hash (void *MokNew, UINTN MokNewSize, CHAR16 *password,
509 UINT32 pw_length, UINT8 *hash)
510 {
511 EFI_STATUS status;
512 unsigned int ctxsize;
513 void *ctx = NULL;
514
515 ctxsize = Sha256GetContextSize();
516 ctx = AllocatePool(ctxsize);
517
518 if (!ctx) {
519 Print(L"Unable to allocate memory for hash context\n");
520 return EFI_OUT_OF_RESOURCES;
521 }
522
523 if (!Sha256Init(ctx)) {
524 Print(L"Unable to initialise hash\n");
525 status = EFI_OUT_OF_RESOURCES;
526 goto done;
527 }
528
529 if (!(Sha256Update(ctx, MokNew, MokNewSize))) {
530 Print(L"Unable to generate hash\n");
531 status = EFI_OUT_OF_RESOURCES;
532 goto done;
533 }
534
535 if (!(Sha256Update(ctx, password, pw_length * sizeof(CHAR16)))) {
536 Print(L"Unable to generate hash\n");
537 status = EFI_OUT_OF_RESOURCES;
538 goto done;
539 }
540
541 if (!(Sha256Final(ctx, hash))) {
542 Print(L"Unable to finalise hash\n");
543 status = EFI_OUT_OF_RESOURCES;
544 goto done;
545 }
546
547 status = EFI_SUCCESS;
548 done:
549 return status;
550 }
551
552 static UINT8 compare_hash (UINT8 *hash1, UINT8 *hash2, UINT32 size)
553 {
554 int i;
555
556 for (i = 0; i < size; i++) {
557 if (hash1[i] != hash2[i]) {
558 return 0;
559 }
560 }
561
562 return 1;
563 }
564
565 static EFI_STATUS store_keys (void *MokNew, UINTN MokNewSize)
566 {
567 EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
568 EFI_STATUS efi_status;
569 UINT8 hash[SHA256_DIGEST_SIZE];
570 UINT8 auth[SHA256_DIGEST_SIZE];
571 UINTN auth_size;
572 UINT32 attributes;
573 CHAR16 password[PASSWORD_MAX];
574 UINT32 pw_length;
575 UINT8 fail_count = 0;
576
577 auth_size = SHA256_DIGEST_SIZE;
578 efi_status = uefi_call_wrapper(RT->GetVariable, 5, L"MokAuth",
579 &shim_lock_guid,
580 &attributes, &auth_size, auth);
581
582
583 if (efi_status != EFI_SUCCESS || auth_size != SHA256_DIGEST_SIZE) {
584 Print(L"Failed to get MokAuth %d\n", efi_status);
585 return efi_status;
586 }
587
588 while (fail_count < 3) {
589 Print(L"Password(%d-%d characters): ",
590 PASSWORD_MIN, PASSWORD_MAX);
591 get_password(&pw_length, password);
592
593 if (pw_length < 8) {
594 Print(L"At least %d characters for the password\n",
595 PASSWORD_MIN);
596 }
597
598 efi_status = compute_pw_hash(MokNew, MokNewSize, password,
599 pw_length, hash);
600
601 if (efi_status != EFI_SUCCESS) {
602 return efi_status;
603 }
604
605 if (!compare_hash(auth, hash, SHA256_DIGEST_SIZE)) {
606 Print(L"Password doesn't match\n");
607 fail_count++;
608 } else {
609 break;
610 }
611 }
612
613 if (fail_count >= 3)
614 return EFI_ACCESS_DENIED;
615
616 /* Write new MOK */
617 efi_status = uefi_call_wrapper(RT->SetVariable, 5, L"MokList",
618 &shim_lock_guid,
619 EFI_VARIABLE_NON_VOLATILE
620 | EFI_VARIABLE_BOOTSERVICE_ACCESS,
621 MokNewSize, MokNew);
622 if (efi_status != EFI_SUCCESS) {
623 Print(L"Failed to set variable %d\n", efi_status);
624 return efi_status;
625 }
626
627 return EFI_SUCCESS;
628 }
629
630 static EFI_STATUS check_mok_request(EFI_HANDLE image_handle)
631 {
632 EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
633 EFI_STATUS efi_status;
634 UINTN MokNewSize = 0;
635 void *MokNew = NULL;
636 UINT32 attributes, MokNum;
637 UINT8 confirmed;
638
639 efi_status = get_variable(L"MokNew", shim_lock_guid, &attributes,
640 &MokNewSize, &MokNew);
641
642 if (efi_status != EFI_SUCCESS || MokNewSize < sizeof(UINT32)) {
643 goto error;
644 }
645
646 CopyMem(&MokNum, MokNew, sizeof(UINT32));
647 if (MokNum == 0) {
648 confirmed = mok_deletion_prompt();
649
650 if (!confirmed)
651 goto error;
652
653 efi_status = store_keys(MokNew, sizeof(UINT32));
654
655 if (efi_status != EFI_SUCCESS) {
656 Print(L"Failed to erase keys\n");
657 goto error;
658 }
659 } else {
660 confirmed = mok_enrollment_prompt(MokNew, MokNewSize);
661
662 if (!confirmed)
663 goto error;
664
665 efi_status = store_keys(MokNew, MokNewSize);
666
667 if (efi_status != EFI_SUCCESS) {
668 Print(L"Failed to enroll MOK\n");
669 goto error;
670 }
671 }
672 error:
673 if (MokNew) {
674 if (delete_variable(L"MokNew", shim_lock_guid) != EFI_SUCCESS) {
675 Print(L"Failed to delete MokNew\n");
676 }
677 FreePool (MokNew);
678 }
679 delete_variable(L"MokAuth", shim_lock_guid);
680
681 return EFI_SUCCESS;
682 }
683
684 EFI_STATUS efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *systab)
685 {
686 EFI_STATUS efi_status;
687
688 InitializeLib(image_handle, systab);
689
690 efi_status = check_mok_request(image_handle);
691
692 return efi_status;
693 }