3 #include <Library/BaseCryptLib.h>
4 #include <openssl/x509.h>
9 #define PASSWORD_MAX 16
10 #define PASSWORD_MIN 8
11 #define SB_PASSWORD_LEN 8
14 #define SHIM_VENDOR L"Shim"
17 #define EFI_VARIABLE_APPEND_WRITE 0x00000040
19 #define CERT_STRING L"Select an X509 certificate to enroll:\n\n"
20 #define HASH_STRING L"Select a file to trust:\n\n"
24 INTN (* callback
)(void *data
, void *data2
, void *data3
);
34 } __attribute__ ((packed
)) MokListNode
;
39 CHAR16 Password
[PASSWORD_MAX
];
40 } __attribute__ ((packed
)) MokSBvar
;
42 static EFI_INPUT_KEY
get_keystroke (void)
47 uefi_call_wrapper(BS
->WaitForEvent
, 3, 1, &ST
->ConIn
->WaitForKey
,
49 uefi_call_wrapper(ST
->ConIn
->ReadKeyStroke
, 2, ST
->ConIn
, &key
);
54 static EFI_STATUS
get_sha1sum (void *Data
, int DataSize
, UINT8
*hash
)
60 ctxsize
= Sha1GetContextSize();
61 ctx
= AllocatePool(ctxsize
);
64 Print(L
"Unable to allocate memory for hash context\n");
65 return EFI_OUT_OF_RESOURCES
;
69 Print(L
"Unable to initialise hash\n");
70 status
= EFI_OUT_OF_RESOURCES
;
74 if (!(Sha1Update(ctx
, Data
, DataSize
))) {
75 Print(L
"Unable to generate hash\n");
76 status
= EFI_OUT_OF_RESOURCES
;
80 if (!(Sha1Final(ctx
, hash
))) {
81 Print(L
"Unable to finalise hash\n");
82 status
= EFI_OUT_OF_RESOURCES
;
91 static MokListNode
*build_mok_list(UINT32 num
, void *Data
, UINTN DataSize
) {
93 EFI_SIGNATURE_LIST
*CertList
= Data
;
94 EFI_SIGNATURE_DATA
*Cert
;
95 EFI_GUID CertType
= EfiCertX509Guid
;
96 EFI_GUID HashType
= EfiHashSha256Guid
;
97 UINTN dbsize
= DataSize
;
100 list
= AllocatePool(sizeof(MokListNode
) * num
);
103 Print(L
"Unable to allocate MOK list\n");
107 while ((dbsize
> 0) && (dbsize
>= CertList
->SignatureListSize
)) {
108 if ((CompareGuid (&CertList
->SignatureType
, &CertType
) != 0) &&
109 (CompareGuid (&CertList
->SignatureType
, &HashType
) != 0)) {
110 dbsize
-= CertList
->SignatureListSize
;
111 CertList
= (EFI_SIGNATURE_LIST
*)((UINT8
*) CertList
+
112 CertList
->SignatureListSize
);
116 if ((CompareGuid (&CertList
->SignatureType
, &HashType
) == 0) &&
117 (CertList
->SignatureSize
!= 48)) {
118 dbsize
-= CertList
->SignatureListSize
;
119 CertList
= (EFI_SIGNATURE_LIST
*)((UINT8
*) CertList
+
120 CertList
->SignatureListSize
);
124 Cert
= (EFI_SIGNATURE_DATA
*) (((UINT8
*) CertList
) +
125 sizeof (EFI_SIGNATURE_LIST
) + CertList
->SignatureHeaderSize
);
127 list
[count
].MokSize
= CertList
->SignatureSize
;
128 list
[count
].Mok
= (void *)Cert
->SignatureData
;
131 dbsize
-= CertList
->SignatureListSize
;
132 CertList
= (EFI_SIGNATURE_LIST
*) ((UINT8
*) CertList
+
133 CertList
->SignatureListSize
);
139 static void print_x509_name (X509_NAME
*X509Name
, CHAR16
*name
)
143 str
= X509_NAME_oneline(X509Name
, NULL
, 0);
145 Print(L
" %s:\n %a\n", name
, str
);
150 static const char *mon
[12]= {
151 "Jan","Feb","Mar","Apr","May","Jun",
152 "Jul","Aug","Sep","Oct","Nov","Dec"
155 static void print_x509_GENERALIZEDTIME_time (ASN1_TIME
*time
, CHAR16
*time_string
)
160 int y
= 0,M
= 0,d
= 0,h
= 0,m
= 0,s
= 0;
165 v
=(char *)time
->data
;
173 for (i
=0; i
<12; i
++) {
174 if ((v
[i
] > '9') || (v
[i
] < '0'))
178 y
= (v
[0]-'0')*1000+(v
[1]-'0')*100 + (v
[2]-'0')*10+(v
[3]-'0');
179 M
= (v
[4]-'0')*10+(v
[5]-'0');
181 if ((M
> 12) || (M
< 1))
184 d
= (v
[6]-'0')*10+(v
[7]-'0');
185 h
= (v
[8]-'0')*10+(v
[9]-'0');
186 m
= (v
[10]-'0')*10+(v
[11]-'0');
188 if (time
->length
>= 14 &&
189 (v
[12] >= '0') && (v
[12] <= '9') &&
190 (v
[13] >= '0') && (v
[13] <= '9')) {
191 s
= (v
[12]-'0')*10+(v
[13]-'0');
192 /* Check for fractions of seconds. */
193 if (time
->length
>= 15 && v
[14] == '.') {
194 int l
= time
->length
;
195 f
= &v
[14]; /* The decimal point. */
197 while (14 + f_len
< l
&& f
[f_len
] >= '0' &&
203 SPrint(time_string
, 0, L
"%a %2d %02d:%02d:%02d%.*a %d%a",
204 mon
[M
-1], d
, h
, m
, s
, f_len
, f
, y
, (gmt
)?" GMT":"");
209 static void print_x509_UTCTIME_time (ASN1_TIME
*time
, CHAR16
*time_string
)
214 int y
= 0,M
= 0,d
= 0,h
= 0,m
= 0,s
= 0;
217 v
=(char *)time
->data
;
226 if ((v
[i
] > '9') || (v
[i
] < '0'))
229 y
= (v
[0]-'0')*10+(v
[1]-'0');
234 M
= (v
[2]-'0')*10+(v
[3]-'0');
236 if ((M
> 12) || (M
< 1))
239 d
= (v
[4]-'0')*10+(v
[5]-'0');
240 h
= (v
[6]-'0')*10+(v
[7]-'0');
241 m
= (v
[8]-'0')*10+(v
[9]-'0');
243 if (time
->length
>=12 &&
244 (v
[10] >= '0') && (v
[10] <= '9') &&
245 (v
[11] >= '0') && (v
[11] <= '9'))
246 s
= (v
[10]-'0')*10+(v
[11]-'0');
248 SPrint(time_string
, 0, L
"%a %2d %02d:%02d:%02d %d%a",
249 mon
[M
-1], d
, h
, m
, s
, y
+1900, (gmt
)?" GMT":"");
254 static void print_x509_time (ASN1_TIME
*time
, CHAR16
*name
)
256 CHAR16 time_string
[30];
258 if (time
->type
== V_ASN1_UTCTIME
) {
259 print_x509_UTCTIME_time(time
, time_string
);
260 } else if (time
->type
== V_ASN1_GENERALIZEDTIME
) {
261 print_x509_GENERALIZEDTIME_time(time
, time_string
);
263 time_string
[0] = '\0';
266 Print(L
" %s:\n %s\n", name
, time_string
);
269 static void show_x509_info (X509
*X509Cert
)
271 ASN1_INTEGER
*serial
;
273 unsigned char hexbuf
[30];
277 serial
= X509_get_serialNumber(X509Cert
);
280 bnser
= ASN1_INTEGER_to_BN(serial
, NULL
);
281 n
= BN_bn2bin(bnser
, hexbuf
);
282 Print(L
" Serial Number:\n ");
283 for (i
= 0; i
< n
-1; i
++) {
284 Print(L
"%02x:", hexbuf
[i
]);
286 Print(L
"%02x\n", hexbuf
[n
-1]);
289 X509Name
= X509_get_issuer_name(X509Cert
);
291 print_x509_name(X509Name
, L
"Issuer");
294 X509Name
= X509_get_subject_name(X509Cert
);
296 print_x509_name(X509Name
, L
"Subject");
299 time
= X509_get_notBefore(X509Cert
);
301 print_x509_time(time
, L
"Validity from");
304 time
= X509_get_notAfter(X509Cert
);
306 print_x509_time(time
, L
"Validity till");
310 static void show_mok_info (void *Mok
, UINTN MokSize
)
312 EFI_STATUS efi_status
;
313 UINT8 hash
[SHA1_DIGEST_SIZE
];
317 if (!Mok
|| MokSize
== 0)
321 if (X509ConstructCertificate(Mok
, MokSize
,
322 (UINT8
**) &X509Cert
) && X509Cert
!= NULL
) {
323 show_x509_info(X509Cert
);
326 Print(L
" Not a valid X509 certificate: %x\n\n",
331 efi_status
= get_sha1sum(Mok
, MokSize
, hash
);
333 if (efi_status
!= EFI_SUCCESS
) {
334 Print(L
"Failed to compute MOK fingerprint\n");
338 Print(L
" Fingerprint (SHA1):\n ");
339 for (i
= 0; i
< SHA1_DIGEST_SIZE
; i
++) {
340 Print(L
" %02x", hash
[i
]);
345 Print(L
"SHA256 hash:\n ");
346 for (i
= 0; i
< SHA256_DIGEST_SIZE
; i
++) {
347 Print(L
" %02x", ((UINT8
*)Mok
)[i
]);
357 static INTN
get_number ()
359 EFI_INPUT_KEY input_key
;
364 input_key
= get_keystroke();
366 if ((input_key
.UnicodeChar
< '0' ||
367 input_key
.UnicodeChar
> '9' ||
369 input_key
.UnicodeChar
!= CHAR_BACKSPACE
) {
373 if (count
== 0 && input_key
.UnicodeChar
== CHAR_BACKSPACE
)
376 Print(L
"%c", input_key
.UnicodeChar
);
378 if (input_key
.UnicodeChar
== CHAR_BACKSPACE
) {
379 input
[--count
] = '\0';
383 input
[count
++] = input_key
.UnicodeChar
;
384 } while (input_key
.UnicodeChar
!= CHAR_CARRIAGE_RETURN
);
391 return (INTN
)Atoi(input
);
394 static UINT8
list_keys (void *MokNew
, UINTN MokNewSize
)
397 MokListNode
*keys
= NULL
;
400 EFI_SIGNATURE_LIST
*CertList
= MokNew
;
401 EFI_GUID CertType
= EfiCertX509Guid
;
402 EFI_GUID HashType
= EfiHashSha256Guid
;
403 UINTN dbsize
= MokNewSize
;
405 if (MokNewSize
< (sizeof(EFI_SIGNATURE_LIST
) +
406 sizeof(EFI_SIGNATURE_DATA
))) {
412 while ((dbsize
> 0) && (dbsize
>= CertList
->SignatureListSize
)) {
413 if ((CompareGuid (&CertList
->SignatureType
, &CertType
) != 0) &&
414 (CompareGuid (&CertList
->SignatureType
, &HashType
) != 0)) {
415 Print(L
"Doesn't look like a key or hash\n");
416 dbsize
-= CertList
->SignatureListSize
;
417 CertList
= (EFI_SIGNATURE_LIST
*) ((UINT8
*) CertList
+
418 CertList
->SignatureListSize
);
422 if ((CompareGuid (&CertList
->SignatureType
, &CertType
) != 0) &&
423 (CertList
->SignatureSize
!= 48)) {
424 Print(L
"Doesn't look like a valid hash\n");
425 dbsize
-= CertList
->SignatureListSize
;
426 CertList
= (EFI_SIGNATURE_LIST
*) ((UINT8
*) CertList
+
427 CertList
->SignatureListSize
);
432 dbsize
-= CertList
->SignatureListSize
;
433 CertList
= (EFI_SIGNATURE_LIST
*) ((UINT8
*) CertList
+
434 CertList
->SignatureListSize
);
437 keys
= build_mok_list(MokNum
, MokNew
, MokNewSize
);
440 Print(L
"Failed to construct key list in MokNew\n");
445 uefi_call_wrapper(ST
->ConOut
->ClearScreen
, 1, ST
->ConOut
);
446 Print(L
"Input the key number to show the details of the key or\n"
447 L
"type \'0\' to continue\n\n");
448 Print(L
"%d key(s) in the new key list\n\n", MokNum
);
450 if (key_num
> MokNum
) {
451 Print(L
"[Key %d]\n", key_num
);
452 Print(L
"No such key\n\n");
453 } else if (initial
!= 1 && key_num
> 0){
454 Print(L
"[Key %d]\n", key_num
);
455 show_mok_info(keys
[key_num
-1].Mok
, keys
[key_num
-1].MokSize
);
458 Print(L
"Key Number: ");
460 key_num
= get_number();
468 } while (key_num
!= 0);
475 static UINT8
get_line (UINT32
*length
, CHAR16
*line
, UINT32 line_max
, UINT8 show
)
481 key
= get_keystroke();
483 if ((count
>= line_max
&&
484 key
.UnicodeChar
!= CHAR_BACKSPACE
) ||
485 key
.UnicodeChar
== CHAR_NULL
||
486 key
.UnicodeChar
== CHAR_TAB
||
487 key
.UnicodeChar
== CHAR_LINEFEED
||
488 key
.UnicodeChar
== CHAR_CARRIAGE_RETURN
) {
492 if (count
== 0 && key
.UnicodeChar
== CHAR_BACKSPACE
) {
494 } else if (key
.UnicodeChar
== CHAR_BACKSPACE
) {
498 line
[--count
] = '\0';
503 Print(L
"%c", key
.UnicodeChar
);
506 line
[count
++] = key
.UnicodeChar
;
507 } while (key
.UnicodeChar
!= CHAR_CARRIAGE_RETURN
);
515 static EFI_STATUS
compute_pw_hash (void *MokNew
, UINTN MokNewSize
, CHAR16
*password
,
516 UINT32 pw_length
, UINT8
*hash
)
519 unsigned int ctxsize
;
522 ctxsize
= Sha256GetContextSize();
523 ctx
= AllocatePool(ctxsize
);
526 Print(L
"Unable to allocate memory for hash context\n");
527 return EFI_OUT_OF_RESOURCES
;
530 if (!Sha256Init(ctx
)) {
531 Print(L
"Unable to initialise hash\n");
532 status
= EFI_OUT_OF_RESOURCES
;
536 if (MokNew
&& MokNewSize
) {
537 if (!(Sha256Update(ctx
, MokNew
, MokNewSize
))) {
538 Print(L
"Unable to generate hash\n");
539 status
= EFI_OUT_OF_RESOURCES
;
544 if (!(Sha256Update(ctx
, password
, pw_length
* sizeof(CHAR16
)))) {
545 Print(L
"Unable to generate hash\n");
546 status
= EFI_OUT_OF_RESOURCES
;
550 if (!(Sha256Final(ctx
, hash
))) {
551 Print(L
"Unable to finalise hash\n");
552 status
= EFI_OUT_OF_RESOURCES
;
556 status
= EFI_SUCCESS
;
561 static EFI_STATUS
store_keys (void *MokNew
, UINTN MokNewSize
, int authenticate
)
563 EFI_GUID shim_lock_guid
= SHIM_LOCK_GUID
;
564 EFI_STATUS efi_status
;
565 UINT8 hash
[SHA256_DIGEST_SIZE
];
566 UINT8 auth
[SHA256_DIGEST_SIZE
];
569 CHAR16 password
[PASSWORD_MAX
];
571 UINT8 fail_count
= 0;
574 auth_size
= SHA256_DIGEST_SIZE
;
575 efi_status
= uefi_call_wrapper(RT
->GetVariable
, 5, L
"MokAuth",
577 &attributes
, &auth_size
, auth
);
580 if (efi_status
!= EFI_SUCCESS
|| auth_size
!= SHA256_DIGEST_SIZE
) {
581 Print(L
"Failed to get MokAuth %d\n", efi_status
);
585 while (fail_count
< 3) {
586 Print(L
"Password(%d-%d characters): ",
587 PASSWORD_MIN
, PASSWORD_MAX
);
588 get_line(&pw_length
, password
, PASSWORD_MAX
, 0);
591 Print(L
"At least %d characters for the password\n",
595 efi_status
= compute_pw_hash(MokNew
, MokNewSize
, password
,
598 if (efi_status
!= EFI_SUCCESS
) {
602 if (CompareMem(auth
, hash
, SHA256_DIGEST_SIZE
) != 0) {
603 Print(L
"Password doesn't match\n");
611 return EFI_ACCESS_DENIED
;
616 efi_status
= uefi_call_wrapper(RT
->SetVariable
, 5, L
"MokList",
618 EFI_VARIABLE_NON_VOLATILE
619 | EFI_VARIABLE_BOOTSERVICE_ACCESS
,
623 efi_status
= uefi_call_wrapper(RT
->SetVariable
, 5, L
"MokList",
625 EFI_VARIABLE_NON_VOLATILE
626 | EFI_VARIABLE_BOOTSERVICE_ACCESS
627 | EFI_VARIABLE_APPEND_WRITE
,
631 if (efi_status
!= EFI_SUCCESS
) {
632 Print(L
"Failed to set variable %d\n", efi_status
);
639 static UINTN
mok_enrollment_prompt (void *MokNew
, UINTN MokNewSize
, int auth
) {
642 EFI_STATUS efi_status
;
645 if (!list_keys(MokNew
, MokNewSize
)) {
649 Print(L
"Enroll the key(s)? (y/n): ");
651 get_line (&length
, line
, 1, 1);
653 if (line
[0] == 'Y' || line
[0] == 'y') {
654 efi_status
= store_keys(MokNew
, MokNewSize
, auth
);
656 if (efi_status
!= EFI_SUCCESS
) {
657 Print(L
"Failed to enroll keys\n");
662 } while (line
[0] != 'N' && line
[0] != 'n');
666 static INTN
mok_enrollment_prompt_callback (void *MokNew
, void *data2
,
668 return mok_enrollment_prompt(MokNew
, (UINTN
)data2
, TRUE
);
671 static INTN
mok_deletion_prompt (void *MokNew
, void *data2
, void *data3
) {
674 EFI_STATUS efi_status
;
676 Print(L
"Erase all stored keys? (y/N): ");
678 get_line (&length
, line
, 1, 1);
680 if (line
[0] == 'Y' || line
[0] == 'y') {
681 efi_status
= store_keys(NULL
, 0, TRUE
);
683 if (efi_status
!= EFI_SUCCESS
) {
684 Print(L
"Failed to erase keys\n");
692 static INTN
mok_sb_prompt (void *MokSB
, void *data2
, void *data3
) {
693 EFI_GUID shim_lock_guid
= SHIM_LOCK_GUID
;
694 EFI_STATUS efi_status
;
695 UINTN MokSBSize
= (UINTN
)data2
;
696 MokSBvar
*var
= MokSB
;
698 UINT8 correct
= 0, fail_count
= 0;
699 UINT8 hash
[SHA256_DIGEST_SIZE
];
705 LibDeleteVariable(L
"MokSB", &shim_lock_guid
);
707 if (MokSBSize
!= sizeof(MokSBvar
)) {
708 Print(L
"Invalid MokSB variable contents\n");
712 uefi_call_wrapper(ST
->ConOut
->ClearScreen
, 1, ST
->ConOut
);
714 while (correct
< 3) {
715 RandomBytes (&pos
, sizeof(pos
));
717 pos
= pos
% var
->PWLen
;
719 Print(L
"Enter password character %d: ", pos
+ 1);
720 get_line(&length
, password
, 1, 0);
722 if (password
[0] != var
->Password
[pos
]) {
723 Print(L
"Invalid character\n");
733 if (fail_count
>= 3) {
734 Print(L
"Password limit reached\n");
738 if (var
->MokSBState
== 0) {
739 Print(L
"Disable Secure Boot? (y/n): ");
741 Print(L
"Enable Secure Boot? (y/n): ");
745 get_line (&length
, line
, 1, 1);
747 if (line
[0] == 'Y' || line
[0] == 'y') {
748 if (var
->MokSBState
== 0) {
749 efi_status
= uefi_call_wrapper(RT
->SetVariable
,
752 EFI_VARIABLE_NON_VOLATILE
|
753 EFI_VARIABLE_BOOTSERVICE_ACCESS
,
755 if (efi_status
!= EFI_SUCCESS
) {
756 Print(L
"Failed to set Secure Boot state\n");
760 LibDeleteVariable(L
"MokSBState",
764 Print(L
"Press a key to reboot system\n");
766 uefi_call_wrapper(RT
->ResetSystem
, 4, EfiResetWarm
,
767 EFI_SUCCESS
, 0, NULL
);
768 Print(L
"Failed to reboot\n");
771 } while (line
[0] != 'N' && line
[0] != 'n');
777 static INTN
mok_pw_prompt (void *MokPW
, void *data2
, void *data3
) {
778 EFI_GUID shim_lock_guid
= SHIM_LOCK_GUID
;
779 EFI_STATUS efi_status
;
780 UINTN MokPWSize
= (UINTN
)data2
;
781 UINT8 fail_count
= 0;
782 UINT8 hash
[SHA256_DIGEST_SIZE
];
783 CHAR16 password
[PASSWORD_MAX
];
787 if (MokPWSize
!= SHA256_DIGEST_SIZE
) {
788 Print(L
"Invalid MokPW variable contents\n");
792 LibDeleteVariable(L
"MokPW", &shim_lock_guid
);
794 uefi_call_wrapper(ST
->ConOut
->ClearScreen
, 1, ST
->ConOut
);
796 while (fail_count
< 3) {
797 Print(L
"Confirm MOK passphrase: ");
798 get_line(&length
, password
, PASSWORD_MAX
, 0);
800 if ((length
< PASSWORD_MIN
) || (length
> PASSWORD_MAX
)) {
801 Print(L
"Invalid password length\n");
806 efi_status
= compute_pw_hash(NULL
, 0, password
,
807 SB_PASSWORD_LEN
, hash
);
809 if (efi_status
!= EFI_SUCCESS
) {
810 Print(L
"Unable to generate password hash\n");
815 if (CompareMem(MokPW
, hash
, SHA256_DIGEST_SIZE
) != 0) {
816 Print(L
"Password doesn't match\n");
824 if (fail_count
>= 3) {
825 Print(L
"Password limit reached\n");
829 Print(L
"Set MOK password? (y/n): ");
832 get_line (&length
, line
, 1, 1);
834 if (line
[0] == 'Y' || line
[0] == 'y') {
835 efi_status
= uefi_call_wrapper(RT
->SetVariable
, 5,
838 EFI_VARIABLE_NON_VOLATILE
|
839 EFI_VARIABLE_BOOTSERVICE_ACCESS
,
841 if (efi_status
!= EFI_SUCCESS
) {
842 Print(L
"Failed to set MOK password\n");
846 Print(L
"Press a key to reboot system\n");
848 uefi_call_wrapper(RT
->ResetSystem
, 4, EfiResetWarm
,
849 EFI_SUCCESS
, 0, NULL
);
850 Print(L
"Failed to reboot\n");
853 } while (line
[0] != 'N' && line
[0] != 'n');
858 static UINTN
draw_menu (CHAR16
*header
, UINTN lines
, struct menu_item
*items
,
862 uefi_call_wrapper(ST
->ConOut
->ClearScreen
, 1, ST
->ConOut
);
864 uefi_call_wrapper(ST
->ConOut
->SetAttribute
, 2, ST
->ConOut
,
865 EFI_WHITE
| EFI_BACKGROUND_BLACK
);
867 Print(L
"%s UEFI key management\n\n", SHIM_VENDOR
);
870 Print(L
"%s", header
);
872 for (i
= 0; i
< count
; i
++) {
873 uefi_call_wrapper(ST
->ConOut
->SetAttribute
, 2, ST
->ConOut
,
874 items
[i
].colour
| EFI_BACKGROUND_BLACK
);
875 Print(L
" %s\n", items
[i
].text
);
878 uefi_call_wrapper(ST
->ConOut
->SetCursorPosition
, 3, ST
->ConOut
, 0, 0);
879 uefi_call_wrapper(ST
->ConOut
->EnableCursor
, 2, ST
->ConOut
, TRUE
);
884 static void free_menu (struct menu_item
*items
, UINTN count
) {
887 for (i
=0; i
<count
; i
++) {
889 FreePool(items
[i
].text
);
895 static void update_time (UINTN position
, UINTN timeout
)
897 uefi_call_wrapper(ST
->ConOut
->SetCursorPosition
, 3, ST
->ConOut
, 0,
900 uefi_call_wrapper(ST
->ConOut
->SetAttribute
, 2, ST
->ConOut
,
901 EFI_BLACK
| EFI_BACKGROUND_BLACK
);
903 Print(L
" ", timeout
);
905 uefi_call_wrapper(ST
->ConOut
->SetCursorPosition
, 3, ST
->ConOut
, 0,
908 uefi_call_wrapper(ST
->ConOut
->SetAttribute
, 2, ST
->ConOut
,
909 EFI_WHITE
| EFI_BACKGROUND_BLACK
);
912 Print(L
"Booting in %d seconds\n", timeout
);
914 Print(L
"Booting in %d second\n", timeout
);
917 static void run_menu (CHAR16
*header
, UINTN lines
, struct menu_item
*items
,
918 UINTN count
, UINTN timeout
) {
919 UINTN index
, pos
= 0, wait
= 0, offset
;
927 offset
= draw_menu (header
, lines
, items
, count
);
930 update_time(count
+ offset
+ 1, timeout
);
932 uefi_call_wrapper(ST
->ConOut
->SetCursorPosition
, 3, ST
->ConOut
,
934 status
= WaitForSingleEvent(ST
->ConIn
->WaitForKey
, wait
);
936 if (status
== EFI_TIMEOUT
) {
939 free_menu(items
, count
);
948 uefi_call_wrapper(BS
->WaitForEvent
, 3, 1,
949 &ST
->ConIn
->WaitForKey
, &index
);
950 uefi_call_wrapper(ST
->ConIn
->ReadKeyStroke
, 2, ST
->ConIn
,
953 switch(key
.ScanCode
) {
961 if (pos
== (count
- 1))
968 switch(key
.UnicodeChar
) {
970 case CHAR_CARRIAGE_RETURN
:
971 if (items
[pos
].callback
== NULL
) {
972 free_menu(items
, count
);
976 ret
= items
[pos
].callback(items
[pos
].data
,
980 Print(L
"Press a key to continue\n");
983 draw_menu (header
, lines
, items
, count
);
990 static UINTN
verify_certificate(void *cert
, UINTN size
)
993 if (!cert
|| size
== 0)
996 if (!(X509ConstructCertificate(cert
, size
, (UINT8
**) &X509Cert
)) ||
998 Print(L
"Invalid X509 certificate\n");
1003 X509_free(X509Cert
);
1007 static INTN
file_callback (void *data
, void *data2
, void *data3
) {
1008 EFI_FILE_INFO
*buffer
= NULL
;
1009 UINTN buffersize
= 0, mokbuffersize
;
1012 CHAR16
*filename
= data
;
1013 EFI_FILE
*parent
= data2
;
1014 BOOLEAN hash
= !!data3
;
1015 EFI_GUID file_info_guid
= EFI_FILE_INFO_ID
;
1016 EFI_GUID shim_lock_guid
= SHIM_LOCK_GUID
;
1017 EFI_SIGNATURE_LIST
*CertList
;
1018 EFI_SIGNATURE_DATA
*CertData
;
1019 void *mokbuffer
= NULL
;
1021 status
= uefi_call_wrapper(parent
->Open
, 5, parent
, &file
, filename
,
1022 EFI_FILE_MODE_READ
, 0);
1024 if (status
!= EFI_SUCCESS
)
1027 status
= uefi_call_wrapper(file
->GetInfo
, 4, file
, &file_info_guid
,
1028 &buffersize
, buffer
);
1030 if (status
== EFI_BUFFER_TOO_SMALL
) {
1031 buffer
= AllocatePool(buffersize
);
1032 status
= uefi_call_wrapper(file
->GetInfo
, 4, file
,
1033 &file_info_guid
, &buffersize
,
1040 buffersize
= buffer
->FileSize
;
1044 UINT8 sha256
[SHA256_DIGEST_SIZE
];
1045 UINT8 sha1
[SHA1_DIGEST_SIZE
];
1046 SHIM_LOCK
*shim_lock
;
1047 EFI_GUID shim_guid
= SHIM_LOCK_GUID
;
1048 PE_COFF_LOADER_IMAGE_CONTEXT context
;
1050 status
= LibLocateProtocol(&shim_guid
, (VOID
**)&shim_lock
);
1052 if (status
!= EFI_SUCCESS
)
1055 mokbuffersize
= sizeof(EFI_SIGNATURE_LIST
) + sizeof(EFI_GUID
) +
1058 mokbuffer
= AllocatePool(mokbuffersize
);
1063 binary
= AllocatePool(buffersize
);
1065 status
= uefi_call_wrapper(file
->Read
, 3, file
, &buffersize
,
1068 if (status
!= EFI_SUCCESS
)
1071 status
= shim_lock
->Context(binary
, buffersize
, &context
);
1073 if (status
!= EFI_SUCCESS
)
1076 status
= shim_lock
->Hash(binary
, buffersize
, &context
, sha256
,
1079 if (status
!= EFI_SUCCESS
)
1082 CertList
= mokbuffer
;
1083 CertList
->SignatureType
= EfiHashSha256Guid
;
1084 CertList
->SignatureSize
= 16 + SHA256_DIGEST_SIZE
;
1085 CertData
= (EFI_SIGNATURE_DATA
*)(((UINT8
*)mokbuffer
) +
1086 sizeof(EFI_SIGNATURE_LIST
));
1087 CopyMem(CertData
->SignatureData
, sha256
, SHA256_DIGEST_SIZE
);
1089 mokbuffersize
= buffersize
+ sizeof(EFI_SIGNATURE_LIST
) +
1091 mokbuffer
= AllocatePool(mokbuffersize
);
1096 CertList
= mokbuffer
;
1097 CertList
->SignatureType
= EfiCertX509Guid
;
1098 CertList
->SignatureSize
= 16 + buffersize
;
1099 status
= uefi_call_wrapper(file
->Read
, 3, file
, &buffersize
,
1100 mokbuffer
+ sizeof(EFI_SIGNATURE_LIST
) + 16);
1102 if (status
!= EFI_SUCCESS
)
1104 CertData
= (EFI_SIGNATURE_DATA
*)(((UINT8
*)mokbuffer
) +
1105 sizeof(EFI_SIGNATURE_LIST
));
1108 CertList
->SignatureListSize
= mokbuffersize
;
1109 CertList
->SignatureHeaderSize
= 0;
1110 CertData
->SignatureOwner
= shim_lock_guid
;
1113 if (!verify_certificate(CertData
->SignatureData
, buffersize
))
1117 mok_enrollment_prompt(mokbuffer
, mokbuffersize
, FALSE
);
1123 FreePool(mokbuffer
);
1128 static INTN
directory_callback (void *data
, void *data2
, void *data3
) {
1129 EFI_FILE_INFO
*buffer
= NULL
;
1130 UINTN buffersize
= 0;
1132 UINTN dircount
= 0, i
= 0;
1133 struct menu_item
*dircontent
;
1135 CHAR16
*filename
= data
;
1136 EFI_FILE
*root
= data2
;
1137 BOOLEAN hash
= !!data3
;
1139 status
= uefi_call_wrapper(root
->Open
, 5, root
, &dir
, filename
,
1140 EFI_FILE_MODE_READ
, 0);
1142 if (status
!= EFI_SUCCESS
)
1146 status
= uefi_call_wrapper(dir
->Read
, 3, dir
, &buffersize
,
1149 if (status
== EFI_BUFFER_TOO_SMALL
) {
1150 buffer
= AllocatePool(buffersize
);
1151 status
= uefi_call_wrapper(dir
->Read
, 3, dir
,
1152 &buffersize
, buffer
);
1155 if (status
!= EFI_SUCCESS
)
1161 if ((StrCmp(buffer
->FileName
, L
".") == 0) ||
1162 (StrCmp(buffer
->FileName
, L
"..") == 0))
1173 dircontent
= AllocatePool(sizeof(struct menu_item
) * dircount
);
1175 dircontent
[0].text
= StrDuplicate(L
"..");
1176 dircontent
[0].callback
= NULL
;
1177 dircontent
[0].colour
= EFI_YELLOW
;
1180 uefi_call_wrapper(dir
->SetPosition
, 2, dir
, 0);
1183 status
= uefi_call_wrapper(dir
->Read
, 3, dir
, &buffersize
,
1186 if (status
== EFI_BUFFER_TOO_SMALL
) {
1187 buffer
= AllocatePool(buffersize
);
1188 status
= uefi_call_wrapper(dir
->Read
, 3, dir
,
1189 &buffersize
, buffer
);
1192 if (status
!= EFI_SUCCESS
)
1198 if ((StrCmp(buffer
->FileName
, L
".") == 0) ||
1199 (StrCmp(buffer
->FileName
, L
"..") == 0))
1202 if (buffer
->Attribute
& EFI_FILE_DIRECTORY
) {
1203 dircontent
[i
].text
= StrDuplicate(buffer
->FileName
);
1204 dircontent
[i
].callback
= directory_callback
;
1205 dircontent
[i
].data
= dircontent
[i
].text
;
1206 dircontent
[i
].data2
= dir
;
1207 dircontent
[i
].data3
= data3
;
1208 dircontent
[i
].colour
= EFI_YELLOW
;
1210 dircontent
[i
].text
= StrDuplicate(buffer
->FileName
);
1211 dircontent
[i
].callback
= file_callback
;
1212 dircontent
[i
].data
= dircontent
[i
].text
;
1213 dircontent
[i
].data2
= dir
;
1214 dircontent
[i
].data3
= data3
;
1215 dircontent
[i
].colour
= EFI_WHITE
;
1225 run_menu(HASH_STRING
, 2, dircontent
, dircount
, 0);
1227 run_menu(CERT_STRING
, 2, dircontent
, dircount
, 0);
1232 static INTN
filesystem_callback (void *data
, void *data2
, void *data3
) {
1233 EFI_FILE_INFO
*buffer
= NULL
;
1234 UINTN buffersize
= 0;
1236 UINTN dircount
= 0, i
= 0;
1237 struct menu_item
*dircontent
;
1238 EFI_FILE
*root
= data
;
1239 BOOLEAN hash
= !!data3
;
1241 uefi_call_wrapper(root
->SetPosition
, 2, root
, 0);
1244 status
= uefi_call_wrapper(root
->Read
, 3, root
, &buffersize
,
1247 if (status
== EFI_BUFFER_TOO_SMALL
) {
1248 buffer
= AllocatePool(buffersize
);
1249 status
= uefi_call_wrapper(root
->Read
, 3, root
,
1250 &buffersize
, buffer
);
1253 if (status
!= EFI_SUCCESS
)
1259 if ((StrCmp(buffer
->FileName
, L
".") == 0) ||
1260 (StrCmp(buffer
->FileName
, L
"..") == 0))
1271 dircontent
= AllocatePool(sizeof(struct menu_item
) * dircount
);
1273 dircontent
[0].text
= StrDuplicate(L
"Return to filesystem list");
1274 dircontent
[0].callback
= NULL
;
1275 dircontent
[0].colour
= EFI_YELLOW
;
1278 uefi_call_wrapper(root
->SetPosition
, 2, root
, 0);
1281 status
= uefi_call_wrapper(root
->Read
, 3, root
, &buffersize
,
1284 if (status
== EFI_BUFFER_TOO_SMALL
) {
1285 buffer
= AllocatePool(buffersize
);
1286 status
= uefi_call_wrapper(root
->Read
, 3, root
,
1287 &buffersize
, buffer
);
1290 if (status
!= EFI_SUCCESS
)
1296 if ((StrCmp(buffer
->FileName
, L
".") == 0) ||
1297 (StrCmp(buffer
->FileName
, L
"..") == 0))
1300 if (buffer
->Attribute
& EFI_FILE_DIRECTORY
) {
1301 dircontent
[i
].text
= StrDuplicate(buffer
->FileName
);
1302 dircontent
[i
].callback
= directory_callback
;
1303 dircontent
[i
].data
= dircontent
[i
].text
;
1304 dircontent
[i
].data2
= root
;
1305 dircontent
[i
].data3
= data3
;
1306 dircontent
[i
].colour
= EFI_YELLOW
;
1308 dircontent
[i
].text
= StrDuplicate(buffer
->FileName
);
1309 dircontent
[i
].callback
= file_callback
;
1310 dircontent
[i
].data
= dircontent
[i
].text
;
1311 dircontent
[i
].data2
= root
;
1312 dircontent
[i
].data3
= data3
;
1313 dircontent
[i
].colour
= EFI_WHITE
;
1323 run_menu(HASH_STRING
, 2, dircontent
, dircount
, 0);
1325 run_menu(CERT_STRING
, 2, dircontent
, dircount
, 0);
1330 static INTN
find_fs (void *data
, void *data2
, void *data3
) {
1331 EFI_GUID fs_guid
= SIMPLE_FILE_SYSTEM_PROTOCOL
;
1333 UINTN OldSize
, NewSize
;
1334 EFI_HANDLE
**filesystem_handles
;
1335 struct menu_item
*filesystems
;
1336 BOOLEAN hash
= !!data3
;
1338 uefi_call_wrapper(BS
->LocateHandleBuffer
, 5, ByProtocol
, &fs_guid
,
1339 NULL
, &count
, &filesystem_handles
);
1341 if (!count
|| !filesystem_handles
) {
1342 Print(L
"No filesystems?\n");
1348 filesystems
= AllocatePool(sizeof(struct menu_item
) * count
);
1350 filesystems
[0].text
= StrDuplicate(L
"Exit");
1351 filesystems
[0].callback
= NULL
;
1352 filesystems
[0].colour
= EFI_YELLOW
;
1354 for (i
=1; i
<count
; i
++) {
1355 EFI_HANDLE
*fs
= filesystem_handles
[i
-1];
1356 EFI_FILE_IO_INTERFACE
*fs_interface
;
1357 EFI_DEVICE_PATH
*path
;
1360 CHAR16
*VolumeLabel
= NULL
;
1361 EFI_FILE_SYSTEM_INFO
*buffer
= NULL
;
1362 UINTN buffersize
= 0;
1363 EFI_GUID file_info_guid
= EFI_FILE_INFO_ID
;
1365 status
= uefi_call_wrapper(BS
->HandleProtocol
, 3, fs
, &fs_guid
,
1368 if (status
!= EFI_SUCCESS
|| !fs_interface
)
1371 path
= DevicePathFromHandle(fs
);
1373 status
= uefi_call_wrapper(fs_interface
->OpenVolume
, 2,
1374 fs_interface
, &root
);
1376 if (status
!= EFI_SUCCESS
|| !root
)
1379 status
= uefi_call_wrapper(root
->GetInfo
, 4, root
,
1380 &file_info_guid
, &buffersize
,
1383 if (status
== EFI_BUFFER_TOO_SMALL
) {
1384 buffer
= AllocatePool(buffersize
);
1385 status
= uefi_call_wrapper(root
->GetInfo
, 4, root
,
1387 &buffersize
, buffer
);
1390 if (status
== EFI_SUCCESS
)
1391 VolumeLabel
= buffer
->VolumeLabel
;
1394 filesystems
[i
].text
= DevicePathToStr(path
);
1396 filesystems
[i
].text
= StrDuplicate(L
"Unknown device\n");
1398 OldSize
= (StrLen(filesystems
[i
].text
) + 1) * sizeof(CHAR16
);
1399 NewSize
= OldSize
+ StrLen(VolumeLabel
) * sizeof(CHAR16
);
1400 filesystems
[i
].text
= ReallocatePool(filesystems
[i
].text
,
1402 StrCat(filesystems
[i
].text
, VolumeLabel
);
1408 filesystems
[i
].data
= root
;
1409 filesystems
[i
].data2
= NULL
;
1410 filesystems
[i
].data3
= data3
;
1411 filesystems
[i
].callback
= filesystem_callback
;
1412 filesystems
[i
].colour
= EFI_YELLOW
;
1415 uefi_call_wrapper(BS
->FreePool
, 1, filesystem_handles
);
1418 run_menu(HASH_STRING
, 2, filesystems
, count
, 0);
1420 run_menu(CERT_STRING
, 2, filesystems
, count
, 0);
1425 static BOOLEAN
verify_pw(void)
1427 EFI_GUID shim_lock_guid
= SHIM_LOCK_GUID
;
1428 EFI_STATUS efi_status
;
1429 CHAR16 password
[PASSWORD_MAX
];
1430 UINT8 fail_count
= 0;
1431 UINT8 hash
[SHA256_DIGEST_SIZE
];
1432 UINT8 pwhash
[SHA256_DIGEST_SIZE
];
1433 UINTN size
= SHA256_DIGEST_SIZE
;
1437 efi_status
= uefi_call_wrapper(RT
->GetVariable
, 5, L
"MokPWStore",
1438 &shim_lock_guid
, &attributes
, &size
,
1442 * If anything can attack the password it could just set it to a
1443 * known value, so there's no safety advantage in failing to validate
1444 * purely because of a failure to read the variable
1446 if (efi_status
!= EFI_SUCCESS
)
1449 if (attributes
& EFI_VARIABLE_RUNTIME_ACCESS
)
1452 uefi_call_wrapper(ST
->ConOut
->ClearScreen
, 1, ST
->ConOut
);
1454 while (fail_count
< 3) {
1455 Print(L
"Enter MOK password: ");
1456 get_line(&length
, password
, PASSWORD_MAX
, 0);
1458 if (length
< PASSWORD_MIN
|| length
> PASSWORD_MAX
) {
1459 Print(L
"Invalid password length\n");
1464 efi_status
= compute_pw_hash(NULL
, 0, password
, length
, hash
);
1466 if (efi_status
!= EFI_SUCCESS
) {
1467 Print(L
"Unable to generate password hash\n");
1472 if (CompareMem(pwhash
, hash
, SHA256_DIGEST_SIZE
) != 0) {
1473 Print(L
"Password doesn't match\n");
1481 Print(L
"Password limit reached\n");
1485 static EFI_STATUS
enter_mok_menu(EFI_HANDLE image_handle
, void *MokNew
,
1486 UINTN MokNewSize
, void *MokSB
,
1487 UINTN MokSBSize
, void *MokPW
, UINTN MokPWSize
)
1489 struct menu_item
*menu_item
;
1491 UINTN menucount
= 3, i
= 0;
1492 EFI_STATUS efi_status
;
1493 EFI_GUID shim_lock_guid
= SHIM_LOCK_GUID
;
1494 UINT8 auth
[SHA256_DIGEST_SIZE
];
1495 UINTN auth_size
= SHA256_DIGEST_SIZE
;
1498 if (verify_pw() == FALSE
)
1499 return EFI_ACCESS_DENIED
;
1501 efi_status
= uefi_call_wrapper(RT
->GetVariable
, 5, L
"MokAuth",
1503 &attributes
, &auth_size
, auth
);
1505 if ((efi_status
== EFI_SUCCESS
) && (auth_size
== SHA256_DIGEST_SIZE
))
1508 if (MokNew
|| MokAuth
)
1517 menu_item
= AllocateZeroPool(sizeof(struct menu_item
) * menucount
);
1520 return EFI_OUT_OF_RESOURCES
;
1522 menu_item
[i
].text
= StrDuplicate(L
"Continue boot");
1523 menu_item
[i
].colour
= EFI_WHITE
;
1524 menu_item
[i
].callback
= NULL
;
1528 if (MokNew
|| MokAuth
) {
1530 menu_item
[i
].text
= StrDuplicate(L
"Delete MOK");
1531 menu_item
[i
].colour
= EFI_WHITE
;
1532 menu_item
[i
].callback
= mok_deletion_prompt
;
1534 menu_item
[i
].text
= StrDuplicate(L
"Enroll MOK");
1535 menu_item
[i
].colour
= EFI_WHITE
;
1536 menu_item
[i
].data
= MokNew
;
1537 menu_item
[i
].data2
= (void *)MokNewSize
;
1538 menu_item
[i
].callback
= mok_enrollment_prompt_callback
;
1544 menu_item
[i
].text
= StrDuplicate(L
"Change Secure Boot state");
1545 menu_item
[i
].colour
= EFI_WHITE
;
1546 menu_item
[i
].callback
= mok_sb_prompt
;
1547 menu_item
[i
].data
= MokSB
;
1548 menu_item
[i
].data2
= (void *)MokSBSize
;
1553 menu_item
[i
].text
= StrDuplicate(L
"Set MOK password");
1554 menu_item
[i
].colour
= EFI_WHITE
;
1555 menu_item
[i
].callback
= mok_pw_prompt
;
1556 menu_item
[i
].data
= MokPW
;
1557 menu_item
[i
].data2
= (void *)MokPWSize
;
1561 menu_item
[i
].text
= StrDuplicate(L
"Enroll key from disk");
1562 menu_item
[i
].colour
= EFI_WHITE
;
1563 menu_item
[i
].callback
= find_fs
;
1564 menu_item
[i
].data3
= (void *)FALSE
;
1568 menu_item
[i
].text
= StrDuplicate(L
"Enroll hash from disk");
1569 menu_item
[i
].colour
= EFI_WHITE
;
1570 menu_item
[i
].callback
= find_fs
;
1571 menu_item
[i
].data3
= (void *)TRUE
;
1575 run_menu(NULL
, 0, menu_item
, menucount
, 10);
1577 uefi_call_wrapper(ST
->ConOut
->ClearScreen
, 1, ST
->ConOut
);
1582 static EFI_STATUS
check_mok_request(EFI_HANDLE image_handle
)
1584 EFI_GUID shim_lock_guid
= SHIM_LOCK_GUID
;
1585 UINTN MokNewSize
= 0, MokSBSize
= 0, MokPWSize
= 0;
1586 void *MokNew
= NULL
;
1590 MokNew
= LibGetVariableAndSize(L
"MokNew", &shim_lock_guid
, &MokNewSize
);
1592 MokSB
= LibGetVariableAndSize(L
"MokSB", &shim_lock_guid
, &MokSBSize
);
1594 MokPW
= LibGetVariableAndSize(L
"MokPW", &shim_lock_guid
, &MokPWSize
);
1596 enter_mok_menu(image_handle
, MokNew
, MokNewSize
, MokSB
, MokSBSize
,
1600 if (LibDeleteVariable(L
"MokNew", &shim_lock_guid
) != EFI_SUCCESS
) {
1601 Print(L
"Failed to delete MokNew\n");
1607 if (LibDeleteVariable(L
"MokSB", &shim_lock_guid
) != EFI_SUCCESS
) {
1608 Print(L
"Failed to delete MokSB\n");
1614 if (LibDeleteVariable(L
"MokPW", &shim_lock_guid
) != EFI_SUCCESS
) {
1615 Print(L
"Failed to delete MokPW\n");
1620 LibDeleteVariable(L
"MokAuth", &shim_lock_guid
);
1625 static EFI_STATUS
setup_rand (void)
1628 EFI_STATUS efi_status
;
1632 efi_status
= uefi_call_wrapper(RT
->GetTime
, 2, &time
, NULL
);
1634 if (efi_status
!= EFI_SUCCESS
)
1637 seed
= ((UINT64
)time
.Year
<< 48) | ((UINT64
)time
.Month
<< 40) |
1638 ((UINT64
)time
.Day
<< 32) | ((UINT64
)time
.Hour
<< 24) |
1639 ((UINT64
)time
.Minute
<< 16) | ((UINT64
)time
.Second
<< 8) |
1640 ((UINT64
)time
.Daylight
);
1642 status
= RandomSeed((UINT8
*)&seed
, sizeof(seed
));
1650 EFI_STATUS
efi_main (EFI_HANDLE image_handle
, EFI_SYSTEM_TABLE
*systab
)
1652 EFI_STATUS efi_status
;
1654 InitializeLib(image_handle
, systab
);
1658 efi_status
= check_mok_request(image_handle
);