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
,
669 uefi_call_wrapper(ST
->ConOut
->ClearScreen
, 1, ST
->ConOut
);
670 return mok_enrollment_prompt(MokNew
, (UINTN
)data2
, TRUE
);
673 static INTN
mok_deletion_prompt (void *MokNew
, void *data2
, void *data3
) {
676 EFI_STATUS efi_status
;
678 Print(L
"Erase all stored keys? (y/N): ");
680 get_line (&length
, line
, 1, 1);
682 if (line
[0] == 'Y' || line
[0] == 'y') {
683 efi_status
= store_keys(NULL
, 0, TRUE
);
685 if (efi_status
!= EFI_SUCCESS
) {
686 Print(L
"Failed to erase keys\n");
694 static INTN
mok_sb_prompt (void *MokSB
, void *data2
, void *data3
) {
695 EFI_GUID shim_lock_guid
= SHIM_LOCK_GUID
;
696 EFI_STATUS efi_status
;
697 UINTN MokSBSize
= (UINTN
)data2
;
698 MokSBvar
*var
= MokSB
;
700 UINT8 correct
= 0, fail_count
= 0;
701 UINT8 hash
[SHA256_DIGEST_SIZE
];
707 LibDeleteVariable(L
"MokSB", &shim_lock_guid
);
709 if (MokSBSize
!= sizeof(MokSBvar
)) {
710 Print(L
"Invalid MokSB variable contents\n");
714 uefi_call_wrapper(ST
->ConOut
->ClearScreen
, 1, ST
->ConOut
);
716 while (correct
< 3) {
717 RandomBytes (&pos
, sizeof(pos
));
719 pos
= pos
% var
->PWLen
;
721 Print(L
"Enter password character %d: ", pos
+ 1);
722 get_line(&length
, password
, 1, 0);
724 if (password
[0] != var
->Password
[pos
]) {
725 Print(L
"Invalid character\n");
735 if (fail_count
>= 3) {
736 Print(L
"Password limit reached\n");
740 if (var
->MokSBState
== 0) {
741 Print(L
"Disable Secure Boot? (y/n): ");
743 Print(L
"Enable Secure Boot? (y/n): ");
747 get_line (&length
, line
, 1, 1);
749 if (line
[0] == 'Y' || line
[0] == 'y') {
750 if (var
->MokSBState
== 0) {
751 efi_status
= uefi_call_wrapper(RT
->SetVariable
,
754 EFI_VARIABLE_NON_VOLATILE
|
755 EFI_VARIABLE_BOOTSERVICE_ACCESS
,
757 if (efi_status
!= EFI_SUCCESS
) {
758 Print(L
"Failed to set Secure Boot state\n");
762 LibDeleteVariable(L
"MokSBState",
766 Print(L
"Press a key to reboot system\n");
768 uefi_call_wrapper(RT
->ResetSystem
, 4, EfiResetWarm
,
769 EFI_SUCCESS
, 0, NULL
);
770 Print(L
"Failed to reboot\n");
773 } while (line
[0] != 'N' && line
[0] != 'n');
779 static INTN
mok_pw_prompt (void *MokPW
, void *data2
, void *data3
) {
780 EFI_GUID shim_lock_guid
= SHIM_LOCK_GUID
;
781 EFI_STATUS efi_status
;
782 UINTN MokPWSize
= (UINTN
)data2
;
783 UINT8 fail_count
= 0;
784 UINT8 hash
[SHA256_DIGEST_SIZE
];
785 CHAR16 password
[PASSWORD_MAX
];
789 if (MokPWSize
!= SHA256_DIGEST_SIZE
) {
790 Print(L
"Invalid MokPW variable contents\n");
794 LibDeleteVariable(L
"MokPW", &shim_lock_guid
);
796 uefi_call_wrapper(ST
->ConOut
->ClearScreen
, 1, ST
->ConOut
);
798 while (fail_count
< 3) {
799 Print(L
"Confirm MOK passphrase: ");
800 get_line(&length
, password
, PASSWORD_MAX
, 0);
802 if ((length
< PASSWORD_MIN
) || (length
> PASSWORD_MAX
)) {
803 Print(L
"Invalid password length\n");
808 efi_status
= compute_pw_hash(NULL
, 0, password
, length
, hash
);
810 if (efi_status
!= EFI_SUCCESS
) {
811 Print(L
"Unable to generate password hash\n");
816 if (CompareMem(MokPW
, hash
, SHA256_DIGEST_SIZE
) != 0) {
817 Print(L
"Password doesn't match\n");
825 if (fail_count
>= 3) {
826 Print(L
"Password limit reached\n");
830 Print(L
"Set MOK password? (y/n): ");
833 get_line (&length
, line
, 1, 1);
835 if (line
[0] == 'Y' || line
[0] == 'y') {
836 efi_status
= uefi_call_wrapper(RT
->SetVariable
, 5,
839 EFI_VARIABLE_NON_VOLATILE
|
840 EFI_VARIABLE_BOOTSERVICE_ACCESS
,
842 if (efi_status
!= EFI_SUCCESS
) {
843 Print(L
"Failed to set MOK password\n");
847 Print(L
"Press a key to reboot system\n");
849 uefi_call_wrapper(RT
->ResetSystem
, 4, EfiResetWarm
,
850 EFI_SUCCESS
, 0, NULL
);
851 Print(L
"Failed to reboot\n");
854 } while (line
[0] != 'N' && line
[0] != 'n');
859 static UINTN
draw_menu (CHAR16
*header
, UINTN lines
, struct menu_item
*items
,
863 uefi_call_wrapper(ST
->ConOut
->ClearScreen
, 1, ST
->ConOut
);
865 uefi_call_wrapper(ST
->ConOut
->SetAttribute
, 2, ST
->ConOut
,
866 EFI_WHITE
| EFI_BACKGROUND_BLACK
);
868 Print(L
"%s UEFI key management\n\n", SHIM_VENDOR
);
871 Print(L
"%s", header
);
873 for (i
= 0; i
< count
; i
++) {
874 uefi_call_wrapper(ST
->ConOut
->SetAttribute
, 2, ST
->ConOut
,
875 items
[i
].colour
| EFI_BACKGROUND_BLACK
);
876 Print(L
" %s\n", items
[i
].text
);
879 uefi_call_wrapper(ST
->ConOut
->SetCursorPosition
, 3, ST
->ConOut
, 0, 0);
880 uefi_call_wrapper(ST
->ConOut
->EnableCursor
, 2, ST
->ConOut
, TRUE
);
885 static void free_menu (struct menu_item
*items
, UINTN count
) {
888 for (i
=0; i
<count
; i
++) {
890 FreePool(items
[i
].text
);
896 static void update_time (UINTN position
, UINTN timeout
)
898 uefi_call_wrapper(ST
->ConOut
->SetCursorPosition
, 3, ST
->ConOut
, 0,
901 uefi_call_wrapper(ST
->ConOut
->SetAttribute
, 2, ST
->ConOut
,
902 EFI_BLACK
| EFI_BACKGROUND_BLACK
);
904 Print(L
" ", timeout
);
906 uefi_call_wrapper(ST
->ConOut
->SetCursorPosition
, 3, ST
->ConOut
, 0,
909 uefi_call_wrapper(ST
->ConOut
->SetAttribute
, 2, ST
->ConOut
,
910 EFI_WHITE
| EFI_BACKGROUND_BLACK
);
913 Print(L
"Booting in %d seconds\n", timeout
);
915 Print(L
"Booting in %d second\n", timeout
);
918 static void run_menu (CHAR16
*header
, UINTN lines
, struct menu_item
*items
,
919 UINTN count
, UINTN timeout
) {
920 UINTN index
, pos
= 0, wait
= 0, offset
;
928 offset
= draw_menu (header
, lines
, items
, count
);
931 update_time(count
+ offset
+ 1, timeout
);
933 uefi_call_wrapper(ST
->ConOut
->SetCursorPosition
, 3, ST
->ConOut
,
935 status
= WaitForSingleEvent(ST
->ConIn
->WaitForKey
, wait
);
937 if (status
== EFI_TIMEOUT
) {
940 free_menu(items
, count
);
949 uefi_call_wrapper(BS
->WaitForEvent
, 3, 1,
950 &ST
->ConIn
->WaitForKey
, &index
);
951 uefi_call_wrapper(ST
->ConIn
->ReadKeyStroke
, 2, ST
->ConIn
,
954 switch(key
.ScanCode
) {
962 if (pos
== (count
- 1))
969 switch(key
.UnicodeChar
) {
971 case CHAR_CARRIAGE_RETURN
:
972 if (items
[pos
].callback
== NULL
) {
973 free_menu(items
, count
);
977 ret
= items
[pos
].callback(items
[pos
].data
,
981 Print(L
"Press a key to continue\n");
984 draw_menu (header
, lines
, items
, count
);
991 static UINTN
verify_certificate(void *cert
, UINTN size
)
994 if (!cert
|| size
== 0)
997 if (!(X509ConstructCertificate(cert
, size
, (UINT8
**) &X509Cert
)) ||
999 Print(L
"Invalid X509 certificate\n");
1004 X509_free(X509Cert
);
1008 static INTN
file_callback (void *data
, void *data2
, void *data3
) {
1009 EFI_FILE_INFO
*buffer
= NULL
;
1010 UINTN buffersize
= 0, mokbuffersize
;
1013 CHAR16
*filename
= data
;
1014 EFI_FILE
*parent
= data2
;
1015 BOOLEAN hash
= !!data3
;
1016 EFI_GUID file_info_guid
= EFI_FILE_INFO_ID
;
1017 EFI_GUID shim_lock_guid
= SHIM_LOCK_GUID
;
1018 EFI_SIGNATURE_LIST
*CertList
;
1019 EFI_SIGNATURE_DATA
*CertData
;
1020 void *mokbuffer
= NULL
;
1022 status
= uefi_call_wrapper(parent
->Open
, 5, parent
, &file
, filename
,
1023 EFI_FILE_MODE_READ
, 0);
1025 if (status
!= EFI_SUCCESS
)
1028 status
= uefi_call_wrapper(file
->GetInfo
, 4, file
, &file_info_guid
,
1029 &buffersize
, buffer
);
1031 if (status
== EFI_BUFFER_TOO_SMALL
) {
1032 buffer
= AllocatePool(buffersize
);
1033 status
= uefi_call_wrapper(file
->GetInfo
, 4, file
,
1034 &file_info_guid
, &buffersize
,
1041 buffersize
= buffer
->FileSize
;
1045 UINT8 sha256
[SHA256_DIGEST_SIZE
];
1046 UINT8 sha1
[SHA1_DIGEST_SIZE
];
1047 SHIM_LOCK
*shim_lock
;
1048 EFI_GUID shim_guid
= SHIM_LOCK_GUID
;
1049 PE_COFF_LOADER_IMAGE_CONTEXT context
;
1051 status
= LibLocateProtocol(&shim_guid
, (VOID
**)&shim_lock
);
1053 if (status
!= EFI_SUCCESS
)
1056 mokbuffersize
= sizeof(EFI_SIGNATURE_LIST
) + sizeof(EFI_GUID
) +
1059 mokbuffer
= AllocatePool(mokbuffersize
);
1064 binary
= AllocatePool(buffersize
);
1066 status
= uefi_call_wrapper(file
->Read
, 3, file
, &buffersize
,
1069 if (status
!= EFI_SUCCESS
)
1072 status
= shim_lock
->Context(binary
, buffersize
, &context
);
1074 if (status
!= EFI_SUCCESS
)
1077 status
= shim_lock
->Hash(binary
, buffersize
, &context
, sha256
,
1080 if (status
!= EFI_SUCCESS
)
1083 CertList
= mokbuffer
;
1084 CertList
->SignatureType
= EfiHashSha256Guid
;
1085 CertList
->SignatureSize
= 16 + SHA256_DIGEST_SIZE
;
1086 CertData
= (EFI_SIGNATURE_DATA
*)(((UINT8
*)mokbuffer
) +
1087 sizeof(EFI_SIGNATURE_LIST
));
1088 CopyMem(CertData
->SignatureData
, sha256
, SHA256_DIGEST_SIZE
);
1090 mokbuffersize
= buffersize
+ sizeof(EFI_SIGNATURE_LIST
) +
1092 mokbuffer
= AllocatePool(mokbuffersize
);
1097 CertList
= mokbuffer
;
1098 CertList
->SignatureType
= EfiCertX509Guid
;
1099 CertList
->SignatureSize
= 16 + buffersize
;
1100 status
= uefi_call_wrapper(file
->Read
, 3, file
, &buffersize
,
1101 mokbuffer
+ sizeof(EFI_SIGNATURE_LIST
) + 16);
1103 if (status
!= EFI_SUCCESS
)
1105 CertData
= (EFI_SIGNATURE_DATA
*)(((UINT8
*)mokbuffer
) +
1106 sizeof(EFI_SIGNATURE_LIST
));
1109 CertList
->SignatureListSize
= mokbuffersize
;
1110 CertList
->SignatureHeaderSize
= 0;
1111 CertData
->SignatureOwner
= shim_lock_guid
;
1114 if (!verify_certificate(CertData
->SignatureData
, buffersize
))
1118 mok_enrollment_prompt(mokbuffer
, mokbuffersize
, FALSE
);
1124 FreePool(mokbuffer
);
1129 static INTN
directory_callback (void *data
, void *data2
, void *data3
) {
1130 EFI_FILE_INFO
*buffer
= NULL
;
1131 UINTN buffersize
= 0;
1133 UINTN dircount
= 0, i
= 0;
1134 struct menu_item
*dircontent
;
1136 CHAR16
*filename
= data
;
1137 EFI_FILE
*root
= data2
;
1138 BOOLEAN hash
= !!data3
;
1140 status
= uefi_call_wrapper(root
->Open
, 5, root
, &dir
, filename
,
1141 EFI_FILE_MODE_READ
, 0);
1143 if (status
!= EFI_SUCCESS
)
1147 status
= uefi_call_wrapper(dir
->Read
, 3, dir
, &buffersize
,
1150 if (status
== EFI_BUFFER_TOO_SMALL
) {
1151 buffer
= AllocatePool(buffersize
);
1152 status
= uefi_call_wrapper(dir
->Read
, 3, dir
,
1153 &buffersize
, buffer
);
1156 if (status
!= EFI_SUCCESS
)
1162 if ((StrCmp(buffer
->FileName
, L
".") == 0) ||
1163 (StrCmp(buffer
->FileName
, L
"..") == 0))
1174 dircontent
= AllocatePool(sizeof(struct menu_item
) * dircount
);
1176 dircontent
[0].text
= StrDuplicate(L
"..");
1177 dircontent
[0].callback
= NULL
;
1178 dircontent
[0].colour
= EFI_YELLOW
;
1181 uefi_call_wrapper(dir
->SetPosition
, 2, dir
, 0);
1184 status
= uefi_call_wrapper(dir
->Read
, 3, dir
, &buffersize
,
1187 if (status
== EFI_BUFFER_TOO_SMALL
) {
1188 buffer
= AllocatePool(buffersize
);
1189 status
= uefi_call_wrapper(dir
->Read
, 3, dir
,
1190 &buffersize
, buffer
);
1193 if (status
!= EFI_SUCCESS
)
1199 if ((StrCmp(buffer
->FileName
, L
".") == 0) ||
1200 (StrCmp(buffer
->FileName
, L
"..") == 0))
1203 if (buffer
->Attribute
& EFI_FILE_DIRECTORY
) {
1204 dircontent
[i
].text
= StrDuplicate(buffer
->FileName
);
1205 dircontent
[i
].callback
= directory_callback
;
1206 dircontent
[i
].data
= dircontent
[i
].text
;
1207 dircontent
[i
].data2
= dir
;
1208 dircontent
[i
].data3
= data3
;
1209 dircontent
[i
].colour
= EFI_YELLOW
;
1211 dircontent
[i
].text
= StrDuplicate(buffer
->FileName
);
1212 dircontent
[i
].callback
= file_callback
;
1213 dircontent
[i
].data
= dircontent
[i
].text
;
1214 dircontent
[i
].data2
= dir
;
1215 dircontent
[i
].data3
= data3
;
1216 dircontent
[i
].colour
= EFI_WHITE
;
1226 run_menu(HASH_STRING
, 2, dircontent
, dircount
, 0);
1228 run_menu(CERT_STRING
, 2, dircontent
, dircount
, 0);
1233 static INTN
filesystem_callback (void *data
, void *data2
, void *data3
) {
1234 EFI_FILE_INFO
*buffer
= NULL
;
1235 UINTN buffersize
= 0;
1237 UINTN dircount
= 0, i
= 0;
1238 struct menu_item
*dircontent
;
1239 EFI_FILE
*root
= data
;
1240 BOOLEAN hash
= !!data3
;
1242 uefi_call_wrapper(root
->SetPosition
, 2, root
, 0);
1245 status
= uefi_call_wrapper(root
->Read
, 3, root
, &buffersize
,
1248 if (status
== EFI_BUFFER_TOO_SMALL
) {
1249 buffer
= AllocatePool(buffersize
);
1250 status
= uefi_call_wrapper(root
->Read
, 3, root
,
1251 &buffersize
, buffer
);
1254 if (status
!= EFI_SUCCESS
)
1260 if ((StrCmp(buffer
->FileName
, L
".") == 0) ||
1261 (StrCmp(buffer
->FileName
, L
"..") == 0))
1272 dircontent
= AllocatePool(sizeof(struct menu_item
) * dircount
);
1274 dircontent
[0].text
= StrDuplicate(L
"Return to filesystem list");
1275 dircontent
[0].callback
= NULL
;
1276 dircontent
[0].colour
= EFI_YELLOW
;
1279 uefi_call_wrapper(root
->SetPosition
, 2, root
, 0);
1282 status
= uefi_call_wrapper(root
->Read
, 3, root
, &buffersize
,
1285 if (status
== EFI_BUFFER_TOO_SMALL
) {
1286 buffer
= AllocatePool(buffersize
);
1287 status
= uefi_call_wrapper(root
->Read
, 3, root
,
1288 &buffersize
, buffer
);
1291 if (status
!= EFI_SUCCESS
)
1297 if ((StrCmp(buffer
->FileName
, L
".") == 0) ||
1298 (StrCmp(buffer
->FileName
, L
"..") == 0))
1301 if (buffer
->Attribute
& EFI_FILE_DIRECTORY
) {
1302 dircontent
[i
].text
= StrDuplicate(buffer
->FileName
);
1303 dircontent
[i
].callback
= directory_callback
;
1304 dircontent
[i
].data
= dircontent
[i
].text
;
1305 dircontent
[i
].data2
= root
;
1306 dircontent
[i
].data3
= data3
;
1307 dircontent
[i
].colour
= EFI_YELLOW
;
1309 dircontent
[i
].text
= StrDuplicate(buffer
->FileName
);
1310 dircontent
[i
].callback
= file_callback
;
1311 dircontent
[i
].data
= dircontent
[i
].text
;
1312 dircontent
[i
].data2
= root
;
1313 dircontent
[i
].data3
= data3
;
1314 dircontent
[i
].colour
= EFI_WHITE
;
1324 run_menu(HASH_STRING
, 2, dircontent
, dircount
, 0);
1326 run_menu(CERT_STRING
, 2, dircontent
, dircount
, 0);
1331 static INTN
find_fs (void *data
, void *data2
, void *data3
) {
1332 EFI_GUID fs_guid
= SIMPLE_FILE_SYSTEM_PROTOCOL
;
1334 UINTN OldSize
, NewSize
;
1335 EFI_HANDLE
**filesystem_handles
;
1336 struct menu_item
*filesystems
;
1337 BOOLEAN hash
= !!data3
;
1339 uefi_call_wrapper(BS
->LocateHandleBuffer
, 5, ByProtocol
, &fs_guid
,
1340 NULL
, &count
, &filesystem_handles
);
1342 if (!count
|| !filesystem_handles
) {
1343 Print(L
"No filesystems?\n");
1349 filesystems
= AllocatePool(sizeof(struct menu_item
) * count
);
1351 filesystems
[0].text
= StrDuplicate(L
"Exit");
1352 filesystems
[0].callback
= NULL
;
1353 filesystems
[0].colour
= EFI_YELLOW
;
1355 for (i
=1; i
<count
; i
++) {
1356 EFI_HANDLE
*fs
= filesystem_handles
[i
-1];
1357 EFI_FILE_IO_INTERFACE
*fs_interface
;
1358 EFI_DEVICE_PATH
*path
;
1361 CHAR16
*VolumeLabel
= NULL
;
1362 EFI_FILE_SYSTEM_INFO
*buffer
= NULL
;
1363 UINTN buffersize
= 0;
1364 EFI_GUID file_info_guid
= EFI_FILE_INFO_ID
;
1366 status
= uefi_call_wrapper(BS
->HandleProtocol
, 3, fs
, &fs_guid
,
1369 if (status
!= EFI_SUCCESS
|| !fs_interface
)
1372 path
= DevicePathFromHandle(fs
);
1374 status
= uefi_call_wrapper(fs_interface
->OpenVolume
, 2,
1375 fs_interface
, &root
);
1377 if (status
!= EFI_SUCCESS
|| !root
)
1380 status
= uefi_call_wrapper(root
->GetInfo
, 4, root
,
1381 &file_info_guid
, &buffersize
,
1384 if (status
== EFI_BUFFER_TOO_SMALL
) {
1385 buffer
= AllocatePool(buffersize
);
1386 status
= uefi_call_wrapper(root
->GetInfo
, 4, root
,
1388 &buffersize
, buffer
);
1391 if (status
== EFI_SUCCESS
)
1392 VolumeLabel
= buffer
->VolumeLabel
;
1395 filesystems
[i
].text
= DevicePathToStr(path
);
1397 filesystems
[i
].text
= StrDuplicate(L
"Unknown device\n");
1399 OldSize
= (StrLen(filesystems
[i
].text
) + 1) * sizeof(CHAR16
);
1400 NewSize
= OldSize
+ StrLen(VolumeLabel
) * sizeof(CHAR16
);
1401 filesystems
[i
].text
= ReallocatePool(filesystems
[i
].text
,
1403 StrCat(filesystems
[i
].text
, VolumeLabel
);
1409 filesystems
[i
].data
= root
;
1410 filesystems
[i
].data2
= NULL
;
1411 filesystems
[i
].data3
= data3
;
1412 filesystems
[i
].callback
= filesystem_callback
;
1413 filesystems
[i
].colour
= EFI_YELLOW
;
1416 uefi_call_wrapper(BS
->FreePool
, 1, filesystem_handles
);
1419 run_menu(HASH_STRING
, 2, filesystems
, count
, 0);
1421 run_menu(CERT_STRING
, 2, filesystems
, count
, 0);
1426 static BOOLEAN
verify_pw(void)
1428 EFI_GUID shim_lock_guid
= SHIM_LOCK_GUID
;
1429 EFI_STATUS efi_status
;
1430 CHAR16 password
[PASSWORD_MAX
];
1431 UINT8 fail_count
= 0;
1432 UINT8 hash
[SHA256_DIGEST_SIZE
];
1433 UINT8 pwhash
[SHA256_DIGEST_SIZE
];
1434 UINTN size
= SHA256_DIGEST_SIZE
;
1438 efi_status
= uefi_call_wrapper(RT
->GetVariable
, 5, L
"MokPWStore",
1439 &shim_lock_guid
, &attributes
, &size
,
1443 * If anything can attack the password it could just set it to a
1444 * known value, so there's no safety advantage in failing to validate
1445 * purely because of a failure to read the variable
1447 if (efi_status
!= EFI_SUCCESS
)
1450 if (attributes
& EFI_VARIABLE_RUNTIME_ACCESS
)
1453 uefi_call_wrapper(ST
->ConOut
->ClearScreen
, 1, ST
->ConOut
);
1455 while (fail_count
< 3) {
1456 Print(L
"Enter MOK password: ");
1457 get_line(&length
, password
, PASSWORD_MAX
, 0);
1459 if (length
< PASSWORD_MIN
|| length
> PASSWORD_MAX
) {
1460 Print(L
"Invalid password length\n");
1465 efi_status
= compute_pw_hash(NULL
, 0, password
, length
, hash
);
1467 if (efi_status
!= EFI_SUCCESS
) {
1468 Print(L
"Unable to generate password hash\n");
1473 if (CompareMem(pwhash
, hash
, SHA256_DIGEST_SIZE
) != 0) {
1474 Print(L
"Password doesn't match\n");
1482 Print(L
"Password limit reached\n");
1486 static EFI_STATUS
enter_mok_menu(EFI_HANDLE image_handle
, void *MokNew
,
1487 UINTN MokNewSize
, void *MokSB
,
1488 UINTN MokSBSize
, void *MokPW
, UINTN MokPWSize
)
1490 struct menu_item
*menu_item
;
1492 UINTN menucount
= 3, i
= 0;
1493 EFI_STATUS efi_status
;
1494 EFI_GUID shim_lock_guid
= SHIM_LOCK_GUID
;
1495 UINT8 auth
[SHA256_DIGEST_SIZE
];
1496 UINTN auth_size
= SHA256_DIGEST_SIZE
;
1499 if (verify_pw() == FALSE
)
1500 return EFI_ACCESS_DENIED
;
1502 efi_status
= uefi_call_wrapper(RT
->GetVariable
, 5, L
"MokAuth",
1504 &attributes
, &auth_size
, auth
);
1506 if ((efi_status
== EFI_SUCCESS
) && (auth_size
== SHA256_DIGEST_SIZE
))
1509 if (MokNew
|| MokAuth
)
1518 menu_item
= AllocateZeroPool(sizeof(struct menu_item
) * menucount
);
1521 return EFI_OUT_OF_RESOURCES
;
1523 menu_item
[i
].text
= StrDuplicate(L
"Continue boot");
1524 menu_item
[i
].colour
= EFI_WHITE
;
1525 menu_item
[i
].callback
= NULL
;
1529 if (MokNew
|| MokAuth
) {
1531 menu_item
[i
].text
= StrDuplicate(L
"Delete MOK");
1532 menu_item
[i
].colour
= EFI_WHITE
;
1533 menu_item
[i
].callback
= mok_deletion_prompt
;
1535 menu_item
[i
].text
= StrDuplicate(L
"Enroll MOK");
1536 menu_item
[i
].colour
= EFI_WHITE
;
1537 menu_item
[i
].data
= MokNew
;
1538 menu_item
[i
].data2
= (void *)MokNewSize
;
1539 menu_item
[i
].callback
= mok_enrollment_prompt_callback
;
1545 menu_item
[i
].text
= StrDuplicate(L
"Change Secure Boot state");
1546 menu_item
[i
].colour
= EFI_WHITE
;
1547 menu_item
[i
].callback
= mok_sb_prompt
;
1548 menu_item
[i
].data
= MokSB
;
1549 menu_item
[i
].data2
= (void *)MokSBSize
;
1554 menu_item
[i
].text
= StrDuplicate(L
"Set MOK password");
1555 menu_item
[i
].colour
= EFI_WHITE
;
1556 menu_item
[i
].callback
= mok_pw_prompt
;
1557 menu_item
[i
].data
= MokPW
;
1558 menu_item
[i
].data2
= (void *)MokPWSize
;
1562 menu_item
[i
].text
= StrDuplicate(L
"Enroll key from disk");
1563 menu_item
[i
].colour
= EFI_WHITE
;
1564 menu_item
[i
].callback
= find_fs
;
1565 menu_item
[i
].data3
= (void *)FALSE
;
1569 menu_item
[i
].text
= StrDuplicate(L
"Enroll hash from disk");
1570 menu_item
[i
].colour
= EFI_WHITE
;
1571 menu_item
[i
].callback
= find_fs
;
1572 menu_item
[i
].data3
= (void *)TRUE
;
1576 run_menu(NULL
, 0, menu_item
, menucount
, 10);
1578 uefi_call_wrapper(ST
->ConOut
->ClearScreen
, 1, ST
->ConOut
);
1583 static EFI_STATUS
check_mok_request(EFI_HANDLE image_handle
)
1585 EFI_GUID shim_lock_guid
= SHIM_LOCK_GUID
;
1586 UINTN MokNewSize
= 0, MokSBSize
= 0, MokPWSize
= 0;
1587 void *MokNew
= NULL
;
1591 MokNew
= LibGetVariableAndSize(L
"MokNew", &shim_lock_guid
, &MokNewSize
);
1593 MokSB
= LibGetVariableAndSize(L
"MokSB", &shim_lock_guid
, &MokSBSize
);
1595 MokPW
= LibGetVariableAndSize(L
"MokPW", &shim_lock_guid
, &MokPWSize
);
1597 enter_mok_menu(image_handle
, MokNew
, MokNewSize
, MokSB
, MokSBSize
,
1601 if (LibDeleteVariable(L
"MokNew", &shim_lock_guid
) != EFI_SUCCESS
) {
1602 Print(L
"Failed to delete MokNew\n");
1608 if (LibDeleteVariable(L
"MokSB", &shim_lock_guid
) != EFI_SUCCESS
) {
1609 Print(L
"Failed to delete MokSB\n");
1615 if (LibDeleteVariable(L
"MokPW", &shim_lock_guid
) != EFI_SUCCESS
) {
1616 Print(L
"Failed to delete MokPW\n");
1621 LibDeleteVariable(L
"MokAuth", &shim_lock_guid
);
1626 static EFI_STATUS
setup_rand (void)
1629 EFI_STATUS efi_status
;
1633 efi_status
= uefi_call_wrapper(RT
->GetTime
, 2, &time
, NULL
);
1635 if (efi_status
!= EFI_SUCCESS
)
1638 seed
= ((UINT64
)time
.Year
<< 48) | ((UINT64
)time
.Month
<< 40) |
1639 ((UINT64
)time
.Day
<< 32) | ((UINT64
)time
.Hour
<< 24) |
1640 ((UINT64
)time
.Minute
<< 16) | ((UINT64
)time
.Second
<< 8) |
1641 ((UINT64
)time
.Daylight
);
1643 status
= RandomSeed((UINT8
*)&seed
, sizeof(seed
));
1651 EFI_STATUS
efi_main (EFI_HANDLE image_handle
, EFI_SYSTEM_TABLE
*systab
)
1653 EFI_STATUS efi_status
;
1655 InitializeLib(image_handle
, systab
);
1659 efi_status
= check_mok_request(image_handle
);