3 #include <Library/BaseCryptLib.h>
4 #include <openssl/x509.h>
7 #define PASSWORD_MAX 16
15 static EFI_STATUS
get_variable (CHAR16
*name
, EFI_GUID guid
, UINT32
*attributes
,
16 UINTN
*size
, void **buffer
)
18 EFI_STATUS efi_status
;
19 char allocate
= !(*size
);
21 efi_status
= uefi_call_wrapper(RT
->GetVariable
, 5, name
, &guid
,
22 attributes
, size
, buffer
);
24 if (efi_status
!= EFI_BUFFER_TOO_SMALL
|| !allocate
) {
29 *buffer
= AllocatePool(*size
);
32 Print(L
"Unable to allocate variable buffer\n");
33 return EFI_OUT_OF_RESOURCES
;
36 efi_status
= uefi_call_wrapper(RT
->GetVariable
, 5, name
, &guid
,
37 attributes
, size
, *buffer
);
42 static EFI_STATUS
delete_variable (CHAR16
*name
, EFI_GUID guid
)
44 EFI_STATUS efi_status
;
46 efi_status
= uefi_call_wrapper(RT
->SetVariable
, 5, name
, &guid
,
52 static EFI_INPUT_KEY
get_keystroke (void)
57 uefi_call_wrapper(BS
->WaitForEvent
, 3, 1, &ST
->ConIn
->WaitForKey
,
59 uefi_call_wrapper(ST
->ConIn
->ReadKeyStroke
, 2, ST
->ConIn
, &key
);
64 static EFI_STATUS
get_sha256sum (void *Data
, int DataSize
, UINT8
*hash
)
70 ctxsize
= Sha256GetContextSize();
71 ctx
= AllocatePool(ctxsize
);
74 Print(L
"Unable to allocate memory for hash context\n");
75 return EFI_OUT_OF_RESOURCES
;
78 if (!Sha256Init(ctx
)) {
79 Print(L
"Unable to initialise hash\n");
80 status
= EFI_OUT_OF_RESOURCES
;
84 if (!(Sha256Update(ctx
, Data
, DataSize
))) {
85 Print(L
"Unable to generate hash\n");
86 status
= EFI_OUT_OF_RESOURCES
;
90 if (!(Sha256Final(ctx
, hash
))) {
91 Print(L
"Unable to finalise hash\n");
92 status
= EFI_OUT_OF_RESOURCES
;
101 static MokListNode
*build_mok_list(UINT32 num
, void *Data
, UINTN DataSize
) {
103 INT64 remain
= DataSize
;
107 if (DataSize
< sizeof(UINT32
))
110 list
= AllocatePool(sizeof(MokListNode
) * num
);
113 Print(L
"Unable to allocate MOK list\n");
118 for (i
= 0; i
< num
; i
++) {
119 CopyMem(&list
[i
].MokSize
, ptr
, sizeof(UINT32
));
120 remain
-= sizeof(UINT32
) + list
[i
].MokSize
;
123 Print(L
"the list was corrupted\n");
128 ptr
+= sizeof(UINT32
);
130 ptr
+= list
[i
].MokSize
;
136 static void print_x509_name (X509_NAME
*X509Name
, CHAR16
*name
)
140 str
= X509_NAME_oneline(X509Name
, NULL
, 0);
142 Print(L
" %s:\n %a\n", name
, str
);
147 static const char *mon
[12]= {
148 "Jan","Feb","Mar","Apr","May","Jun",
149 "Jul","Aug","Sep","Oct","Nov","Dec"
152 static void print_x509_GENERALIZEDTIME_time (ASN1_TIME
*time
, CHAR16
*time_string
)
157 int y
= 0,M
= 0,d
= 0,h
= 0,m
= 0,s
= 0;
162 v
=(char *)time
->data
;
170 for (i
=0; i
<12; i
++) {
171 if ((v
[i
] > '9') || (v
[i
] < '0'))
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');
178 if ((M
> 12) || (M
< 1))
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');
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. */
194 while (14 + f_len
< l
&& f
[f_len
] >= '0' &&
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":"");
206 static void print_x509_UTCTIME_time (ASN1_TIME
*time
, CHAR16
*time_string
)
211 int y
= 0,M
= 0,d
= 0,h
= 0,m
= 0,s
= 0;
214 v
=(char *)time
->data
;
223 if ((v
[i
] > '9') || (v
[i
] < '0'))
226 y
= (v
[0]-'0')*10+(v
[1]-'0');
231 M
= (v
[2]-'0')*10+(v
[3]-'0');
233 if ((M
> 12) || (M
< 1))
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');
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');
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":"");
251 static void print_x509_time (ASN1_TIME
*time
, CHAR16
*name
)
253 CHAR16 time_string
[30];
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
);
260 time_string
[0] = '\0';
263 Print(L
" %s:\n %s\n", name
, time_string
);
266 static void show_x509_info (X509
*X509Cert
)
268 ASN1_INTEGER
*serial
;
270 unsigned char hexbuf
[30];
274 serial
= X509_get_serialNumber(X509Cert
);
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
]);
283 Print(L
"%02x\n", hexbuf
[n
-1]);
286 X509Name
= X509_get_issuer_name(X509Cert
);
288 print_x509_name(X509Name
, L
"Issuer");
291 X509Name
= X509_get_subject_name(X509Cert
);
293 print_x509_name(X509Name
, L
"Subject");
296 time
= X509_get_notBefore(X509Cert
);
298 print_x509_time(time
, L
"Validity from");
301 time
= X509_get_notAfter(X509Cert
);
303 print_x509_time(time
, L
"Validity till");
307 static void show_mok_info (void *Mok
, UINTN MokSize
)
309 EFI_STATUS efi_status
;
310 UINT8 hash
[SHA256_DIGEST_SIZE
];
314 if (!Mok
|| MokSize
== 0)
317 if (X509ConstructCertificate(Mok
, MokSize
, (UINT8
**) &X509Cert
) &&
319 show_x509_info(X509Cert
);
322 Print(L
" Not a valid X509 certificate\n\n");
326 efi_status
= get_sha256sum(Mok
, MokSize
, hash
);
328 if (efi_status
!= EFI_SUCCESS
) {
329 Print(L
"Failed to compute MOK fingerprint\n");
333 Print(L
" Fingerprint (SHA256):\n ");
334 for (i
= 0; i
< SHA256_DIGEST_SIZE
; i
++) {
335 Print(L
" %02x", hash
[i
]);
342 static INTN
get_number ()
344 EFI_INPUT_KEY input_key
;
349 input_key
= get_keystroke();
351 if ((input_key
.UnicodeChar
< '0' ||
352 input_key
.UnicodeChar
> '9' ||
354 input_key
.UnicodeChar
!= CHAR_BACKSPACE
) {
358 if (count
== 0 && input_key
.UnicodeChar
== CHAR_BACKSPACE
)
361 Print(L
"%c", input_key
.UnicodeChar
);
363 if (input_key
.UnicodeChar
== CHAR_BACKSPACE
) {
364 input
[--count
] = '\0';
368 input
[count
++] = input_key
.UnicodeChar
;
369 } while (input_key
.UnicodeChar
!= CHAR_CARRIAGE_RETURN
);
376 return (INTN
)Atoi(input
);
379 static UINT8
list_keys (void *MokNew
, UINTN MokNewSize
)
382 MokListNode
*keys
= NULL
;
386 CopyMem(&MokNum
, MokNew
, sizeof(UINT32
));
388 Print(L
"No key exists\n");
392 keys
= build_mok_list(MokNum
,
393 (void *)MokNew
+ sizeof(UINT32
),
394 MokNewSize
- sizeof(UINT32
));
397 Print(L
"Failed to construct key list in MokNew\n");
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
);
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
);
414 Print(L
"Key Number: ");
416 key_num
= get_number();
424 } while (key_num
!= 0);
431 static UINT8
mok_enrollment_prompt (void *MokNew
, UINTN MokNewSize
)
436 if (!list_keys(MokNew
, MokNewSize
)) {
440 Print(L
"Enroll the key(s) or list the key(s) again? (y/n/l): ");
442 key
= get_keystroke();
443 Print(L
"%c\n", key
.UnicodeChar
);
445 if (key
.UnicodeChar
== 'Y' || key
.UnicodeChar
== 'y') {
448 } while (key
.UnicodeChar
== 'L' || key
.UnicodeChar
== 'l');
455 static UINT8
mok_deletion_prompt () {
458 Print(L
"Erase all stored keys? (y/N): ");
460 key
= get_keystroke();
461 Print(L
"%c\n", key
.UnicodeChar
);
463 if (key
.UnicodeChar
== 'Y' || key
.UnicodeChar
== 'y') {
472 static UINT8
get_password (UINT32
*length
, CHAR16
*password
)
475 CHAR16 input
[PASSWORD_MAX
];
479 key
= get_keystroke();
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
) {
490 if (count
== 0 && key
.UnicodeChar
== CHAR_BACKSPACE
) {
492 } else if (key
.UnicodeChar
== CHAR_BACKSPACE
) {
493 Print(L
"%c", CHAR_BACKSPACE
);
494 input
[--count
] = '\0';
498 input
[count
++] = key
.UnicodeChar
;
499 } while (key
.UnicodeChar
!= CHAR_CARRIAGE_RETURN
);
503 CopyMem(password
, input
, count
* sizeof(CHAR16
));
508 static EFI_STATUS
compute_pw_hash (void *MokNew
, UINTN MokNewSize
, CHAR16
*password
,
509 UINT32 pw_length
, UINT8
*hash
)
512 unsigned int ctxsize
;
515 ctxsize
= Sha256GetContextSize();
516 ctx
= AllocatePool(ctxsize
);
519 Print(L
"Unable to allocate memory for hash context\n");
520 return EFI_OUT_OF_RESOURCES
;
523 if (!Sha256Init(ctx
)) {
524 Print(L
"Unable to initialise hash\n");
525 status
= EFI_OUT_OF_RESOURCES
;
529 if (!(Sha256Update(ctx
, MokNew
, MokNewSize
))) {
530 Print(L
"Unable to generate hash\n");
531 status
= EFI_OUT_OF_RESOURCES
;
535 if (!(Sha256Update(ctx
, password
, pw_length
* sizeof(CHAR16
)))) {
536 Print(L
"Unable to generate hash\n");
537 status
= EFI_OUT_OF_RESOURCES
;
541 if (!(Sha256Final(ctx
, hash
))) {
542 Print(L
"Unable to finalise hash\n");
543 status
= EFI_OUT_OF_RESOURCES
;
547 status
= EFI_SUCCESS
;
552 static UINT8
compare_hash (UINT8
*hash1
, UINT8
*hash2
, UINT32 size
)
556 for (i
= 0; i
< size
; i
++) {
557 if (hash1
[i
] != hash2
[i
]) {
565 static EFI_STATUS
store_keys (void *MokNew
, UINTN MokNewSize
)
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
];
573 CHAR16 password
[PASSWORD_MAX
];
575 UINT8 fail_count
= 0;
577 auth_size
= SHA256_DIGEST_SIZE
;
578 efi_status
= uefi_call_wrapper(RT
->GetVariable
, 5, L
"MokAuth",
580 &attributes
, &auth_size
, auth
);
583 if (efi_status
!= EFI_SUCCESS
|| auth_size
!= SHA256_DIGEST_SIZE
) {
584 Print(L
"Failed to get MokAuth %d\n", efi_status
);
588 while (fail_count
< 3) {
589 Print(L
"Password(%d-%d characters): ",
590 PASSWORD_MIN
, PASSWORD_MAX
);
591 get_password(&pw_length
, password
);
594 Print(L
"At least %d characters for the password\n",
598 efi_status
= compute_pw_hash(MokNew
, MokNewSize
, password
,
601 if (efi_status
!= EFI_SUCCESS
) {
605 if (!compare_hash(auth
, hash
, SHA256_DIGEST_SIZE
)) {
606 Print(L
"Password doesn't match\n");
614 return EFI_ACCESS_DENIED
;
617 efi_status
= uefi_call_wrapper(RT
->SetVariable
, 5, L
"MokList",
619 EFI_VARIABLE_NON_VOLATILE
620 | EFI_VARIABLE_BOOTSERVICE_ACCESS
,
622 if (efi_status
!= EFI_SUCCESS
) {
623 Print(L
"Failed to set variable %d\n", efi_status
);
630 static EFI_STATUS
check_mok_request(EFI_HANDLE image_handle
)
632 EFI_GUID shim_lock_guid
= SHIM_LOCK_GUID
;
633 EFI_STATUS efi_status
;
634 UINTN MokNewSize
= 0;
636 UINT32 attributes
, MokNum
;
639 efi_status
= get_variable(L
"MokNew", shim_lock_guid
, &attributes
,
640 &MokNewSize
, &MokNew
);
642 if (efi_status
!= EFI_SUCCESS
|| MokNewSize
< sizeof(UINT32
)) {
646 CopyMem(&MokNum
, MokNew
, sizeof(UINT32
));
648 confirmed
= mok_deletion_prompt();
653 efi_status
= store_keys(MokNew
, sizeof(UINT32
));
655 if (efi_status
!= EFI_SUCCESS
) {
656 Print(L
"Failed to erase keys\n");
660 confirmed
= mok_enrollment_prompt(MokNew
, MokNewSize
);
665 efi_status
= store_keys(MokNew
, MokNewSize
);
667 if (efi_status
!= EFI_SUCCESS
) {
668 Print(L
"Failed to enroll MOK\n");
674 if (delete_variable(L
"MokNew", shim_lock_guid
) != EFI_SUCCESS
) {
675 Print(L
"Failed to delete MokNew\n");
679 delete_variable(L
"MokAuth", shim_lock_guid
);
684 EFI_STATUS
efi_main (EFI_HANDLE image_handle
, EFI_SYSTEM_TABLE
*systab
)
686 EFI_STATUS efi_status
;
688 InitializeLib(image_handle
, systab
);
690 efi_status
= check_mok_request(image_handle
);