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