mirror of
https://git.proxmox.com/git/efi-boot-shim
synced 2025-05-01 01:31:24 +00:00
2686 lines
63 KiB
C
2686 lines
63 KiB
C
// SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
#include "shim.h"
|
|
|
|
#include <Library/BaseCryptLib.h>
|
|
#include <openssl/x509.h>
|
|
#include <openssl/x509v3.h>
|
|
#include <openssl/asn1.h>
|
|
#include <openssl/bn.h>
|
|
|
|
#define PASSWORD_MAX 256
|
|
#define PASSWORD_MIN 1
|
|
#define SB_PASSWORD_LEN 16
|
|
|
|
#define NAME_LINE_MAX 70
|
|
|
|
#ifndef SHIM_VENDOR
|
|
#define SHIM_VENDOR L"Shim"
|
|
#endif
|
|
|
|
#define CERT_STRING L"Select an X509 certificate to enroll:\n\n"
|
|
#define HASH_STRING L"Select a file to trust:\n\n"
|
|
|
|
#define CompareMemberGuid(x, y) CompareMem(x, y, sizeof(EFI_GUID))
|
|
|
|
typedef struct {
|
|
UINT32 MokSize;
|
|
UINT8 *Mok;
|
|
EFI_GUID Type;
|
|
} __attribute__ ((packed)) MokListNode;
|
|
|
|
typedef struct {
|
|
UINT32 MokSBState;
|
|
UINT32 PWLen;
|
|
CHAR16 Password[SB_PASSWORD_LEN];
|
|
} __attribute__ ((packed)) MokSBvar;
|
|
|
|
typedef struct {
|
|
UINT32 MokDBState;
|
|
UINT32 PWLen;
|
|
CHAR16 Password[SB_PASSWORD_LEN];
|
|
} __attribute__ ((packed)) MokDBvar;
|
|
|
|
typedef struct {
|
|
UINT32 MokTMLState;
|
|
UINT32 PWLen;
|
|
CHAR16 Password[SB_PASSWORD_LEN];
|
|
} __attribute__ ((packed)) MokTMLvar;
|
|
|
|
typedef struct {
|
|
INT32 Timeout;
|
|
} __attribute__ ((packed)) MokTimeoutvar;
|
|
|
|
static EFI_STATUS get_sha1sum(void *Data, int DataSize, UINT8 * hash)
|
|
{
|
|
EFI_STATUS efi_status;
|
|
unsigned int ctxsize;
|
|
void *ctx = NULL;
|
|
|
|
ctxsize = Sha1GetContextSize();
|
|
ctx = AllocatePool(ctxsize);
|
|
|
|
if (!ctx) {
|
|
console_notify(L"Unable to allocate memory for hash context");
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
if (!Sha1Init(ctx)) {
|
|
console_notify(L"Unable to initialise hash");
|
|
efi_status = EFI_OUT_OF_RESOURCES;
|
|
goto done;
|
|
}
|
|
|
|
if (!(Sha1Update(ctx, Data, DataSize))) {
|
|
console_notify(L"Unable to generate hash");
|
|
efi_status = EFI_OUT_OF_RESOURCES;
|
|
goto done;
|
|
}
|
|
|
|
if (!(Sha1Final(ctx, hash))) {
|
|
console_notify(L"Unable to finalise hash");
|
|
efi_status = EFI_OUT_OF_RESOURCES;
|
|
goto done;
|
|
}
|
|
|
|
efi_status = EFI_SUCCESS;
|
|
done:
|
|
return efi_status;
|
|
}
|
|
|
|
static BOOLEAN is_sha2_hash(EFI_GUID Type)
|
|
{
|
|
if (CompareGuid(&Type, &EFI_CERT_SHA224_GUID) == 0)
|
|
return TRUE;
|
|
else if (CompareGuid(&Type, &EFI_CERT_SHA256_GUID) == 0)
|
|
return TRUE;
|
|
else if (CompareGuid(&Type, &EFI_CERT_SHA384_GUID) == 0)
|
|
return TRUE;
|
|
else if (CompareGuid(&Type, &EFI_CERT_SHA512_GUID) == 0)
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static UINT32 sha_size(EFI_GUID Type)
|
|
{
|
|
if (CompareGuid(&Type, &EFI_CERT_SHA1_GUID) == 0)
|
|
return SHA1_DIGEST_SIZE;
|
|
else if (CompareGuid(&Type, &EFI_CERT_SHA224_GUID) == 0)
|
|
return SHA224_DIGEST_LENGTH;
|
|
else if (CompareGuid(&Type, &EFI_CERT_SHA256_GUID) == 0)
|
|
return SHA256_DIGEST_SIZE;
|
|
else if (CompareGuid(&Type, &EFI_CERT_SHA384_GUID) == 0)
|
|
return SHA384_DIGEST_LENGTH;
|
|
else if (CompareGuid(&Type, &EFI_CERT_SHA512_GUID) == 0)
|
|
return SHA512_DIGEST_LENGTH;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static BOOLEAN is_valid_siglist(EFI_GUID Type, UINT32 SigSize)
|
|
{
|
|
UINT32 hash_sig_size;
|
|
|
|
if (CompareGuid (&Type, &X509_GUID) == 0 && SigSize != 0)
|
|
return TRUE;
|
|
|
|
if (!is_sha2_hash(Type))
|
|
return FALSE;
|
|
|
|
hash_sig_size = sha_size(Type) + sizeof(EFI_GUID);
|
|
if (SigSize != hash_sig_size)
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static UINT32 count_keys(void *Data, UINTN DataSize)
|
|
{
|
|
EFI_SIGNATURE_LIST *CertList = Data;
|
|
UINTN dbsize = DataSize;
|
|
UINT32 MokNum = 0;
|
|
void *end = Data + DataSize;
|
|
|
|
while ((dbsize > 0) && (dbsize >= CertList->SignatureListSize)) {
|
|
/* Use ptr arithmetics to ensure bounded access. */
|
|
if ((void *)(CertList + 1) > end) {
|
|
console_notify
|
|
(L"Invalid MOK detected! Ignoring MOK List.");
|
|
return 0;
|
|
}
|
|
|
|
if (CertList->SignatureListSize <= CertList->SignatureSize) {
|
|
console_errorbox(L"Corrupted signature list");
|
|
return 0;
|
|
}
|
|
|
|
if (!is_valid_siglist
|
|
(CertList->SignatureType, CertList->SignatureSize)) {
|
|
console_errorbox(L"Invalid signature list found");
|
|
return 0;
|
|
}
|
|
|
|
MokNum++;
|
|
dbsize -= CertList->SignatureListSize;
|
|
CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList +
|
|
CertList->SignatureListSize);
|
|
}
|
|
|
|
return MokNum;
|
|
}
|
|
|
|
static MokListNode *build_mok_list(UINT32 num, void *Data, UINTN DataSize)
|
|
{
|
|
MokListNode *list;
|
|
EFI_SIGNATURE_LIST *CertList = Data;
|
|
EFI_SIGNATURE_DATA *Cert;
|
|
UINTN dbsize = DataSize;
|
|
UINTN count = 0;
|
|
void *end = Data + DataSize;
|
|
|
|
list = AllocatePool(sizeof(MokListNode) * num);
|
|
if (!list) {
|
|
console_notify(L"Unable to allocate MOK list");
|
|
return NULL;
|
|
}
|
|
|
|
while ((dbsize > 0) && (dbsize >= CertList->SignatureListSize)) {
|
|
/* CertList out of bounds? */
|
|
if ((void *)(CertList + 1) > end
|
|
|| CertList->SignatureListSize == 0) {
|
|
FreePool(list);
|
|
return NULL;
|
|
}
|
|
|
|
/* Omit the signature check here since we already did it
|
|
in count_keys() */
|
|
|
|
Cert = (EFI_SIGNATURE_DATA *) (((UINT8 *) CertList) +
|
|
sizeof(EFI_SIGNATURE_LIST) +
|
|
CertList->SignatureHeaderSize);
|
|
/* Cert out of bounds? */
|
|
if ((void *)(Cert + 1) > end
|
|
|| CertList->SignatureSize <= sizeof(EFI_GUID)) {
|
|
FreePool(list);
|
|
return NULL;
|
|
}
|
|
|
|
list[count].Type = CertList->SignatureType;
|
|
if (CompareGuid (&CertList->SignatureType, &X509_GUID) == 0) {
|
|
list[count].MokSize = CertList->SignatureSize -
|
|
sizeof(EFI_GUID);
|
|
list[count].Mok = (void *)Cert->SignatureData;
|
|
} else {
|
|
list[count].MokSize = CertList->SignatureListSize -
|
|
sizeof(EFI_SIGNATURE_LIST);
|
|
list[count].Mok = (void *)Cert;
|
|
}
|
|
|
|
/* MOK out of bounds? */
|
|
if (list[count].MokSize > (unsigned long)end -
|
|
(unsigned long)list[count].Mok) {
|
|
FreePool(list);
|
|
return NULL;
|
|
}
|
|
|
|
count++;
|
|
dbsize -= CertList->SignatureListSize;
|
|
CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList +
|
|
CertList->SignatureListSize);
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
typedef struct {
|
|
int nid;
|
|
CHAR16 *name;
|
|
} NidName;
|
|
|
|
static NidName nidname[] = {
|
|
{NID_commonName, L"CN"},
|
|
{NID_organizationName, L"O"},
|
|
{NID_countryName, L"C"},
|
|
{NID_stateOrProvinceName, L"ST"},
|
|
{NID_localityName, L"L"},
|
|
{-1, NULL}
|
|
};
|
|
|
|
static CHAR16 *get_x509_name(X509_NAME * X509Name)
|
|
{
|
|
CHAR16 name[NAME_LINE_MAX + 1];
|
|
CHAR16 part[NAME_LINE_MAX + 1];
|
|
char str[NAME_LINE_MAX];
|
|
int i, len, rest, first;
|
|
|
|
name[0] = '\0';
|
|
rest = NAME_LINE_MAX;
|
|
first = 1;
|
|
for (i = 0; nidname[i].name != NULL; i++) {
|
|
int add;
|
|
len = X509_NAME_get_text_by_NID(X509Name, nidname[i].nid,
|
|
str, NAME_LINE_MAX);
|
|
if (len <= 0)
|
|
continue;
|
|
|
|
if (first)
|
|
add = len + (int)StrLen(nidname[i].name) + 1;
|
|
else
|
|
add = len + (int)StrLen(nidname[i].name) + 3;
|
|
|
|
if (add > rest)
|
|
continue;
|
|
|
|
if (first) {
|
|
SPrint(part, NAME_LINE_MAX * sizeof(CHAR16), L"%s=%a",
|
|
nidname[i].name, str);
|
|
} else {
|
|
SPrint(part, NAME_LINE_MAX * sizeof(CHAR16), L", %s=%a",
|
|
nidname[i].name, str);
|
|
}
|
|
StrCat(name, part);
|
|
rest -= add;
|
|
first = 0;
|
|
}
|
|
|
|
if (rest >= 0 && rest < NAME_LINE_MAX)
|
|
return PoolPrint(L"%s", name);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static CHAR16 *get_x509_time(ASN1_TIME * time)
|
|
{
|
|
BIO *bio = BIO_new(BIO_s_mem());
|
|
char str[30];
|
|
int len;
|
|
|
|
ASN1_TIME_print(bio, time);
|
|
len = BIO_read(bio, str, 29);
|
|
if (len < 0)
|
|
len = 0;
|
|
str[len] = '\0';
|
|
BIO_free(bio);
|
|
|
|
return PoolPrint(L"%a", str);
|
|
}
|
|
|
|
static void show_x509_info(X509 * X509Cert, UINT8 * hash)
|
|
{
|
|
ASN1_INTEGER *serial;
|
|
BIGNUM *bnser;
|
|
unsigned char hexbuf[30];
|
|
X509_NAME *X509Name;
|
|
ASN1_TIME *time;
|
|
CHAR16 *issuer = NULL;
|
|
CHAR16 *subject = NULL;
|
|
CHAR16 *from = NULL;
|
|
CHAR16 *until = NULL;
|
|
EXTENDED_KEY_USAGE *extusage;
|
|
POOL_PRINT hash_string1;
|
|
POOL_PRINT hash_string2;
|
|
POOL_PRINT serial_string;
|
|
int fields = 0;
|
|
CHAR16 **text;
|
|
int i = 0;
|
|
|
|
ZeroMem(&hash_string1, sizeof(hash_string1));
|
|
ZeroMem(&hash_string2, sizeof(hash_string2));
|
|
ZeroMem(&serial_string, sizeof(serial_string));
|
|
|
|
serial = X509_get_serialNumber(X509Cert);
|
|
if (serial) {
|
|
int i, n;
|
|
bnser = ASN1_INTEGER_to_BN(serial, NULL);
|
|
n = BN_bn2bin(bnser, hexbuf);
|
|
for (i = 0; i < n; i++) {
|
|
CatPrint(&serial_string, L"%02x:", hexbuf[i]);
|
|
}
|
|
BN_free(bnser);
|
|
}
|
|
|
|
if (serial_string.str)
|
|
fields++;
|
|
|
|
X509Name = X509_get_issuer_name(X509Cert);
|
|
if (X509Name) {
|
|
issuer = get_x509_name(X509Name);
|
|
if (issuer)
|
|
fields++;
|
|
}
|
|
|
|
X509Name = X509_get_subject_name(X509Cert);
|
|
if (X509Name) {
|
|
subject = get_x509_name(X509Name);
|
|
if (subject)
|
|
fields++;
|
|
}
|
|
|
|
time = X509_get_notBefore(X509Cert);
|
|
if (time) {
|
|
from = get_x509_time(time);
|
|
if (from)
|
|
fields++;
|
|
}
|
|
|
|
time = X509_get_notAfter(X509Cert);
|
|
if (time) {
|
|
until = get_x509_time(time);
|
|
if (until)
|
|
fields++;
|
|
}
|
|
|
|
for (i = 0; i < 10; i++)
|
|
CatPrint(&hash_string1, L"%02x ", hash[i]);
|
|
for (i = 10; i < 20; i++)
|
|
CatPrint(&hash_string2, L"%02x ", hash[i]);
|
|
|
|
if (hash_string1.str)
|
|
fields++;
|
|
|
|
if (hash_string2.str)
|
|
fields++;
|
|
|
|
if (!fields)
|
|
return;
|
|
|
|
i = 0;
|
|
|
|
extusage = X509_get_ext_d2i(X509Cert, NID_ext_key_usage, NULL, NULL);
|
|
text = AllocateZeroPool(sizeof(CHAR16 *) *
|
|
(fields * 3 +
|
|
sk_ASN1_OBJECT_num(extusage) + 3));
|
|
if (extusage) {
|
|
int j = 0;
|
|
|
|
text[i++] = StrDuplicate(L"[Extended Key Usage]");
|
|
|
|
for (j = 0; j < sk_ASN1_OBJECT_num(extusage); j++) {
|
|
POOL_PRINT extkeyusage;
|
|
ASN1_OBJECT *obj = sk_ASN1_OBJECT_value(extusage, j);
|
|
int buflen = 80;
|
|
char buf[buflen];
|
|
|
|
ZeroMem(&extkeyusage, sizeof(extkeyusage));
|
|
|
|
OBJ_obj2txt(buf, buflen, obj, 0);
|
|
CatPrint(&extkeyusage, L"OID: %a", buf);
|
|
text[i++] = StrDuplicate(extkeyusage.str);
|
|
FreePool(extkeyusage.str);
|
|
}
|
|
text[i++] = StrDuplicate(L"");
|
|
EXTENDED_KEY_USAGE_free(extusage);
|
|
}
|
|
|
|
if (serial_string.str) {
|
|
text[i++] = StrDuplicate(L"[Serial Number]");
|
|
text[i++] = serial_string.str;
|
|
text[i++] = StrDuplicate(L"");
|
|
}
|
|
if (issuer) {
|
|
text[i++] = StrDuplicate(L"[Issuer]");
|
|
text[i++] = issuer;
|
|
text[i++] = StrDuplicate(L"");
|
|
}
|
|
if (subject) {
|
|
text[i++] = StrDuplicate(L"[Subject]");
|
|
text[i++] = subject;
|
|
text[i++] = StrDuplicate(L"");
|
|
}
|
|
if (from) {
|
|
text[i++] = StrDuplicate(L"[Valid Not Before]");
|
|
text[i++] = from;
|
|
text[i++] = StrDuplicate(L"");
|
|
}
|
|
if (until) {
|
|
text[i++] = StrDuplicate(L"[Valid Not After]");
|
|
text[i++] = until;
|
|
text[i++] = StrDuplicate(L"");
|
|
}
|
|
if (hash_string1.str) {
|
|
text[i++] = StrDuplicate(L"[Fingerprint]");
|
|
text[i++] = hash_string1.str;
|
|
}
|
|
if (hash_string2.str) {
|
|
text[i++] = hash_string2.str;
|
|
text[i++] = StrDuplicate(L"");
|
|
}
|
|
text[i] = NULL;
|
|
|
|
console_print_box(text, -1);
|
|
|
|
for (i = 0; text[i] != NULL; i++)
|
|
FreePool(text[i]);
|
|
|
|
FreePool(text);
|
|
}
|
|
|
|
static void show_sha_digest(EFI_GUID Type, UINT8 * hash)
|
|
{
|
|
CHAR16 *text[5];
|
|
POOL_PRINT hash_string1;
|
|
POOL_PRINT hash_string2;
|
|
int i;
|
|
int length;
|
|
|
|
if (CompareGuid(&Type, &EFI_CERT_SHA1_GUID) == 0) {
|
|
length = SHA1_DIGEST_SIZE;
|
|
text[0] = L"SHA1 hash";
|
|
} else if (CompareGuid(&Type, &EFI_CERT_SHA224_GUID) == 0) {
|
|
length = SHA224_DIGEST_LENGTH;
|
|
text[0] = L"SHA224 hash";
|
|
} else if (CompareGuid(&Type, &EFI_CERT_SHA256_GUID) == 0) {
|
|
length = SHA256_DIGEST_SIZE;
|
|
text[0] = L"SHA256 hash";
|
|
} else if (CompareGuid(&Type, &EFI_CERT_SHA384_GUID) == 0) {
|
|
length = SHA384_DIGEST_LENGTH;
|
|
text[0] = L"SHA384 hash";
|
|
} else if (CompareGuid(&Type, &EFI_CERT_SHA512_GUID) == 0) {
|
|
length = SHA512_DIGEST_LENGTH;
|
|
text[0] = L"SHA512 hash";
|
|
} else {
|
|
return;
|
|
}
|
|
|
|
ZeroMem(&hash_string1, sizeof(hash_string1));
|
|
ZeroMem(&hash_string2, sizeof(hash_string2));
|
|
|
|
text[1] = L"";
|
|
|
|
for (i = 0; i < length / 2; i++)
|
|
CatPrint(&hash_string1, L"%02x ", hash[i]);
|
|
for (i = length / 2; i < length; i++)
|
|
CatPrint(&hash_string2, L"%02x ", hash[i]);
|
|
|
|
text[2] = hash_string1.str;
|
|
text[3] = hash_string2.str;
|
|
text[4] = NULL;
|
|
|
|
console_print_box(text, -1);
|
|
|
|
if (hash_string1.str)
|
|
FreePool(hash_string1.str);
|
|
|
|
if (hash_string2.str)
|
|
FreePool(hash_string2.str);
|
|
}
|
|
|
|
static void show_efi_hash(EFI_GUID Type, void *Mok, UINTN MokSize)
|
|
{
|
|
UINTN sig_size;
|
|
UINTN hash_num;
|
|
UINT8 *hash;
|
|
CHAR16 **menu_strings;
|
|
CHAR16 *selection[] = { L"[Hash List]", NULL };
|
|
UINTN key_num = 0;
|
|
UINTN i;
|
|
|
|
sig_size = sha_size(Type) + sizeof(EFI_GUID);
|
|
if ((MokSize % sig_size) != 0) {
|
|
console_errorbox(L"Corrupted Hash List");
|
|
return;
|
|
}
|
|
hash_num = MokSize / sig_size;
|
|
|
|
if (hash_num == 1) {
|
|
hash = (UINT8 *) Mok + sizeof(EFI_GUID);
|
|
show_sha_digest(Type, hash);
|
|
return;
|
|
}
|
|
|
|
menu_strings = AllocateZeroPool(sizeof(CHAR16 *) * (hash_num + 2));
|
|
if (!menu_strings) {
|
|
console_errorbox(L"Out of Resources");
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < hash_num; i++) {
|
|
menu_strings[i] = PoolPrint(L"View hash %d", i);
|
|
}
|
|
menu_strings[i] = StrDuplicate(L"Back");
|
|
menu_strings[i + 1] = NULL;
|
|
|
|
while (key_num < hash_num) {
|
|
int rc;
|
|
|
|
key_num = rc = console_select(selection, menu_strings, key_num);
|
|
if (rc < 0 || key_num >= hash_num)
|
|
break;
|
|
|
|
hash = (UINT8 *) Mok + sig_size * key_num + sizeof(EFI_GUID);
|
|
show_sha_digest(Type, hash);
|
|
}
|
|
|
|
for (i = 0; menu_strings[i] != NULL; i++)
|
|
FreePool(menu_strings[i]);
|
|
|
|
FreePool(menu_strings);
|
|
}
|
|
|
|
static void show_mok_info(EFI_GUID Type, void *Mok, UINTN MokSize)
|
|
{
|
|
EFI_STATUS efi_status;
|
|
|
|
if (!Mok || MokSize == 0)
|
|
return;
|
|
|
|
if (CompareGuid (&Type, &X509_GUID) == 0) {
|
|
UINT8 hash[SHA1_DIGEST_SIZE];
|
|
X509 *X509Cert;
|
|
|
|
efi_status = get_sha1sum(Mok, MokSize, hash);
|
|
if (EFI_ERROR(efi_status)) {
|
|
console_notify(L"Failed to compute MOK fingerprint");
|
|
return;
|
|
}
|
|
|
|
if (X509ConstructCertificate(Mok, MokSize,
|
|
(UINT8 **) & X509Cert)
|
|
&& X509Cert != NULL) {
|
|
show_x509_info(X509Cert, hash);
|
|
X509_free(X509Cert);
|
|
} else {
|
|
console_notify(L"Not a valid X509 certificate");
|
|
return;
|
|
}
|
|
} else if (is_sha2_hash(Type)) {
|
|
show_efi_hash(Type, Mok, MokSize);
|
|
}
|
|
}
|
|
|
|
static EFI_STATUS list_keys(void *KeyList, UINTN KeyListSize, CHAR16 * title)
|
|
{
|
|
UINTN MokNum = 0;
|
|
MokListNode *keys = NULL;
|
|
UINT32 key_num = 0;
|
|
CHAR16 **menu_strings;
|
|
CHAR16 *selection[] = { title, NULL };
|
|
unsigned int i;
|
|
|
|
if (KeyListSize < (sizeof(EFI_SIGNATURE_LIST) +
|
|
sizeof(EFI_SIGNATURE_DATA))) {
|
|
console_notify(L"No MOK keys found");
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
MokNum = count_keys(KeyList, KeyListSize);
|
|
if (MokNum == 0) {
|
|
console_errorbox(L"Invalid key list");
|
|
return EFI_ABORTED;
|
|
}
|
|
keys = build_mok_list(MokNum, KeyList, KeyListSize);
|
|
if (!keys) {
|
|
console_errorbox(L"Failed to construct key list");
|
|
return EFI_ABORTED;
|
|
}
|
|
|
|
menu_strings = AllocateZeroPool(sizeof(CHAR16 *) * (MokNum + 2));
|
|
if (!menu_strings)
|
|
return EFI_OUT_OF_RESOURCES;
|
|
|
|
for (i = 0; i < MokNum; i++) {
|
|
menu_strings[i] = PoolPrint(L"View key %d", i);
|
|
}
|
|
menu_strings[i] = StrDuplicate(L"Continue");
|
|
|
|
menu_strings[i + 1] = NULL;
|
|
|
|
while (key_num < MokNum) {
|
|
int rc;
|
|
rc = key_num = console_select(selection, menu_strings, key_num);
|
|
|
|
if (rc < 0 || key_num >= MokNum)
|
|
break;
|
|
|
|
show_mok_info(keys[key_num].Type, keys[key_num].Mok,
|
|
keys[key_num].MokSize);
|
|
}
|
|
|
|
for (i = 0; menu_strings[i] != NULL; i++)
|
|
FreePool(menu_strings[i]);
|
|
FreePool(menu_strings);
|
|
FreePool(keys);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
static EFI_STATUS get_line(UINT32 * length, CHAR16 * line, UINT32 line_max,
|
|
UINT8 show)
|
|
{
|
|
EFI_INPUT_KEY key;
|
|
EFI_STATUS efi_status;
|
|
unsigned int count = 0;
|
|
|
|
do {
|
|
efi_status = console_get_keystroke(&key);
|
|
if (EFI_ERROR(efi_status)) {
|
|
console_error(L"Failed to read the keystroke",
|
|
efi_status);
|
|
*length = 0;
|
|
return efi_status;
|
|
}
|
|
|
|
if ((count >= line_max &&
|
|
key.UnicodeChar != CHAR_BACKSPACE) ||
|
|
key.UnicodeChar == CHAR_NULL ||
|
|
key.UnicodeChar == CHAR_TAB ||
|
|
key.UnicodeChar == CHAR_LINEFEED ||
|
|
key.UnicodeChar == CHAR_CARRIAGE_RETURN) {
|
|
continue;
|
|
}
|
|
|
|
if (count == 0 && key.UnicodeChar == CHAR_BACKSPACE) {
|
|
continue;
|
|
} else if (key.UnicodeChar == CHAR_BACKSPACE) {
|
|
if (show) {
|
|
console_print(L"\b");
|
|
}
|
|
line[--count] = '\0';
|
|
continue;
|
|
}
|
|
|
|
if (show) {
|
|
console_print(L"%c", key.UnicodeChar);
|
|
}
|
|
|
|
line[count++] = key.UnicodeChar;
|
|
} while (key.UnicodeChar != CHAR_CARRIAGE_RETURN);
|
|
console_print(L"\n");
|
|
|
|
*length = count;
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
static EFI_STATUS compute_pw_hash(void *Data, UINTN DataSize, UINT8 * password,
|
|
UINT32 pw_length, UINT8 * hash)
|
|
{
|
|
EFI_STATUS efi_status;
|
|
unsigned int ctxsize;
|
|
void *ctx = NULL;
|
|
|
|
ctxsize = Sha256GetContextSize();
|
|
ctx = AllocatePool(ctxsize);
|
|
if (!ctx) {
|
|
console_notify(L"Unable to allocate memory for hash context");
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
if (!Sha256Init(ctx)) {
|
|
console_notify(L"Unable to initialise hash");
|
|
efi_status = EFI_OUT_OF_RESOURCES;
|
|
goto done;
|
|
}
|
|
|
|
if (Data && DataSize) {
|
|
if (!(Sha256Update(ctx, Data, DataSize))) {
|
|
console_notify(L"Unable to generate hash");
|
|
efi_status = EFI_OUT_OF_RESOURCES;
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
if (!(Sha256Update(ctx, password, pw_length))) {
|
|
console_notify(L"Unable to generate hash");
|
|
efi_status = EFI_OUT_OF_RESOURCES;
|
|
goto done;
|
|
}
|
|
|
|
if (!(Sha256Final(ctx, hash))) {
|
|
console_notify(L"Unable to finalise hash");
|
|
efi_status = EFI_OUT_OF_RESOURCES;
|
|
goto done;
|
|
}
|
|
|
|
efi_status = EFI_SUCCESS;
|
|
done:
|
|
return efi_status;
|
|
}
|
|
|
|
static INTN reset_system()
|
|
{
|
|
RT->ResetSystem(EfiResetWarm, EFI_SUCCESS, 0, NULL);
|
|
console_notify(L"Failed to reboot\n");
|
|
return -1;
|
|
}
|
|
|
|
static UINT32 get_password(CHAR16 * prompt, CHAR16 * password, UINT32 max)
|
|
{
|
|
SIMPLE_TEXT_OUTPUT_MODE SavedMode;
|
|
CHAR16 *str;
|
|
CHAR16 *message[2];
|
|
UINTN length;
|
|
UINT32 pw_length;
|
|
|
|
if (!prompt)
|
|
prompt = L"Password:";
|
|
|
|
console_save_and_set_mode(&SavedMode);
|
|
|
|
str = PoolPrint(L"%s ", prompt);
|
|
if (!str) {
|
|
console_errorbox(L"Failed to allocate prompt");
|
|
return 0;
|
|
}
|
|
|
|
message[0] = str;
|
|
message[1] = NULL;
|
|
length = StrLen(message[0]);
|
|
console_print_box_at(message, -1, -length - 4, -5, length + 4, 3, 0, 1);
|
|
get_line(&pw_length, password, max, 0);
|
|
|
|
console_restore_mode(&SavedMode);
|
|
|
|
FreePool(str);
|
|
|
|
return pw_length;
|
|
}
|
|
|
|
static EFI_STATUS match_password(PASSWORD_CRYPT * pw_crypt,
|
|
void *Data, UINTN DataSize,
|
|
UINT8 * auth, CHAR16 * prompt)
|
|
{
|
|
EFI_STATUS efi_status;
|
|
UINT8 hash[128];
|
|
UINT8 *auth_hash;
|
|
UINT32 auth_size;
|
|
CHAR16 password[PASSWORD_MAX];
|
|
UINT32 pw_length;
|
|
UINT8 fail_count = 0;
|
|
unsigned int i;
|
|
|
|
if (pw_crypt) {
|
|
auth_hash = pw_crypt->hash;
|
|
auth_size = get_hash_size(pw_crypt->method);
|
|
if (auth_size == 0)
|
|
return EFI_INVALID_PARAMETER;
|
|
} else if (auth) {
|
|
auth_hash = auth;
|
|
auth_size = SHA256_DIGEST_SIZE;
|
|
} else {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
while (fail_count < 3) {
|
|
pw_length = get_password(prompt, password, PASSWORD_MAX);
|
|
|
|
if (pw_length < PASSWORD_MIN || pw_length > PASSWORD_MAX) {
|
|
console_errorbox(L"Invalid password length");
|
|
fail_count++;
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Compute password hash
|
|
*/
|
|
if (pw_crypt) {
|
|
char pw_ascii[PASSWORD_MAX + 1];
|
|
for (i = 0; i < pw_length; i++)
|
|
pw_ascii[i] = (char)password[i];
|
|
pw_ascii[pw_length] = '\0';
|
|
|
|
efi_status = password_crypt(pw_ascii, pw_length,
|
|
pw_crypt, hash);
|
|
} else {
|
|
/*
|
|
* For backward compatibility
|
|
*/
|
|
efi_status = compute_pw_hash(Data, DataSize,
|
|
(UINT8 *) password,
|
|
pw_length * sizeof(CHAR16),
|
|
hash);
|
|
}
|
|
if (EFI_ERROR(efi_status)) {
|
|
console_errorbox(L"Unable to generate password hash");
|
|
fail_count++;
|
|
continue;
|
|
}
|
|
|
|
if (CompareMem(auth_hash, hash, auth_size) != 0) {
|
|
console_errorbox(L"Password doesn't match");
|
|
fail_count++;
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if (fail_count >= 3)
|
|
return EFI_ACCESS_DENIED;
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
static EFI_STATUS write_db(CHAR16 * db_name, void *MokNew, UINTN MokNewSize)
|
|
{
|
|
EFI_STATUS efi_status;
|
|
UINT32 attributes;
|
|
void *old_data = NULL;
|
|
void *new_data = NULL;
|
|
UINTN old_size;
|
|
UINTN new_size;
|
|
|
|
/* Do not use EFI_VARIABLE_APPEND_WRITE due to faulty firmwares.
|
|
* ref: https://github.com/rhboot/shim/issues/55
|
|
* https://github.com/rhboot/shim/issues/105 */
|
|
|
|
efi_status = get_variable_attr(db_name, (UINT8 **)&old_data, &old_size,
|
|
SHIM_LOCK_GUID, &attributes);
|
|
if (EFI_ERROR(efi_status) && efi_status != EFI_NOT_FOUND) {
|
|
return efi_status;
|
|
}
|
|
|
|
/* Check if the old db is compromised or not */
|
|
if (attributes & EFI_VARIABLE_RUNTIME_ACCESS) {
|
|
FreePool(old_data);
|
|
old_data = NULL;
|
|
old_size = 0;
|
|
}
|
|
|
|
new_size = old_size + MokNewSize;
|
|
new_data = AllocatePool(new_size);
|
|
if (new_data == NULL) {
|
|
efi_status = EFI_OUT_OF_RESOURCES;
|
|
goto out;
|
|
}
|
|
|
|
CopyMem(new_data, old_data, old_size);
|
|
CopyMem(new_data + old_size, MokNew, MokNewSize);
|
|
|
|
efi_status = RT->SetVariable(db_name, &SHIM_LOCK_GUID,
|
|
EFI_VARIABLE_NON_VOLATILE |
|
|
EFI_VARIABLE_BOOTSERVICE_ACCESS,
|
|
new_size, new_data);
|
|
out:
|
|
if (old_size > 0) {
|
|
FreePool(old_data);
|
|
}
|
|
|
|
if (new_data != NULL) {
|
|
FreePool(new_data);
|
|
}
|
|
|
|
return efi_status;
|
|
}
|
|
|
|
static EFI_STATUS store_keys(void *MokNew, UINTN MokNewSize, int authenticate,
|
|
BOOLEAN MokX)
|
|
{
|
|
EFI_STATUS efi_status;
|
|
CHAR16 *db_name;
|
|
CHAR16 *auth_name;
|
|
UINT8 auth[PASSWORD_CRYPT_SIZE];
|
|
UINTN auth_size = PASSWORD_CRYPT_SIZE;
|
|
UINT32 attributes;
|
|
|
|
if (MokX) {
|
|
db_name = L"MokListX";
|
|
auth_name = L"MokXAuth";
|
|
} else {
|
|
db_name = L"MokList";
|
|
auth_name = L"MokAuth";
|
|
}
|
|
|
|
if (authenticate) {
|
|
efi_status = RT->GetVariable(auth_name, &SHIM_LOCK_GUID,
|
|
&attributes, &auth_size, auth);
|
|
if (EFI_ERROR(efi_status) ||
|
|
(auth_size != SHA256_DIGEST_SIZE &&
|
|
auth_size != PASSWORD_CRYPT_SIZE)) {
|
|
if (MokX)
|
|
console_error(L"Failed to get MokXAuth",
|
|
efi_status);
|
|
else
|
|
console_error(L"Failed to get MokAuth",
|
|
efi_status);
|
|
return efi_status;
|
|
}
|
|
|
|
if (auth_size == PASSWORD_CRYPT_SIZE) {
|
|
efi_status = match_password((PASSWORD_CRYPT *) auth,
|
|
NULL, 0, NULL, NULL);
|
|
} else {
|
|
efi_status = match_password(NULL, MokNew, MokNewSize,
|
|
auth, NULL);
|
|
}
|
|
if (EFI_ERROR(efi_status))
|
|
return EFI_ACCESS_DENIED;
|
|
}
|
|
|
|
if (!MokNewSize) {
|
|
/* Delete MOK */
|
|
efi_status = RT->SetVariable(db_name, &SHIM_LOCK_GUID,
|
|
EFI_VARIABLE_NON_VOLATILE |
|
|
EFI_VARIABLE_BOOTSERVICE_ACCESS,
|
|
0, NULL);
|
|
} else {
|
|
/* Write new MOK */
|
|
efi_status = write_db(db_name, MokNew, MokNewSize);
|
|
}
|
|
|
|
if (EFI_ERROR(efi_status)) {
|
|
console_error(L"Failed to set variable", efi_status);
|
|
return efi_status;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
static EFI_STATUS mok_enrollment_prompt(void *MokNew, UINTN MokNewSize,
|
|
int auth, BOOLEAN MokX)
|
|
{
|
|
EFI_STATUS efi_status;
|
|
CHAR16 *enroll_p[] = { L"Enroll the key(s)?", NULL };
|
|
CHAR16 *title;
|
|
|
|
if (MokX)
|
|
title = L"[Enroll MOKX]";
|
|
else
|
|
title = L"[Enroll MOK]";
|
|
|
|
efi_status = list_keys(MokNew, MokNewSize, title);
|
|
if (EFI_ERROR(efi_status))
|
|
return efi_status;
|
|
|
|
if (console_yes_no(enroll_p) == 0)
|
|
return EFI_ABORTED;
|
|
|
|
efi_status = store_keys(MokNew, MokNewSize, auth, MokX);
|
|
if (EFI_ERROR(efi_status)) {
|
|
console_notify(L"Failed to enroll keys\n");
|
|
return efi_status;
|
|
}
|
|
|
|
if (auth) {
|
|
if (MokX) {
|
|
LibDeleteVariable(L"MokXNew", &SHIM_LOCK_GUID);
|
|
LibDeleteVariable(L"MokXAuth", &SHIM_LOCK_GUID);
|
|
} else {
|
|
LibDeleteVariable(L"MokNew", &SHIM_LOCK_GUID);
|
|
LibDeleteVariable(L"MokAuth", &SHIM_LOCK_GUID);
|
|
}
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
static EFI_STATUS mok_reset_prompt(BOOLEAN MokX)
|
|
{
|
|
EFI_STATUS efi_status;
|
|
CHAR16 *prompt[] = { NULL, NULL };
|
|
|
|
clear_screen();
|
|
|
|
if (MokX)
|
|
prompt[0] = L"Erase all stored keys in MokListX?";
|
|
else
|
|
prompt[0] = L"Erase all stored keys in MokList?";
|
|
|
|
if (console_yes_no(prompt) == 0)
|
|
return EFI_ABORTED;
|
|
|
|
efi_status = store_keys(NULL, 0, TRUE, MokX);
|
|
if (EFI_ERROR(efi_status)) {
|
|
console_notify(L"Failed to erase keys\n");
|
|
return efi_status;
|
|
}
|
|
|
|
if (MokX) {
|
|
LibDeleteVariable(L"MokXNew", &SHIM_LOCK_GUID);
|
|
LibDeleteVariable(L"MokXAuth", &SHIM_LOCK_GUID);
|
|
LibDeleteVariable(L"MokListX", &SHIM_LOCK_GUID);
|
|
} else {
|
|
LibDeleteVariable(L"MokNew", &SHIM_LOCK_GUID);
|
|
LibDeleteVariable(L"MokAuth", &SHIM_LOCK_GUID);
|
|
LibDeleteVariable(L"MokList", &SHIM_LOCK_GUID);
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
static EFI_STATUS write_back_mok_list(MokListNode * list, INTN key_num,
|
|
BOOLEAN MokX)
|
|
{
|
|
EFI_STATUS efi_status;
|
|
EFI_SIGNATURE_LIST *CertList;
|
|
EFI_SIGNATURE_DATA *CertData;
|
|
EFI_GUID type;
|
|
void *Data = NULL, *ptr;
|
|
INTN DataSize = 0;
|
|
int i;
|
|
CHAR16 *db_name;
|
|
|
|
if (MokX)
|
|
db_name = L"MokListX";
|
|
else
|
|
db_name = L"MokList";
|
|
|
|
dprint(L"Writing back %s (%d entries)\n", db_name, key_num);
|
|
for (i = 0; i < key_num; i++) {
|
|
if (list[i].Mok == NULL)
|
|
continue;
|
|
|
|
DataSize += sizeof(EFI_SIGNATURE_LIST);
|
|
type = list[i].Type; /* avoid -Werror=address-of-packed-member */
|
|
if (CompareGuid(&type, &X509_GUID) == 0)
|
|
DataSize += sizeof(EFI_GUID);
|
|
DataSize += list[i].MokSize;
|
|
}
|
|
if (DataSize == 0) {
|
|
dprint(L"DataSize = 0; deleting variable %s\n", db_name);
|
|
efi_status = RT->SetVariable(db_name, &SHIM_LOCK_GUID,
|
|
EFI_VARIABLE_NON_VOLATILE |
|
|
EFI_VARIABLE_BOOTSERVICE_ACCESS,
|
|
DataSize, Data);
|
|
dprint(L"efi_status:%llu\n", efi_status);
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
Data = AllocatePool(DataSize);
|
|
if (Data == NULL)
|
|
return EFI_OUT_OF_RESOURCES;
|
|
|
|
ptr = Data;
|
|
|
|
for (i = 0; i < key_num; i++) {
|
|
if (list[i].Mok == NULL)
|
|
continue;
|
|
|
|
CertList = (EFI_SIGNATURE_LIST *) ptr;
|
|
CertData = (EFI_SIGNATURE_DATA *) (((uint8_t *) ptr) +
|
|
sizeof(EFI_SIGNATURE_LIST));
|
|
|
|
CertList->SignatureType = list[i].Type;
|
|
CertList->SignatureHeaderSize = 0;
|
|
|
|
if (CompareGuid(&(CertList->SignatureType), &X509_GUID) == 0) {
|
|
CertList->SignatureListSize = list[i].MokSize +
|
|
sizeof(EFI_SIGNATURE_LIST) + sizeof(EFI_GUID);
|
|
CertList->SignatureSize =
|
|
list[i].MokSize + sizeof(EFI_GUID);
|
|
|
|
CertData->SignatureOwner = SHIM_LOCK_GUID;
|
|
CopyMem(CertData->SignatureData, list[i].Mok,
|
|
list[i].MokSize);
|
|
} else {
|
|
CertList->SignatureListSize = list[i].MokSize +
|
|
sizeof(EFI_SIGNATURE_LIST);
|
|
CertList->SignatureSize =
|
|
sha_size(list[i].Type) + sizeof(EFI_GUID);
|
|
|
|
CopyMem(CertData, list[i].Mok, list[i].MokSize);
|
|
}
|
|
ptr = (uint8_t *) ptr + CertList->SignatureListSize;
|
|
}
|
|
|
|
efi_status = RT->SetVariable(db_name, &SHIM_LOCK_GUID,
|
|
EFI_VARIABLE_NON_VOLATILE |
|
|
EFI_VARIABLE_BOOTSERVICE_ACCESS,
|
|
DataSize, Data);
|
|
if (Data)
|
|
FreePool(Data);
|
|
|
|
if (EFI_ERROR(efi_status)) {
|
|
console_error(L"Failed to set variable", efi_status);
|
|
return efi_status;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
static void delete_cert(void *key, UINT32 key_size,
|
|
MokListNode * mok, INTN mok_num)
|
|
{
|
|
EFI_GUID type;
|
|
int i;
|
|
|
|
for (i = 0; i < mok_num; i++) {
|
|
type = mok[i].Type; /* avoid -Werror=address-of-packed-member */
|
|
if (CompareGuid(&type, &X509_GUID) != 0)
|
|
continue;
|
|
|
|
if (mok[i].MokSize == key_size &&
|
|
CompareMem(key, mok[i].Mok, key_size) == 0) {
|
|
/* Remove the key */
|
|
mok[i].Mok = NULL;
|
|
mok[i].MokSize = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int match_hash(UINT8 * hash, UINT32 hash_size, int start,
|
|
void *hash_list, UINT32 list_num)
|
|
{
|
|
UINT8 *ptr;
|
|
UINTN i;
|
|
|
|
ptr = hash_list + sizeof(EFI_GUID);
|
|
for (i = start; i < list_num; i++) {
|
|
if (CompareMem(hash, ptr, hash_size) == 0)
|
|
return i;
|
|
ptr += hash_size + sizeof(EFI_GUID);
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static void mem_move(void *dest, void *src, UINTN size)
|
|
{
|
|
UINT8 *d, *s;
|
|
UINTN i;
|
|
|
|
d = (UINT8 *) dest;
|
|
s = (UINT8 *) src;
|
|
for (i = 0; i < size; i++)
|
|
d[i] = s[i];
|
|
}
|
|
|
|
static void delete_hash_in_list(EFI_GUID Type, UINT8 * hash, UINT32 hash_size,
|
|
MokListNode * mok, INTN mok_num)
|
|
{
|
|
EFI_GUID type;
|
|
UINT32 sig_size;
|
|
UINT32 list_num;
|
|
int i, del_ind;
|
|
void *start, *end;
|
|
UINT32 remain;
|
|
|
|
sig_size = hash_size + sizeof(EFI_GUID);
|
|
|
|
for (i = 0; i < mok_num; i++) {
|
|
type = mok[i].Type; /* avoid -Werror=address-of-packed-member */
|
|
if ((CompareGuid(&type, &Type) != 0) ||
|
|
(mok[i].MokSize < sig_size))
|
|
continue;
|
|
|
|
list_num = mok[i].MokSize / sig_size;
|
|
|
|
del_ind = match_hash(hash, hash_size, 0, mok[i].Mok, list_num);
|
|
while (del_ind >= 0) {
|
|
/* Remove the hash */
|
|
if (sig_size == mok[i].MokSize) {
|
|
mok[i].Mok = NULL;
|
|
mok[i].MokSize = 0;
|
|
break;
|
|
}
|
|
|
|
start = mok[i].Mok + del_ind * sig_size;
|
|
end = start + sig_size;
|
|
remain = mok[i].MokSize - (del_ind + 1) * sig_size;
|
|
|
|
mem_move(start, end, remain);
|
|
mok[i].MokSize -= sig_size;
|
|
list_num--;
|
|
|
|
del_ind = match_hash(hash, hash_size, del_ind,
|
|
mok[i].Mok, list_num);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void delete_hash_list(EFI_GUID Type, void *hash_list, UINT32 list_size,
|
|
MokListNode * mok, INTN mok_num)
|
|
{
|
|
UINT32 hash_size;
|
|
UINT32 hash_num;
|
|
UINT32 sig_size;
|
|
UINT8 *hash;
|
|
UINT32 i;
|
|
|
|
hash_size = sha_size(Type);
|
|
sig_size = hash_size + sizeof(EFI_GUID);
|
|
if (list_size < sig_size)
|
|
return;
|
|
|
|
hash_num = list_size / sig_size;
|
|
|
|
hash = hash_list + sizeof(EFI_GUID);
|
|
|
|
for (i = 0; i < hash_num; i++) {
|
|
delete_hash_in_list(Type, hash, hash_size, mok, mok_num);
|
|
hash += sig_size;
|
|
}
|
|
}
|
|
|
|
static EFI_STATUS delete_keys(void *MokDel, UINTN MokDelSize, BOOLEAN MokX)
|
|
{
|
|
EFI_STATUS efi_status;
|
|
EFI_GUID type;
|
|
CHAR16 *db_name;
|
|
CHAR16 *auth_name;
|
|
CHAR16 *err_strs[] = { NULL, NULL, NULL };
|
|
UINT8 auth[PASSWORD_CRYPT_SIZE];
|
|
UINTN auth_size = PASSWORD_CRYPT_SIZE;
|
|
UINT32 attributes;
|
|
UINT8 *MokListData = NULL;
|
|
UINTN MokListDataSize = 0;
|
|
MokListNode *mok = NULL, *del_key = NULL;
|
|
INTN mok_num, del_num;
|
|
int i;
|
|
|
|
if (MokX) {
|
|
db_name = L"MokListX";
|
|
auth_name = L"MokXDelAuth";
|
|
} else {
|
|
db_name = L"MokList";
|
|
auth_name = L"MokDelAuth";
|
|
}
|
|
|
|
efi_status = RT->GetVariable(auth_name, &SHIM_LOCK_GUID, &attributes,
|
|
&auth_size, auth);
|
|
if (EFI_ERROR(efi_status) ||
|
|
(auth_size != SHA256_DIGEST_SIZE
|
|
&& auth_size != PASSWORD_CRYPT_SIZE)) {
|
|
if (MokX)
|
|
console_error(L"Failed to get MokXDelAuth", efi_status);
|
|
else
|
|
console_error(L"Failed to get MokDelAuth", efi_status);
|
|
return efi_status;
|
|
}
|
|
|
|
if (auth_size == PASSWORD_CRYPT_SIZE) {
|
|
dprint(L"matching password with CRYPT");
|
|
efi_status = match_password((PASSWORD_CRYPT *) auth, NULL, 0,
|
|
NULL, NULL);
|
|
dprint(L"match_password(0x%llx, NULL, 0, NULL, NULL) = %lu\n", auth, efi_status);
|
|
} else {
|
|
dprint(L"matching password as sha256sum");
|
|
efi_status =
|
|
match_password(NULL, MokDel, MokDelSize, auth, NULL);
|
|
dprint(L"match_password(NULL, 0x%llx, %llu, 0x%llx, NULL) = %lu\n", MokDel, MokDelSize, auth, efi_status);
|
|
}
|
|
if (EFI_ERROR(efi_status))
|
|
return EFI_ACCESS_DENIED;
|
|
|
|
efi_status = get_variable_attr(db_name, &MokListData, &MokListDataSize,
|
|
SHIM_LOCK_GUID, &attributes);
|
|
if (EFI_ERROR(efi_status)) {
|
|
if (MokX)
|
|
console_errorbox(L"Failed to retrieve MokListX");
|
|
else
|
|
console_errorbox(L"Failed to retrieve MokList");
|
|
return EFI_ABORTED;
|
|
} else if (attributes & EFI_VARIABLE_RUNTIME_ACCESS) {
|
|
if (MokX) {
|
|
err_strs[0] = L"MokListX is compromised!";
|
|
err_strs[1] = L"Erase all keys in MokListX!";
|
|
} else {
|
|
err_strs[0] = L"MokList is compromised!";
|
|
err_strs[1] = L"Erase all keys in MokList!";
|
|
}
|
|
console_alertbox(err_strs);
|
|
RT->SetVariable(db_name, &SHIM_LOCK_GUID,
|
|
EFI_VARIABLE_NON_VOLATILE |
|
|
EFI_VARIABLE_BOOTSERVICE_ACCESS, 0, NULL);
|
|
efi_status = EFI_ACCESS_DENIED;
|
|
goto error;
|
|
}
|
|
|
|
/* Nothing to do */
|
|
if (!MokListData || MokListDataSize == 0)
|
|
return EFI_SUCCESS;
|
|
|
|
/* Construct lists */
|
|
mok_num = count_keys(MokListData, MokListDataSize);
|
|
if (mok_num == 0) {
|
|
if (MokX) {
|
|
err_strs[0] = L"Failed to construct the key list of MokListX";
|
|
err_strs[1] = L"Reset MokListX!";
|
|
} else {
|
|
err_strs[0] = L"Failed to construct the key list of MokList";
|
|
err_strs[1] = L"Reset MokList!";
|
|
}
|
|
console_alertbox(err_strs);
|
|
RT->SetVariable(db_name, &SHIM_LOCK_GUID,
|
|
EFI_VARIABLE_NON_VOLATILE |
|
|
EFI_VARIABLE_BOOTSERVICE_ACCESS, 0, NULL);
|
|
efi_status = EFI_ABORTED;
|
|
goto error;
|
|
}
|
|
mok = build_mok_list(mok_num, MokListData, MokListDataSize);
|
|
if (!mok) {
|
|
console_errorbox(L"Failed to construct key list");
|
|
efi_status = EFI_ABORTED;
|
|
goto error;
|
|
}
|
|
del_num = count_keys(MokDel, MokDelSize);
|
|
if (del_num == 0) {
|
|
console_errorbox(L"Invalid key delete list");
|
|
efi_status = EFI_ABORTED;
|
|
goto error;
|
|
}
|
|
del_key = build_mok_list(del_num, MokDel, MokDelSize);
|
|
if (!del_key) {
|
|
console_errorbox(L"Failed to construct key list");
|
|
efi_status = EFI_ABORTED;
|
|
goto error;
|
|
}
|
|
|
|
/* Search and destroy */
|
|
dprint(L"deleting certs from %a\n", MokX ? "MokListX" : "MokList");
|
|
for (i = 0; i < del_num; i++) {
|
|
type = del_key[i].Type; /* avoid -Werror=address-of-packed-member */
|
|
if (CompareGuid(&type, &X509_GUID) == 0) {
|
|
dprint(L"deleting key %d (total %d):\n", i, mok_num);
|
|
dhexdumpat(del_key[i].Mok, del_key[i].MokSize, 0);
|
|
delete_cert(del_key[i].Mok, del_key[i].MokSize,
|
|
mok, mok_num);
|
|
} else if (is_sha2_hash(del_key[i].Type)) {
|
|
dprint(L"deleting hash %d (total %d):\n", i, mok_num);
|
|
dhexdumpat(del_key[i].Mok, del_key[i].MokSize, 0);
|
|
delete_hash_list(del_key[i].Type, del_key[i].Mok,
|
|
del_key[i].MokSize, mok, mok_num);
|
|
}
|
|
}
|
|
|
|
efi_status = write_back_mok_list(mok, mok_num, MokX);
|
|
|
|
error:
|
|
if (MokListData)
|
|
FreePool(MokListData);
|
|
if (mok)
|
|
FreePool(mok);
|
|
if (del_key)
|
|
FreePool(del_key);
|
|
|
|
return efi_status;
|
|
}
|
|
|
|
static EFI_STATUS mok_deletion_prompt(void *MokDel, UINTN MokDelSize,
|
|
BOOLEAN MokX)
|
|
{
|
|
EFI_STATUS efi_status;
|
|
CHAR16 *delete_p[] = { L"Delete the key(s)?", NULL };
|
|
CHAR16 *title;
|
|
|
|
if (MokX)
|
|
title = L"[Delete MOKX]";
|
|
else
|
|
title = L"[Delete MOK]";
|
|
|
|
efi_status = list_keys(MokDel, MokDelSize, title);
|
|
if (EFI_ERROR(efi_status))
|
|
return efi_status;
|
|
|
|
if (console_yes_no(delete_p) == 0)
|
|
return EFI_ABORTED;
|
|
|
|
efi_status = delete_keys(MokDel, MokDelSize, MokX);
|
|
if (EFI_ERROR(efi_status)) {
|
|
console_notify(L"Failed to delete keys");
|
|
return efi_status;
|
|
}
|
|
|
|
if (MokX) {
|
|
LibDeleteVariable(L"MokXDel", &SHIM_LOCK_GUID);
|
|
LibDeleteVariable(L"MokXDelAuth", &SHIM_LOCK_GUID);
|
|
} else {
|
|
LibDeleteVariable(L"MokDel", &SHIM_LOCK_GUID);
|
|
LibDeleteVariable(L"MokDelAuth", &SHIM_LOCK_GUID);
|
|
}
|
|
|
|
if (MokDel)
|
|
FreePool(MokDel);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
static CHAR16 get_password_charater(CHAR16 * prompt)
|
|
{
|
|
SIMPLE_TEXT_OUTPUT_MODE SavedMode;
|
|
EFI_STATUS efi_status;
|
|
CHAR16 *message[2];
|
|
CHAR16 character = 0;
|
|
UINTN length;
|
|
UINT32 pw_length;
|
|
|
|
if (!prompt)
|
|
prompt = L"Password charater: ";
|
|
|
|
console_save_and_set_mode(&SavedMode);
|
|
|
|
message[0] = prompt;
|
|
message[1] = NULL;
|
|
length = StrLen(message[0]);
|
|
console_print_box_at(message, -1, -length - 4, -5, length + 4, 3, 0, 1);
|
|
efi_status = get_line(&pw_length, &character, 1, 0);
|
|
if (EFI_ERROR(efi_status))
|
|
character = 0;
|
|
|
|
console_restore_mode(&SavedMode);
|
|
|
|
return character;
|
|
}
|
|
|
|
static EFI_STATUS mok_sb_prompt(void *MokSB, UINTN MokSBSize)
|
|
{
|
|
EFI_STATUS efi_status;
|
|
SIMPLE_TEXT_OUTPUT_MODE SavedMode;
|
|
MokSBvar *var = MokSB;
|
|
CHAR16 *message[4];
|
|
CHAR16 pass1, pass2, pass3;
|
|
CHAR16 *str;
|
|
UINT8 fail_count = 0;
|
|
UINT8 sbval = 1;
|
|
UINT8 pos1, pos2, pos3;
|
|
int ret;
|
|
CHAR16 *disable_sb[] = { L"Disable Secure Boot", NULL };
|
|
CHAR16 *enable_sb[] = { L"Enable Secure Boot", NULL };
|
|
|
|
if (MokSBSize != sizeof(MokSBvar)) {
|
|
console_notify(L"Invalid MokSB variable contents");
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
clear_screen();
|
|
|
|
message[0] = L"Change Secure Boot state";
|
|
message[1] = NULL;
|
|
|
|
console_save_and_set_mode(&SavedMode);
|
|
console_print_box_at(message, -1, 0, 0, -1, -1, 1, 1);
|
|
console_restore_mode(&SavedMode);
|
|
|
|
while (fail_count < 3) {
|
|
RandomBytes(&pos1, sizeof(pos1));
|
|
pos1 = (pos1 % var->PWLen);
|
|
|
|
do {
|
|
RandomBytes(&pos2, sizeof(pos2));
|
|
pos2 = (pos2 % var->PWLen);
|
|
} while (pos2 == pos1);
|
|
|
|
do {
|
|
RandomBytes(&pos3, sizeof(pos3));
|
|
pos3 = (pos3 % var->PWLen);
|
|
} while (pos3 == pos2 || pos3 == pos1);
|
|
|
|
str = PoolPrint(L"Enter password character %d: ", pos1 + 1);
|
|
if (!str) {
|
|
console_errorbox(L"Failed to allocate buffer");
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
pass1 = get_password_charater(str);
|
|
FreePool(str);
|
|
|
|
str = PoolPrint(L"Enter password character %d: ", pos2 + 1);
|
|
if (!str) {
|
|
console_errorbox(L"Failed to allocate buffer");
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
pass2 = get_password_charater(str);
|
|
FreePool(str);
|
|
|
|
str = PoolPrint(L"Enter password character %d: ", pos3 + 1);
|
|
if (!str) {
|
|
console_errorbox(L"Failed to allocate buffer");
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
pass3 = get_password_charater(str);
|
|
FreePool(str);
|
|
|
|
if (pass1 != var->Password[pos1] ||
|
|
pass2 != var->Password[pos2] ||
|
|
pass3 != var->Password[pos3]) {
|
|
console_print(L"Invalid character\n");
|
|
fail_count++;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (fail_count >= 3) {
|
|
console_notify(L"Password limit reached");
|
|
return EFI_ACCESS_DENIED;
|
|
}
|
|
|
|
if (var->MokSBState == 0)
|
|
ret = console_yes_no(disable_sb);
|
|
else
|
|
ret = console_yes_no(enable_sb);
|
|
|
|
if (ret == 0) {
|
|
LibDeleteVariable(L"MokSB", &SHIM_LOCK_GUID);
|
|
return EFI_ABORTED;
|
|
}
|
|
|
|
if (var->MokSBState == 0) {
|
|
efi_status = RT->SetVariable(L"MokSBState", &SHIM_LOCK_GUID,
|
|
EFI_VARIABLE_NON_VOLATILE |
|
|
EFI_VARIABLE_BOOTSERVICE_ACCESS,
|
|
1, &sbval);
|
|
if (EFI_ERROR(efi_status)) {
|
|
console_notify(L"Failed to set Secure Boot state");
|
|
return efi_status;
|
|
}
|
|
} else {
|
|
efi_status = RT->SetVariable(L"MokSBState", &SHIM_LOCK_GUID,
|
|
EFI_VARIABLE_NON_VOLATILE |
|
|
EFI_VARIABLE_BOOTSERVICE_ACCESS,
|
|
0, NULL);
|
|
if (EFI_ERROR(efi_status)) {
|
|
console_notify(L"Failed to delete Secure Boot state");
|
|
return efi_status;
|
|
}
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
static EFI_STATUS mok_db_prompt(void *MokDB, UINTN MokDBSize)
|
|
{
|
|
EFI_STATUS efi_status;
|
|
SIMPLE_TEXT_OUTPUT_MODE SavedMode;
|
|
MokDBvar *var = MokDB;
|
|
CHAR16 *message[4];
|
|
CHAR16 pass1, pass2, pass3;
|
|
CHAR16 *str;
|
|
UINT8 fail_count = 0;
|
|
UINT8 dbval = 1;
|
|
UINT8 pos1, pos2, pos3;
|
|
int ret;
|
|
CHAR16 *ignore_db[] = { L"Ignore DB certs/hashes", NULL };
|
|
CHAR16 *use_db[] = { L"Use DB certs/hashes", NULL };
|
|
|
|
if (MokDBSize != sizeof(MokDBvar)) {
|
|
console_notify(L"Invalid MokDB variable contents");
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
clear_screen();
|
|
|
|
message[0] = L"Change DB state";
|
|
message[1] = NULL;
|
|
|
|
console_save_and_set_mode(&SavedMode);
|
|
console_print_box_at(message, -1, 0, 0, -1, -1, 1, 1);
|
|
console_restore_mode(&SavedMode);
|
|
|
|
while (fail_count < 3) {
|
|
RandomBytes(&pos1, sizeof(pos1));
|
|
pos1 = (pos1 % var->PWLen);
|
|
|
|
do {
|
|
RandomBytes(&pos2, sizeof(pos2));
|
|
pos2 = (pos2 % var->PWLen);
|
|
} while (pos2 == pos1);
|
|
|
|
do {
|
|
RandomBytes(&pos3, sizeof(pos3));
|
|
pos3 = (pos3 % var->PWLen);
|
|
} while (pos3 == pos2 || pos3 == pos1);
|
|
|
|
str = PoolPrint(L"Enter password character %d: ", pos1 + 1);
|
|
if (!str) {
|
|
console_errorbox(L"Failed to allocate buffer");
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
pass1 = get_password_charater(str);
|
|
FreePool(str);
|
|
|
|
str = PoolPrint(L"Enter password character %d: ", pos2 + 1);
|
|
if (!str) {
|
|
console_errorbox(L"Failed to allocate buffer");
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
pass2 = get_password_charater(str);
|
|
FreePool(str);
|
|
|
|
str = PoolPrint(L"Enter password character %d: ", pos3 + 1);
|
|
if (!str) {
|
|
console_errorbox(L"Failed to allocate buffer");
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
pass3 = get_password_charater(str);
|
|
FreePool(str);
|
|
|
|
if (pass1 != var->Password[pos1] ||
|
|
pass2 != var->Password[pos2] ||
|
|
pass3 != var->Password[pos3]) {
|
|
console_print(L"Invalid character\n");
|
|
fail_count++;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (fail_count >= 3) {
|
|
console_notify(L"Password limit reached");
|
|
return EFI_ACCESS_DENIED;
|
|
}
|
|
|
|
if (var->MokDBState == 0)
|
|
ret = console_yes_no(ignore_db);
|
|
else
|
|
ret = console_yes_no(use_db);
|
|
|
|
if (ret == 0) {
|
|
LibDeleteVariable(L"MokDB", &SHIM_LOCK_GUID);
|
|
return EFI_ABORTED;
|
|
}
|
|
|
|
if (var->MokDBState == 0) {
|
|
efi_status = RT->SetVariable(L"MokDBState", &SHIM_LOCK_GUID,
|
|
EFI_VARIABLE_NON_VOLATILE |
|
|
EFI_VARIABLE_BOOTSERVICE_ACCESS,
|
|
1, &dbval);
|
|
if (EFI_ERROR(efi_status)) {
|
|
console_notify(L"Failed to set DB state");
|
|
return efi_status;
|
|
}
|
|
} else {
|
|
efi_status = RT->SetVariable(L"MokDBState", &SHIM_LOCK_GUID,
|
|
EFI_VARIABLE_NON_VOLATILE |
|
|
EFI_VARIABLE_BOOTSERVICE_ACCESS,
|
|
0, NULL);
|
|
if (EFI_ERROR(efi_status)) {
|
|
console_notify(L"Failed to delete DB state");
|
|
return efi_status;
|
|
}
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
static EFI_STATUS mok_tml_prompt(void *MokTML, UINTN MokTMLSize)
|
|
{
|
|
EFI_STATUS efi_status;
|
|
SIMPLE_TEXT_OUTPUT_MODE SavedMode;
|
|
MokTMLvar *var = MokTML;
|
|
CHAR16 *message[4];
|
|
CHAR16 pass1, pass2, pass3;
|
|
CHAR16 *str;
|
|
UINT8 fail_count = 0;
|
|
UINT8 dbval = 1;
|
|
UINT8 pos1, pos2, pos3;
|
|
int ret;
|
|
CHAR16 *untrust_tml[] = { L"Do not trust the MOK list", NULL };
|
|
CHAR16 *trust_tml[] = { L"Trust the MOK list", NULL };
|
|
|
|
if (MokTMLSize != sizeof(MokTMLvar)) {
|
|
console_notify(L"Invalid MokTML variable contents");
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
clear_screen();
|
|
|
|
message[0] = L"Change Trusted MOK List Keyring state";
|
|
message[1] = NULL;
|
|
|
|
console_save_and_set_mode(&SavedMode);
|
|
console_print_box_at(message, -1, 0, 0, -1, -1, 1, 1);
|
|
console_restore_mode(&SavedMode);
|
|
|
|
while (fail_count < 3) {
|
|
RandomBytes(&pos1, sizeof(pos1));
|
|
pos1 = (pos1 % var->PWLen);
|
|
|
|
do {
|
|
RandomBytes(&pos2, sizeof(pos2));
|
|
pos2 = (pos2 % var->PWLen);
|
|
} while (pos2 == pos1);
|
|
|
|
do {
|
|
RandomBytes(&pos3, sizeof(pos3));
|
|
pos3 = (pos3 % var->PWLen);
|
|
} while (pos3 == pos2 || pos3 == pos1);
|
|
|
|
str = PoolPrint(L"Enter password character %d: ", pos1 + 1);
|
|
if (!str) {
|
|
console_errorbox(L"Failed to allocate buffer");
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
pass1 = get_password_charater(str);
|
|
FreePool(str);
|
|
|
|
str = PoolPrint(L"Enter password character %d: ", pos2 + 1);
|
|
if (!str) {
|
|
console_errorbox(L"Failed to allocate buffer");
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
pass2 = get_password_charater(str);
|
|
FreePool(str);
|
|
|
|
str = PoolPrint(L"Enter password character %d: ", pos3 + 1);
|
|
if (!str) {
|
|
console_errorbox(L"Failed to allocate buffer");
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
pass3 = get_password_charater(str);
|
|
FreePool(str);
|
|
|
|
if (pass1 != var->Password[pos1] ||
|
|
pass2 != var->Password[pos2] ||
|
|
pass3 != var->Password[pos3]) {
|
|
console_print(L"Invalid character\n");
|
|
fail_count++;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (fail_count >= 3) {
|
|
console_notify(L"Password limit reached");
|
|
return EFI_ACCESS_DENIED;
|
|
}
|
|
|
|
if (var->MokTMLState == 0)
|
|
ret = console_yes_no(trust_tml);
|
|
else
|
|
ret = console_yes_no(untrust_tml);
|
|
|
|
if (ret == 0) {
|
|
LibDeleteVariable(L"MokListTrustedNew", &SHIM_LOCK_GUID);
|
|
return EFI_ABORTED;
|
|
}
|
|
if (var->MokTMLState == 0) {
|
|
efi_status = RT->SetVariable(L"MokListTrusted", &SHIM_LOCK_GUID,
|
|
EFI_VARIABLE_NON_VOLATILE |
|
|
EFI_VARIABLE_BOOTSERVICE_ACCESS,
|
|
0, NULL);
|
|
if (EFI_ERROR(efi_status)) {
|
|
console_notify(L"Failed to delete MokListTrusted state");
|
|
return efi_status;
|
|
}
|
|
} else {
|
|
efi_status = RT->SetVariable(L"MokListTrusted", &SHIM_LOCK_GUID,
|
|
EFI_VARIABLE_NON_VOLATILE |
|
|
EFI_VARIABLE_BOOTSERVICE_ACCESS,
|
|
1, &dbval);
|
|
if (EFI_ERROR(efi_status)) {
|
|
console_notify(L"Failed to set MokListTrusted state");
|
|
return efi_status;
|
|
}
|
|
}
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
static EFI_STATUS mok_pw_prompt(void *MokPW, UINTN MokPWSize)
|
|
{
|
|
EFI_STATUS efi_status;
|
|
UINT8 hash[PASSWORD_CRYPT_SIZE];
|
|
UINT8 clear = 0;
|
|
CHAR16 *clear_p[] = { L"Clear MOK password?", NULL };
|
|
CHAR16 *set_p[] = { L"Set MOK password?", NULL };
|
|
|
|
if (MokPWSize != SHA256_DIGEST_SIZE && MokPWSize != PASSWORD_CRYPT_SIZE) {
|
|
console_notify(L"Invalid MokPW variable contents");
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
clear_screen();
|
|
|
|
SetMem(hash, PASSWORD_CRYPT_SIZE, 0);
|
|
|
|
if (MokPWSize == PASSWORD_CRYPT_SIZE) {
|
|
if (CompareMem(MokPW, hash, PASSWORD_CRYPT_SIZE) == 0)
|
|
clear = 1;
|
|
} else {
|
|
if (CompareMem(MokPW, hash, SHA256_DIGEST_SIZE) == 0)
|
|
clear = 1;
|
|
}
|
|
|
|
if (clear) {
|
|
if (console_yes_no(clear_p) == 0)
|
|
return EFI_ABORTED;
|
|
|
|
RT->SetVariable(L"MokPWStore", &SHIM_LOCK_GUID,
|
|
EFI_VARIABLE_NON_VOLATILE |
|
|
EFI_VARIABLE_BOOTSERVICE_ACCESS, 0, NULL);
|
|
goto mokpw_done;
|
|
}
|
|
|
|
if (MokPWSize == PASSWORD_CRYPT_SIZE) {
|
|
efi_status = match_password((PASSWORD_CRYPT *) MokPW, NULL, 0,
|
|
NULL, L"Confirm MOK passphrase: ");
|
|
} else {
|
|
efi_status = match_password(NULL, NULL, 0, MokPW,
|
|
L"Confirm MOK passphrase: ");
|
|
}
|
|
|
|
if (EFI_ERROR(efi_status)) {
|
|
console_notify(L"Password limit reached");
|
|
return efi_status;
|
|
}
|
|
|
|
if (console_yes_no(set_p) == 0)
|
|
return EFI_ABORTED;
|
|
|
|
efi_status = RT->SetVariable(L"MokPWStore", &SHIM_LOCK_GUID,
|
|
EFI_VARIABLE_NON_VOLATILE |
|
|
EFI_VARIABLE_BOOTSERVICE_ACCESS,
|
|
MokPWSize, MokPW);
|
|
if (EFI_ERROR(efi_status)) {
|
|
console_notify(L"Failed to set MOK password");
|
|
return efi_status;
|
|
}
|
|
|
|
mokpw_done:
|
|
LibDeleteVariable(L"MokPW", &SHIM_LOCK_GUID);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
static BOOLEAN verify_certificate(UINT8 * cert, UINTN size)
|
|
{
|
|
X509 *X509Cert;
|
|
UINTN length;
|
|
if (!cert || size < 4)
|
|
return FALSE;
|
|
|
|
/*
|
|
* A DER encoding x509 certificate starts with SEQUENCE(0x30),
|
|
* the number of length bytes, and the number of value bytes.
|
|
* The size of a x509 certificate is usually between 127 bytes
|
|
* and 64KB. For convenience, assume the number of value bytes
|
|
* is 2, i.e. the second byte is 0x82.
|
|
*/
|
|
if (cert[0] != 0x30 || cert[1] != 0x82) {
|
|
console_notify(L"Not a DER encoding X509 certificate");
|
|
return FALSE;
|
|
}
|
|
|
|
length = (cert[2] << 8 | cert[3]);
|
|
if (length != (size - 4)) {
|
|
console_notify(L"Invalid X509 certificate: Inconsistent size");
|
|
return FALSE;
|
|
}
|
|
|
|
if (!(X509ConstructCertificate(cert, size, (UINT8 **) & X509Cert)) ||
|
|
X509Cert == NULL) {
|
|
console_notify(L"Invalid X509 certificate");
|
|
return FALSE;
|
|
}
|
|
|
|
X509_free(X509Cert);
|
|
return TRUE;
|
|
}
|
|
|
|
static EFI_STATUS enroll_file(void *data, UINTN datasize, BOOLEAN hash)
|
|
{
|
|
EFI_STATUS efi_status = EFI_SUCCESS;
|
|
EFI_SIGNATURE_LIST *CertList;
|
|
EFI_SIGNATURE_DATA *CertData;
|
|
UINTN mokbuffersize;
|
|
void *mokbuffer = NULL;
|
|
|
|
if (hash) {
|
|
UINT8 sha256[SHA256_DIGEST_SIZE];
|
|
UINT8 sha1[SHA1_DIGEST_SIZE];
|
|
SHIM_LOCK *shim_lock;
|
|
PE_COFF_LOADER_IMAGE_CONTEXT context;
|
|
|
|
efi_status = LibLocateProtocol(&SHIM_LOCK_GUID,
|
|
(VOID **) &shim_lock);
|
|
if (EFI_ERROR(efi_status))
|
|
goto out;
|
|
|
|
mokbuffersize = sizeof(EFI_SIGNATURE_LIST) + sizeof(EFI_GUID) +
|
|
SHA256_DIGEST_SIZE;
|
|
|
|
mokbuffer = AllocatePool(mokbuffersize);
|
|
if (!mokbuffer)
|
|
goto out;
|
|
|
|
efi_status = shim_lock->Context(data, datasize, &context);
|
|
if (EFI_ERROR(efi_status))
|
|
goto out;
|
|
|
|
efi_status = shim_lock->Hash(data, datasize, &context, sha256,
|
|
sha1);
|
|
if (EFI_ERROR(efi_status))
|
|
goto out;
|
|
|
|
CertList = mokbuffer;
|
|
CertList->SignatureType = EFI_CERT_SHA256_GUID;
|
|
CertList->SignatureSize = 16 + SHA256_DIGEST_SIZE;
|
|
CertData = (EFI_SIGNATURE_DATA *) (((UINT8 *) mokbuffer) +
|
|
sizeof(EFI_SIGNATURE_LIST));
|
|
CopyMem(CertData->SignatureData, sha256, SHA256_DIGEST_SIZE);
|
|
} else {
|
|
mokbuffersize = datasize + sizeof(EFI_SIGNATURE_LIST) +
|
|
sizeof(EFI_GUID);
|
|
mokbuffer = AllocatePool(mokbuffersize);
|
|
|
|
if (!mokbuffer)
|
|
goto out;
|
|
|
|
CertList = mokbuffer;
|
|
CertList->SignatureType = X509_GUID;
|
|
CertList->SignatureSize = 16 + datasize;
|
|
|
|
memcpy(mokbuffer + sizeof(EFI_SIGNATURE_LIST) + 16, data,
|
|
datasize);
|
|
|
|
CertData = (EFI_SIGNATURE_DATA *) (((UINT8 *) mokbuffer) +
|
|
sizeof(EFI_SIGNATURE_LIST));
|
|
}
|
|
|
|
CertList->SignatureListSize = mokbuffersize;
|
|
CertList->SignatureHeaderSize = 0;
|
|
CertData->SignatureOwner = SHIM_LOCK_GUID;
|
|
|
|
if (!hash) {
|
|
if (!verify_certificate(CertData->SignatureData, datasize))
|
|
goto out;
|
|
}
|
|
|
|
efi_status = mok_enrollment_prompt(mokbuffer, mokbuffersize,
|
|
FALSE, FALSE);
|
|
out:
|
|
if (mokbuffer)
|
|
FreePool(mokbuffer);
|
|
|
|
return efi_status;
|
|
}
|
|
|
|
static EFI_STATUS mok_hash_enroll(void)
|
|
{
|
|
EFI_STATUS efi_status;
|
|
CHAR16 *file_name = NULL;
|
|
EFI_HANDLE im = NULL;
|
|
EFI_FILE *file = NULL;
|
|
UINTN filesize;
|
|
void *data;
|
|
CHAR16 *selections[] = {
|
|
L"Select Binary",
|
|
L"",
|
|
L"The Selected Binary will have its hash Enrolled",
|
|
L"This means it will subsequently Boot with no prompting",
|
|
L"Remember to make sure it is a genuine binary before enrolling its hash",
|
|
NULL
|
|
};
|
|
|
|
simple_file_selector(&im, selections, L"\\", L"", &file_name);
|
|
|
|
if (!file_name)
|
|
return EFI_INVALID_PARAMETER;
|
|
|
|
efi_status = simple_file_open(im, file_name, &file, EFI_FILE_MODE_READ);
|
|
if (EFI_ERROR(efi_status)) {
|
|
console_error(L"Unable to open file", efi_status);
|
|
return efi_status;
|
|
}
|
|
|
|
simple_file_read_all(file, &filesize, &data);
|
|
file->Close(file);
|
|
if (!filesize) {
|
|
console_error(L"Unable to read file", efi_status);
|
|
return EFI_BAD_BUFFER_SIZE;
|
|
}
|
|
|
|
efi_status = enroll_file(data, filesize, TRUE);
|
|
if (EFI_ERROR(efi_status))
|
|
console_error(
|
|
L"Hash failed (did you select a valid EFI binary?)",
|
|
efi_status);
|
|
|
|
FreePool(data);
|
|
|
|
return efi_status;
|
|
}
|
|
|
|
static CHAR16 *der_suffix[] = {
|
|
L".cer",
|
|
L".der",
|
|
L".crt",
|
|
NULL
|
|
};
|
|
|
|
static BOOLEAN check_der_suffix(CHAR16 * file_name)
|
|
{
|
|
CHAR16 suffix[5];
|
|
int i;
|
|
|
|
if (!file_name || StrLen(file_name) <= 4)
|
|
return FALSE;
|
|
|
|
suffix[0] = '\0';
|
|
StrnCat(suffix, file_name + StrLen(file_name) - 4, 4);
|
|
|
|
StrLwr(suffix);
|
|
for (i = 0; der_suffix[i] != NULL; i++) {
|
|
if (StrCmp(suffix, der_suffix[i]) == 0) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static EFI_STATUS mok_key_enroll(void)
|
|
{
|
|
EFI_STATUS efi_status;
|
|
CHAR16 *file_name = NULL;
|
|
EFI_HANDLE im = NULL;
|
|
EFI_FILE *file = NULL;
|
|
UINTN filesize;
|
|
void *data;
|
|
CHAR16 *selections[] = {
|
|
L"Select Key",
|
|
L"",
|
|
L"The selected key will be enrolled into the MOK database",
|
|
L"This means any binaries signed with it will be run without prompting",
|
|
L"Remember to make sure it is a genuine key before Enrolling it",
|
|
NULL
|
|
};
|
|
CHAR16 *alert[] = {
|
|
L"Unsupported Format",
|
|
L"",
|
|
L"Only DER encoded certificate (*.cer/der/crt) is supported",
|
|
NULL
|
|
};
|
|
|
|
simple_file_selector(&im, selections, L"\\", L"", &file_name);
|
|
|
|
if (!file_name)
|
|
return EFI_INVALID_PARAMETER;
|
|
|
|
if (!check_der_suffix(file_name)) {
|
|
console_alertbox(alert);
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
efi_status = simple_file_open(im, file_name, &file, EFI_FILE_MODE_READ);
|
|
if (EFI_ERROR(efi_status)) {
|
|
console_error(L"Unable to open file", efi_status);
|
|
return efi_status;
|
|
}
|
|
|
|
simple_file_read_all(file, &filesize, &data);
|
|
file->Close(file);
|
|
if (!filesize) {
|
|
console_error(L"Unable to read file", efi_status);
|
|
return EFI_BAD_BUFFER_SIZE;
|
|
}
|
|
|
|
efi_status = enroll_file(data, filesize, FALSE);
|
|
FreePool(data);
|
|
|
|
return efi_status;
|
|
}
|
|
|
|
static BOOLEAN verify_pw(BOOLEAN * protected)
|
|
{
|
|
EFI_STATUS efi_status;
|
|
SIMPLE_TEXT_OUTPUT_MODE SavedMode;
|
|
UINT8 pwhash[PASSWORD_CRYPT_SIZE];
|
|
UINTN size = PASSWORD_CRYPT_SIZE;
|
|
UINT32 attributes;
|
|
CHAR16 *message[2];
|
|
|
|
*protected = FALSE;
|
|
|
|
efi_status = RT->GetVariable(L"MokPWStore", &SHIM_LOCK_GUID, &attributes,
|
|
&size, pwhash);
|
|
/*
|
|
* If anything can attack the password it could just set it to a
|
|
* known value, so there's no safety advantage in failing to validate
|
|
* purely because of a failure to read the variable
|
|
*/
|
|
if (EFI_ERROR(efi_status) ||
|
|
(size != SHA256_DIGEST_SIZE && size != PASSWORD_CRYPT_SIZE))
|
|
return TRUE;
|
|
|
|
if (attributes & EFI_VARIABLE_RUNTIME_ACCESS)
|
|
return TRUE;
|
|
|
|
clear_screen();
|
|
|
|
/* Draw the background */
|
|
console_save_and_set_mode(&SavedMode);
|
|
message[0] = PoolPrint(L"%s UEFI key management", SHIM_VENDOR);
|
|
message[1] = NULL;
|
|
console_print_box_at(message, -1, 0, 0, -1, -1, 1, 1);
|
|
FreePool(message[0]);
|
|
console_restore_mode(&SavedMode);
|
|
|
|
if (size == PASSWORD_CRYPT_SIZE) {
|
|
efi_status = match_password((PASSWORD_CRYPT *) pwhash, NULL, 0,
|
|
NULL, L"Enter MOK password:");
|
|
} else {
|
|
efi_status = match_password(NULL, NULL, 0, pwhash,
|
|
L"Enter MOK password:");
|
|
}
|
|
if (EFI_ERROR(efi_status)) {
|
|
console_notify(L"Password limit reached");
|
|
return FALSE;
|
|
}
|
|
|
|
*protected = TRUE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static int draw_countdown()
|
|
{
|
|
CHAR16 *message = L"Press any key to perform MOK management";
|
|
CHAR16 *title;
|
|
void *MokTimeout = NULL;
|
|
MokTimeoutvar *var;
|
|
UINTN MokTimeoutSize = 0;
|
|
int timeout = 10;
|
|
EFI_STATUS efi_status;
|
|
|
|
efi_status = get_variable(L"MokTimeout", (UINT8 **) &MokTimeout,
|
|
&MokTimeoutSize, SHIM_LOCK_GUID);
|
|
if (!EFI_ERROR(efi_status)) {
|
|
var = MokTimeout;
|
|
timeout = (int)var->Timeout;
|
|
FreePool(MokTimeout);
|
|
LibDeleteVariable(L"MokTimeout", &SHIM_LOCK_GUID);
|
|
}
|
|
|
|
if (timeout < 0)
|
|
return timeout;
|
|
|
|
title = PoolPrint(L"%s UEFI key management", SHIM_VENDOR);
|
|
timeout = console_countdown(title, message, timeout);
|
|
|
|
FreePool(title);
|
|
return timeout;
|
|
}
|
|
|
|
typedef enum {
|
|
MOK_BOOT,
|
|
MOK_RESET_MOK,
|
|
MOK_RESET_MOKX,
|
|
MOK_ENROLL_MOK,
|
|
MOK_ENROLL_MOKX,
|
|
MOK_DELETE_MOK,
|
|
MOK_DELETE_MOKX,
|
|
MOK_CHANGE_SB,
|
|
MOK_SET_PW,
|
|
MOK_CHANGE_DB,
|
|
MOK_KEY_ENROLL,
|
|
MOK_HASH_ENROLL,
|
|
MOK_CHANGE_TML
|
|
} mok_menu_item;
|
|
|
|
static void free_menu(mok_menu_item * menu_item, CHAR16 ** menu_strings)
|
|
{
|
|
if (menu_strings)
|
|
FreePool(menu_strings);
|
|
|
|
if (menu_item)
|
|
FreePool(menu_item);
|
|
}
|
|
|
|
static EFI_STATUS enter_mok_menu(EFI_HANDLE image_handle UNUSED,
|
|
void *MokNew, UINTN MokNewSize,
|
|
void *MokDel, UINTN MokDelSize,
|
|
void *MokSB, UINTN MokSBSize,
|
|
void *MokPW, UINTN MokPWSize,
|
|
void *MokDB, UINTN MokDBSize,
|
|
void *MokXNew, UINTN MokXNewSize,
|
|
void *MokXDel, UINTN MokXDelSize,
|
|
void *MokTML, UINTN MokTMLSize)
|
|
{
|
|
CHAR16 **menu_strings = NULL;
|
|
mok_menu_item *menu_item = NULL;
|
|
int choice = 0;
|
|
int mok_changed = 0;
|
|
EFI_STATUS efi_status;
|
|
UINT8 auth[PASSWORD_CRYPT_SIZE];
|
|
UINTN auth_size = PASSWORD_CRYPT_SIZE;
|
|
UINT32 attributes;
|
|
BOOLEAN protected;
|
|
CHAR16 *mok_mgmt_p[] = { L"Perform MOK management", NULL };
|
|
EFI_STATUS ret = EFI_SUCCESS;
|
|
|
|
if (verify_pw(&protected) == FALSE)
|
|
return EFI_ACCESS_DENIED;
|
|
|
|
if (protected == FALSE && draw_countdown() == 0)
|
|
goto out;
|
|
|
|
while (choice >= 0) {
|
|
UINTN menucount = 3, i = 0;
|
|
UINT32 MokAuth = 0;
|
|
UINT32 MokDelAuth = 0;
|
|
UINT32 MokXAuth = 0;
|
|
UINT32 MokXDelAuth = 0;
|
|
|
|
efi_status = RT->GetVariable(L"MokAuth", &SHIM_LOCK_GUID,
|
|
&attributes, &auth_size, auth);
|
|
if (!EFI_ERROR(efi_status) &&
|
|
(auth_size == SHA256_DIGEST_SIZE ||
|
|
auth_size == PASSWORD_CRYPT_SIZE))
|
|
MokAuth = 1;
|
|
|
|
efi_status = RT->GetVariable(L"MokDelAuth", &SHIM_LOCK_GUID,
|
|
&attributes, &auth_size, auth);
|
|
if (!EFI_ERROR(efi_status) &&
|
|
(auth_size == SHA256_DIGEST_SIZE ||
|
|
auth_size == PASSWORD_CRYPT_SIZE))
|
|
MokDelAuth = 1;
|
|
|
|
efi_status = RT->GetVariable(L"MokXAuth", &SHIM_LOCK_GUID,
|
|
&attributes, &auth_size, auth);
|
|
if (!EFI_ERROR(efi_status) &&
|
|
(auth_size == SHA256_DIGEST_SIZE ||
|
|
auth_size == PASSWORD_CRYPT_SIZE))
|
|
MokXAuth = 1;
|
|
|
|
efi_status = RT->GetVariable(L"MokXDelAuth", &SHIM_LOCK_GUID,
|
|
&attributes, &auth_size, auth);
|
|
if (!EFI_ERROR(efi_status) &&
|
|
(auth_size == SHA256_DIGEST_SIZE ||
|
|
auth_size == PASSWORD_CRYPT_SIZE))
|
|
MokXDelAuth = 1;
|
|
|
|
if (MokNew || MokAuth)
|
|
menucount++;
|
|
|
|
if (MokDel || MokDelAuth)
|
|
menucount++;
|
|
|
|
if (MokXNew || MokXAuth)
|
|
menucount++;
|
|
|
|
if (MokXDel || MokXDelAuth)
|
|
menucount++;
|
|
|
|
if (MokSB)
|
|
menucount++;
|
|
|
|
if (MokPW)
|
|
menucount++;
|
|
|
|
if (MokDB)
|
|
menucount++;
|
|
|
|
if (MokTML)
|
|
menucount++;
|
|
|
|
menu_strings = AllocateZeroPool(sizeof(CHAR16 *) *
|
|
(menucount + 1));
|
|
if (!menu_strings)
|
|
return EFI_OUT_OF_RESOURCES;
|
|
|
|
menu_item = AllocateZeroPool(sizeof(mok_menu_item) * menucount);
|
|
if (!menu_item) {
|
|
FreePool(menu_strings);
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
if (mok_changed)
|
|
menu_strings[i] = L"Reboot";
|
|
else
|
|
menu_strings[i] = L"Continue boot";
|
|
menu_item[i] = MOK_BOOT;
|
|
|
|
i++;
|
|
|
|
if (MokNew || MokAuth) {
|
|
if (!MokNew) {
|
|
menu_strings[i] = L"Reset MOK";
|
|
menu_item[i] = MOK_RESET_MOK;
|
|
} else {
|
|
menu_strings[i] = L"Enroll MOK";
|
|
menu_item[i] = MOK_ENROLL_MOK;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
if (MokDel || MokDelAuth) {
|
|
menu_strings[i] = L"Delete MOK";
|
|
menu_item[i] = MOK_DELETE_MOK;
|
|
i++;
|
|
}
|
|
|
|
if (MokXNew || MokXAuth) {
|
|
if (!MokXNew) {
|
|
menu_strings[i] = L"Reset MOKX";
|
|
menu_item[i] = MOK_RESET_MOKX;
|
|
} else {
|
|
menu_strings[i] = L"Enroll MOKX";
|
|
menu_item[i] = MOK_ENROLL_MOKX;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
if (MokXDel || MokXDelAuth) {
|
|
menu_strings[i] = L"Delete MOKX";
|
|
menu_item[i] = MOK_DELETE_MOKX;
|
|
i++;
|
|
}
|
|
|
|
if (MokSB) {
|
|
menu_strings[i] = L"Change Secure Boot state";
|
|
menu_item[i] = MOK_CHANGE_SB;
|
|
i++;
|
|
}
|
|
|
|
if (MokPW) {
|
|
menu_strings[i] = L"Set MOK password";
|
|
menu_item[i] = MOK_SET_PW;
|
|
i++;
|
|
}
|
|
|
|
if (MokDB) {
|
|
menu_strings[i] = L"Change DB state";
|
|
menu_item[i] = MOK_CHANGE_DB;
|
|
i++;
|
|
}
|
|
|
|
if (MokTML) {
|
|
menu_strings[i] = L"Change MOK List Trusted State";
|
|
menu_item[i] = MOK_CHANGE_TML;
|
|
i++;
|
|
}
|
|
|
|
menu_strings[i] = L"Enroll key from disk";
|
|
menu_item[i] = MOK_KEY_ENROLL;
|
|
i++;
|
|
|
|
menu_strings[i] = L"Enroll hash from disk";
|
|
menu_item[i] = MOK_HASH_ENROLL;
|
|
i++;
|
|
|
|
menu_strings[i] = NULL;
|
|
|
|
choice = console_select(mok_mgmt_p, menu_strings, 0);
|
|
if (choice < 0)
|
|
goto out;
|
|
|
|
switch (menu_item[choice]) {
|
|
case MOK_BOOT:
|
|
goto out;
|
|
case MOK_RESET_MOK:
|
|
efi_status = mok_reset_prompt(FALSE);
|
|
break;
|
|
case MOK_ENROLL_MOK:
|
|
if (!MokNew) {
|
|
console_print(L"MokManager: internal error: %s",
|
|
L"MokNew was !NULL but is now NULL\n");
|
|
ret = EFI_ABORTED;
|
|
goto out;
|
|
}
|
|
efi_status = mok_enrollment_prompt(MokNew, MokNewSize,
|
|
TRUE, FALSE);
|
|
if (!EFI_ERROR(efi_status))
|
|
MokNew = NULL;
|
|
break;
|
|
case MOK_DELETE_MOK:
|
|
if (!MokDel) {
|
|
console_print(L"MokManager: internal error: %s",
|
|
L"MokDel was !NULL but is now NULL\n");
|
|
ret = EFI_ABORTED;
|
|
goto out;
|
|
}
|
|
efi_status = mok_deletion_prompt(MokDel, MokDelSize,
|
|
FALSE);
|
|
if (!EFI_ERROR(efi_status))
|
|
MokDel = NULL;
|
|
break;
|
|
case MOK_RESET_MOKX:
|
|
efi_status = mok_reset_prompt(TRUE);
|
|
break;
|
|
case MOK_ENROLL_MOKX:
|
|
if (!MokXNew) {
|
|
console_print(L"MokManager: internal error: %s",
|
|
L"MokXNew was !NULL but is now NULL\n");
|
|
ret = EFI_ABORTED;
|
|
goto out;
|
|
}
|
|
efi_status = mok_enrollment_prompt(MokXNew, MokXNewSize,
|
|
TRUE, TRUE);
|
|
if (!EFI_ERROR(efi_status))
|
|
MokXNew = NULL;
|
|
break;
|
|
case MOK_DELETE_MOKX:
|
|
if (!MokXDel) {
|
|
console_print(L"MokManager: internal error: %s",
|
|
L"MokXDel was !NULL but is now NULL\n");
|
|
ret = EFI_ABORTED;
|
|
goto out;
|
|
}
|
|
efi_status = mok_deletion_prompt(MokXDel, MokXDelSize,
|
|
TRUE);
|
|
if (!EFI_ERROR(efi_status))
|
|
MokXDel = NULL;
|
|
break;
|
|
case MOK_CHANGE_SB:
|
|
if (!MokSB) {
|
|
console_print(L"MokManager: internal error: %s",
|
|
L"MokSB was !NULL but is now NULL\n");
|
|
ret = EFI_ABORTED;
|
|
goto out;
|
|
}
|
|
efi_status = mok_sb_prompt(MokSB, MokSBSize);
|
|
if (!EFI_ERROR(efi_status))
|
|
MokSB = NULL;
|
|
break;
|
|
case MOK_SET_PW:
|
|
if (!MokPW) {
|
|
console_print(L"MokManager: internal error: %s",
|
|
L"MokPW was !NULL but is now NULL\n");
|
|
ret = EFI_ABORTED;
|
|
goto out;
|
|
}
|
|
efi_status = mok_pw_prompt(MokPW, MokPWSize);
|
|
if (!EFI_ERROR(efi_status))
|
|
MokPW = NULL;
|
|
break;
|
|
case MOK_CHANGE_DB:
|
|
if (!MokDB) {
|
|
console_print(L"MokManager: internal error: %s",
|
|
L"MokDB was !NULL but is now NULL\n");
|
|
ret = EFI_ABORTED;
|
|
goto out;
|
|
}
|
|
efi_status = mok_db_prompt(MokDB, MokDBSize);
|
|
if (!EFI_ERROR(efi_status))
|
|
MokDB = NULL;
|
|
break;
|
|
case MOK_KEY_ENROLL:
|
|
efi_status = mok_key_enroll();
|
|
break;
|
|
case MOK_HASH_ENROLL:
|
|
efi_status = mok_hash_enroll();
|
|
break;
|
|
case MOK_CHANGE_TML:
|
|
if (!MokTML) {
|
|
console_print(L"MokManager: internal error: %s",
|
|
L"MokListTrusted was ! NULL bs is now NULL\n");
|
|
ret = EFI_ABORTED;
|
|
goto out;
|
|
}
|
|
efi_status = mok_tml_prompt(MokTML, MokTMLSize);
|
|
if (!EFI_ERROR(efi_status))
|
|
MokTML = NULL;
|
|
break;
|
|
}
|
|
|
|
if (!EFI_ERROR(efi_status))
|
|
mok_changed = 1;
|
|
|
|
free_menu(menu_item, menu_strings);
|
|
menu_item = NULL;
|
|
menu_strings = NULL;
|
|
}
|
|
|
|
out:
|
|
free_menu(menu_item, menu_strings);
|
|
|
|
if (mok_changed)
|
|
return reset_system();
|
|
|
|
console_reset();
|
|
|
|
return ret;
|
|
}
|
|
|
|
static EFI_STATUS check_mok_request(EFI_HANDLE image_handle)
|
|
{
|
|
UINTN MokNewSize = 0, MokDelSize = 0, MokSBSize = 0, MokPWSize = 0;
|
|
UINTN MokDBSize = 0, MokXNewSize = 0, MokXDelSize = 0, MokTMLSize = 0;
|
|
void *MokNew = NULL;
|
|
void *MokDel = NULL;
|
|
void *MokSB = NULL;
|
|
void *MokPW = NULL;
|
|
void *MokDB = NULL;
|
|
void *MokXNew = NULL;
|
|
void *MokXDel = NULL;
|
|
void *MokTML = NULL;
|
|
EFI_STATUS efi_status;
|
|
|
|
efi_status = get_variable(L"MokNew", (UINT8 **) & MokNew, &MokNewSize,
|
|
SHIM_LOCK_GUID);
|
|
if (!EFI_ERROR(efi_status)) {
|
|
efi_status = LibDeleteVariable(L"MokNew", &SHIM_LOCK_GUID);
|
|
if (EFI_ERROR(efi_status))
|
|
console_notify(L"Failed to delete MokNew");
|
|
} else if (EFI_ERROR(efi_status) && efi_status != EFI_NOT_FOUND) {
|
|
console_error(L"Could not retrieve MokNew", efi_status);
|
|
}
|
|
|
|
efi_status = get_variable(L"MokDel", (UINT8 **) & MokDel, &MokDelSize,
|
|
SHIM_LOCK_GUID);
|
|
if (!EFI_ERROR(efi_status)) {
|
|
efi_status = LibDeleteVariable(L"MokDel", &SHIM_LOCK_GUID);
|
|
if (EFI_ERROR(efi_status))
|
|
console_notify(L"Failed to delete MokDel");
|
|
} else if (EFI_ERROR(efi_status) && efi_status != EFI_NOT_FOUND) {
|
|
console_error(L"Could not retrieve MokDel", efi_status);
|
|
}
|
|
|
|
efi_status = get_variable(L"MokSB", (UINT8 **) & MokSB, &MokSBSize,
|
|
SHIM_LOCK_GUID);
|
|
if (!EFI_ERROR(efi_status)) {
|
|
efi_status = LibDeleteVariable(L"MokSB", &SHIM_LOCK_GUID);
|
|
if (EFI_ERROR(efi_status))
|
|
console_notify(L"Failed to delete MokSB");
|
|
} else if (EFI_ERROR(efi_status) && efi_status != EFI_NOT_FOUND) {
|
|
console_error(L"Could not retrieve MokSB", efi_status);
|
|
}
|
|
|
|
efi_status = get_variable(L"MokPW", (UINT8 **) & MokPW, &MokPWSize,
|
|
SHIM_LOCK_GUID);
|
|
if (!EFI_ERROR(efi_status)) {
|
|
efi_status = LibDeleteVariable(L"MokPW", &SHIM_LOCK_GUID);
|
|
if (EFI_ERROR(efi_status))
|
|
console_notify(L"Failed to delete MokPW");
|
|
} else if (EFI_ERROR(efi_status) && efi_status != EFI_NOT_FOUND) {
|
|
console_error(L"Could not retrieve MokPW", efi_status);
|
|
}
|
|
|
|
efi_status = get_variable(L"MokDB", (UINT8 **) & MokDB, &MokDBSize,
|
|
SHIM_LOCK_GUID);
|
|
if (!EFI_ERROR(efi_status)) {
|
|
efi_status = LibDeleteVariable(L"MokDB", &SHIM_LOCK_GUID);
|
|
if (EFI_ERROR(efi_status))
|
|
console_notify(L"Failed to delete MokDB");
|
|
} else if (EFI_ERROR(efi_status) && efi_status != EFI_NOT_FOUND) {
|
|
console_error(L"Could not retrieve MokDB", efi_status);
|
|
}
|
|
|
|
efi_status = get_variable(L"MokListTrustedNew", (UINT8 **) & MokTML,
|
|
&MokTMLSize, SHIM_LOCK_GUID);
|
|
if (!EFI_ERROR(efi_status)) {
|
|
efi_status = LibDeleteVariable(L"MokListTrustedNew",
|
|
&SHIM_LOCK_GUID);
|
|
if (EFI_ERROR(efi_status))
|
|
console_notify(L"Failed to delete MokListTrustedNew");
|
|
} else if (EFI_ERROR(efi_status) && efi_status != EFI_NOT_FOUND) {
|
|
console_error(L"Could not retrieve MokListTrustedNew",
|
|
efi_status);
|
|
}
|
|
|
|
efi_status = get_variable(L"MokXNew", (UINT8 **) & MokXNew,
|
|
&MokXNewSize, SHIM_LOCK_GUID);
|
|
if (!EFI_ERROR(efi_status)) {
|
|
efi_status = LibDeleteVariable(L"MokXNew", &SHIM_LOCK_GUID);
|
|
if (EFI_ERROR(efi_status))
|
|
console_notify(L"Failed to delete MokXNew");
|
|
} else if (EFI_ERROR(efi_status) && efi_status != EFI_NOT_FOUND) {
|
|
console_error(L"Could not retrieve MokXNew", efi_status);
|
|
}
|
|
|
|
efi_status = get_variable(L"MokXDel", (UINT8 **) & MokXDel,
|
|
&MokXDelSize, SHIM_LOCK_GUID);
|
|
if (!EFI_ERROR(efi_status)) {
|
|
efi_status = LibDeleteVariable(L"MokXDel", &SHIM_LOCK_GUID);
|
|
if (EFI_ERROR(efi_status))
|
|
console_notify(L"Failed to delete MokXDel");
|
|
} else if (EFI_ERROR(efi_status) && efi_status != EFI_NOT_FOUND) {
|
|
console_error(L"Could not retrieve MokXDel", efi_status);
|
|
}
|
|
|
|
enter_mok_menu(image_handle, MokNew, MokNewSize, MokDel, MokDelSize,
|
|
MokSB, MokSBSize, MokPW, MokPWSize, MokDB, MokDBSize,
|
|
MokXNew, MokXNewSize, MokXDel, MokXDelSize, MokTML, MokTMLSize);
|
|
|
|
if (MokNew)
|
|
FreePool(MokNew);
|
|
|
|
if (MokDel)
|
|
FreePool(MokDel);
|
|
|
|
if (MokSB)
|
|
FreePool(MokSB);
|
|
|
|
if (MokPW)
|
|
FreePool(MokPW);
|
|
|
|
if (MokDB)
|
|
FreePool(MokDB);
|
|
|
|
if (MokXNew)
|
|
FreePool(MokXNew);
|
|
|
|
if (MokXDel)
|
|
FreePool(MokXDel);
|
|
|
|
if (MokTML)
|
|
FreePool(MokTML);
|
|
|
|
LibDeleteVariable(L"MokAuth", &SHIM_LOCK_GUID);
|
|
LibDeleteVariable(L"MokDelAuth", &SHIM_LOCK_GUID);
|
|
LibDeleteVariable(L"MokXAuth", &SHIM_LOCK_GUID);
|
|
LibDeleteVariable(L"MokXDelAuth", &SHIM_LOCK_GUID);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
static EFI_STATUS setup_rand(void)
|
|
{
|
|
EFI_TIME time;
|
|
EFI_STATUS efi_status;
|
|
UINT64 seed;
|
|
BOOLEAN status;
|
|
|
|
efi_status = RT->GetTime(&time, NULL);
|
|
if (EFI_ERROR(efi_status))
|
|
return efi_status;
|
|
|
|
seed = ((UINT64) time.Year << 48) | ((UINT64) time.Month << 40) |
|
|
((UINT64) time.Day << 32) | ((UINT64) time.Hour << 24) |
|
|
((UINT64) time.Minute << 16) | ((UINT64) time.Second << 8) |
|
|
((UINT64) time.Daylight);
|
|
|
|
status = RandomSeed((UINT8 *) & seed, sizeof(seed));
|
|
if (!status)
|
|
return EFI_ABORTED;
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
EFI_STATUS efi_main(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE * systab)
|
|
{
|
|
EFI_STATUS efi_status;
|
|
|
|
InitializeLib(image_handle, systab);
|
|
|
|
setup_verbosity();
|
|
setup_rand();
|
|
|
|
console_mode_handle();
|
|
|
|
efi_status = check_mok_request(image_handle);
|
|
|
|
console_fini();
|
|
return efi_status;
|
|
}
|