efi-boot-shim/MokManager.c
Peter Jones 227d13a2d9 We have to declare SHIM_LOCK_GUID here as well.
Signed-off-by: Peter Jones <pjones@redhat.com>

Conflicts:
	MokManager.c
2013-09-26 11:56:52 -04:00

1968 lines
45 KiB
C

#include <efi.h>
#include <efilib.h>
#include <Library/BaseCryptLib.h>
#include <openssl/x509.h>
#include "shim.h"
#include "signature.h"
#include "PeImage.h"
#include "console_control.h"
#define PASSWORD_MAX 16
#define PASSWORD_MIN 8
#define SB_PASSWORD_LEN 8
#ifndef SHIM_VENDOR
#define SHIM_VENDOR L"Shim"
#endif
#define EFI_VARIABLE_APPEND_WRITE 0x00000040
EFI_GUID SHIM_LOCK_GUID = { 0x605dab50, 0xe046, 0x4300, {0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23} };
#define CERT_STRING L"Select an X509 certificate to enroll:\n\n"
#define HASH_STRING L"Select a file to trust:\n\n"
struct menu_item {
CHAR16 *text;
INTN (* callback)(void *data, void *data2, void *data3);
void *data;
void *data2;
void *data3;
UINTN colour;
};
typedef struct {
UINT32 MokSize;
UINT8 *Mok;
EFI_GUID Type;
} __attribute__ ((packed)) MokListNode;
typedef struct {
UINT32 MokSBState;
UINT32 PWLen;
CHAR16 Password[PASSWORD_MAX];
} __attribute__ ((packed)) MokSBvar;
static EFI_STATUS get_variable (CHAR16 *name, EFI_GUID guid, UINT32 *attributes,
UINTN *size, void **buffer)
{
EFI_STATUS efi_status;
char allocate = !(*size);
efi_status = uefi_call_wrapper(RT->GetVariable, 5, name, &guid,
attributes, size, buffer);
if (efi_status != EFI_BUFFER_TOO_SMALL || !allocate) {
return efi_status;
}
*buffer = AllocatePool(*size);
if (!*buffer) {
Print(L"Unable to allocate variable buffer\n");
return EFI_OUT_OF_RESOURCES;
}
efi_status = uefi_call_wrapper(RT->GetVariable, 5, name, &guid,
attributes, size, *buffer);
return efi_status;
}
static EFI_INPUT_KEY get_keystroke (void)
{
EFI_INPUT_KEY key;
UINTN EventIndex;
uefi_call_wrapper(BS->WaitForEvent, 3, 1, &ST->ConIn->WaitForKey,
&EventIndex);
uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, ST->ConIn, &key);
return key;
}
static EFI_STATUS get_sha1sum (void *Data, int DataSize, UINT8 *hash)
{
EFI_STATUS status;
unsigned int ctxsize;
void *ctx = NULL;
ctxsize = Sha1GetContextSize();
ctx = AllocatePool(ctxsize);
if (!ctx) {
Print(L"Unable to allocate memory for hash context\n");
return EFI_OUT_OF_RESOURCES;
}
if (!Sha1Init(ctx)) {
Print(L"Unable to initialise hash\n");
status = EFI_OUT_OF_RESOURCES;
goto done;
}
if (!(Sha1Update(ctx, Data, DataSize))) {
Print(L"Unable to generate hash\n");
status = EFI_OUT_OF_RESOURCES;
goto done;
}
if (!(Sha1Final(ctx, hash))) {
Print(L"Unable to finalise hash\n");
status = EFI_OUT_OF_RESOURCES;
goto done;
}
status = EFI_SUCCESS;
done:
return status;
}
static UINT32 count_keys(void *Data, UINTN DataSize)
{
EFI_SIGNATURE_LIST *CertList = Data;
EFI_GUID CertType = EfiCertX509Guid;
EFI_GUID HashType = EfiHashSha256Guid;
UINTN dbsize = DataSize;
UINT32 MokNum = 0;
while ((dbsize > 0) && (dbsize >= CertList->SignatureListSize)) {
if ((CompareGuid (&CertList->SignatureType, &CertType) != 0) &&
(CompareGuid (&CertList->SignatureType, &HashType) != 0)) {
Print(L"Doesn't look like a key or hash\n");
dbsize -= CertList->SignatureListSize;
CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList +
CertList->SignatureListSize);
continue;
}
if ((CompareGuid (&CertList->SignatureType, &CertType) != 0) &&
(CertList->SignatureSize != 48)) {
Print(L"Doesn't look like a valid hash\n");
dbsize -= CertList->SignatureListSize;
CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList +
CertList->SignatureListSize);
continue;
}
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;
EFI_GUID CertType = EfiCertX509Guid;
EFI_GUID HashType = EfiHashSha256Guid;
UINTN dbsize = DataSize;
UINTN count = 0;
list = AllocatePool(sizeof(MokListNode) * num);
if (!list) {
Print(L"Unable to allocate MOK list\n");
return NULL;
}
while ((dbsize > 0) && (dbsize >= CertList->SignatureListSize)) {
if ((CompareGuid (&CertList->SignatureType, &CertType) != 0) &&
(CompareGuid (&CertList->SignatureType, &HashType) != 0)) {
dbsize -= CertList->SignatureListSize;
CertList = (EFI_SIGNATURE_LIST *)((UINT8 *) CertList +
CertList->SignatureListSize);
continue;
}
if ((CompareGuid (&CertList->SignatureType, &HashType) == 0) &&
(CertList->SignatureSize != 48)) {
dbsize -= CertList->SignatureListSize;
CertList = (EFI_SIGNATURE_LIST *)((UINT8 *) CertList +
CertList->SignatureListSize);
continue;
}
Cert = (EFI_SIGNATURE_DATA *) (((UINT8 *) CertList) +
sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize);
list[count].MokSize = CertList->SignatureSize - sizeof(EFI_GUID);
list[count].Mok = (void *)Cert->SignatureData;
list[count].Type = CertList->SignatureType;
count++;
dbsize -= CertList->SignatureListSize;
CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList +
CertList->SignatureListSize);
}
return list;
}
static void print_x509_name (X509_NAME *X509Name, CHAR16 *name)
{
char *str;
str = X509_NAME_oneline(X509Name, NULL, 0);
if (str) {
Print(L" %s:\n %a\n", name, str);
OPENSSL_free(str);
}
}
static const char *mon[12]= {
"Jan","Feb","Mar","Apr","May","Jun",
"Jul","Aug","Sep","Oct","Nov","Dec"
};
static void print_x509_GENERALIZEDTIME_time (ASN1_TIME *time, CHAR16 *time_string)
{
char *v;
int gmt = 0;
int i;
int y = 0,M = 0,d = 0,h = 0,m = 0,s = 0;
char *f = NULL;
int f_len = 0;
i=time->length;
v=(char *)time->data;
if (i < 12)
goto error;
if (v[i-1] == 'Z')
gmt=1;
for (i=0; i<12; i++) {
if ((v[i] > '9') || (v[i] < '0'))
goto error;
}
y = (v[0]-'0')*1000+(v[1]-'0')*100 + (v[2]-'0')*10+(v[3]-'0');
M = (v[4]-'0')*10+(v[5]-'0');
if ((M > 12) || (M < 1))
goto error;
d = (v[6]-'0')*10+(v[7]-'0');
h = (v[8]-'0')*10+(v[9]-'0');
m = (v[10]-'0')*10+(v[11]-'0');
if (time->length >= 14 &&
(v[12] >= '0') && (v[12] <= '9') &&
(v[13] >= '0') && (v[13] <= '9')) {
s = (v[12]-'0')*10+(v[13]-'0');
/* Check for fractions of seconds. */
if (time->length >= 15 && v[14] == '.') {
int l = time->length;
f = &v[14]; /* The decimal point. */
f_len = 1;
while (14 + f_len < l && f[f_len] >= '0' &&
f[f_len] <= '9')
++f_len;
}
}
SPrint(time_string, 0, L"%a %2d %02d:%02d:%02d%.*a %d%a",
mon[M-1], d, h, m, s, f_len, f, y, (gmt)?" GMT":"");
error:
return;
}
static void print_x509_UTCTIME_time (ASN1_TIME *time, CHAR16 *time_string)
{
char *v;
int gmt=0;
int i;
int y = 0,M = 0,d = 0,h = 0,m = 0,s = 0;
i=time->length;
v=(char *)time->data;
if (i < 10)
goto error;
if (v[i-1] == 'Z')
gmt=1;
for (i=0; i<10; i++)
if ((v[i] > '9') || (v[i] < '0'))
goto error;
y = (v[0]-'0')*10+(v[1]-'0');
if (y < 50)
y+=100;
M = (v[2]-'0')*10+(v[3]-'0');
if ((M > 12) || (M < 1))
goto error;
d = (v[4]-'0')*10+(v[5]-'0');
h = (v[6]-'0')*10+(v[7]-'0');
m = (v[8]-'0')*10+(v[9]-'0');
if (time->length >=12 &&
(v[10] >= '0') && (v[10] <= '9') &&
(v[11] >= '0') && (v[11] <= '9'))
s = (v[10]-'0')*10+(v[11]-'0');
SPrint(time_string, 0, L"%a %2d %02d:%02d:%02d %d%a",
mon[M-1], d, h, m, s, y+1900, (gmt)?" GMT":"");
error:
return;
}
static void print_x509_time (ASN1_TIME *time, CHAR16 *name)
{
CHAR16 time_string[30];
if (time->type == V_ASN1_UTCTIME) {
print_x509_UTCTIME_time(time, time_string);
} else if (time->type == V_ASN1_GENERALIZEDTIME) {
print_x509_GENERALIZEDTIME_time(time, time_string);
} else {
time_string[0] = '\0';
}
Print(L" %s:\n %s\n", name, time_string);
}
static void show_x509_info (X509 *X509Cert)
{
ASN1_INTEGER *serial;
BIGNUM *bnser;
unsigned char hexbuf[30];
X509_NAME *X509Name;
ASN1_TIME *time;
serial = X509_get_serialNumber(X509Cert);
if (serial) {
int i, n;
bnser = ASN1_INTEGER_to_BN(serial, NULL);
n = BN_bn2bin(bnser, hexbuf);
Print(L" Serial Number:\n ");
for (i = 0; i < n-1; i++) {
Print(L"%02x:", hexbuf[i]);
}
Print(L"%02x\n", hexbuf[n-1]);
}
X509Name = X509_get_issuer_name(X509Cert);
if (X509Name) {
print_x509_name(X509Name, L"Issuer");
}
X509Name = X509_get_subject_name(X509Cert);
if (X509Name) {
print_x509_name(X509Name, L"Subject");
}
time = X509_get_notBefore(X509Cert);
if (time) {
print_x509_time(time, L"Validity from");
}
time = X509_get_notAfter(X509Cert);
if (time) {
print_x509_time(time, L"Validity till");
}
}
static void show_mok_info (void *Mok, UINTN MokSize)
{
EFI_STATUS efi_status;
UINT8 hash[SHA1_DIGEST_SIZE];
unsigned int i;
X509 *X509Cert;
if (!Mok || MokSize == 0)
return;
if (MokSize != SHA256_DIGEST_SIZE) {
if (X509ConstructCertificate(Mok, MokSize,
(UINT8 **) &X509Cert) && X509Cert != NULL) {
show_x509_info(X509Cert);
X509_free(X509Cert);
} else {
Print(L" Not a valid X509 certificate: %x\n\n",
((UINT32 *)Mok)[0]);
return;
}
efi_status = get_sha1sum(Mok, MokSize, hash);
if (efi_status != EFI_SUCCESS) {
Print(L"Failed to compute MOK fingerprint\n");
return;
}
Print(L" Fingerprint (SHA1):\n ");
for (i = 0; i < SHA1_DIGEST_SIZE; i++) {
Print(L" %02x", hash[i]);
if (i % 10 == 9)
Print(L"\n ");
}
} else {
Print(L"SHA256 hash:\n ");
for (i = 0; i < SHA256_DIGEST_SIZE; i++) {
Print(L" %02x", ((UINT8 *)Mok)[i]);
if (i % 10 == 9)
Print(L"\n ");
}
Print(L"\n");
}
Print(L"\n");
}
static INTN get_number ()
{
EFI_INPUT_KEY input_key;
CHAR16 input[10];
int count = 0;
do {
input_key = get_keystroke();
if ((input_key.UnicodeChar < '0' ||
input_key.UnicodeChar > '9' ||
count >= 10) &&
input_key.UnicodeChar != CHAR_BACKSPACE) {
continue;
}
if (count == 0 && input_key.UnicodeChar == CHAR_BACKSPACE)
continue;
Print(L"%c", input_key.UnicodeChar);
if (input_key.UnicodeChar == CHAR_BACKSPACE) {
input[--count] = '\0';
continue;
}
input[count++] = input_key.UnicodeChar;
} while (input_key.UnicodeChar != CHAR_CARRIAGE_RETURN);
if (count == 0)
return -1;
input[count] = '\0';
return (INTN)Atoi(input);
}
static UINT8 list_keys (void *KeyList, UINTN KeyListSize, CHAR16 *title)
{
UINT32 MokNum = 0;
MokListNode *keys = NULL;
INTN key_num = 0;
UINT8 initial = 1;
if (KeyListSize < (sizeof(EFI_SIGNATURE_LIST) +
sizeof(EFI_SIGNATURE_DATA))) {
Print(L"No keys\n");
Pause();
return 0;
}
MokNum = count_keys(KeyList, KeyListSize);
keys = build_mok_list(MokNum, KeyList, KeyListSize);
if (!keys) {
Print(L"Failed to construct key list\n");
return 0;
}
do {
uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
if (title)
Print(L"%s\n", title);
Print(L"Input the key number to show the details of the key or\n"
L"type \'0\' to continue\n\n");
Print(L"%d key(s) in the key list\n\n", MokNum);
if (key_num > MokNum) {
Print(L"[Key %d]\n", key_num);
Print(L"No such key\n\n");
} else if (initial != 1 && key_num > 0){
Print(L"[Key %d]\n", key_num);
show_mok_info(keys[key_num-1].Mok, keys[key_num-1].MokSize);
}
Print(L"Key Number: ");
key_num = get_number();
Print(L"\n\n");
if (key_num == -1)
continue;
initial = 0;
} while (key_num != 0);
FreePool(keys);
return 1;
}
static UINT8 get_line (UINT32 *length, CHAR16 *line, UINT32 line_max, UINT8 show)
{
EFI_INPUT_KEY key;
int count = 0;
do {
key = get_keystroke();
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) {
Print(L"\b");
}
line[--count] = '\0';
continue;
}
if (show) {
Print(L"%c", key.UnicodeChar);
}
line[count++] = key.UnicodeChar;
} while (key.UnicodeChar != CHAR_CARRIAGE_RETURN);
Print(L"\n");
*length = count;
return 1;
}
static EFI_STATUS compute_pw_hash (void *MokNew, UINTN MokNewSize, CHAR16 *password,
UINT32 pw_length, UINT8 *hash)
{
EFI_STATUS status;
unsigned int ctxsize;
void *ctx = NULL;
ctxsize = Sha256GetContextSize();
ctx = AllocatePool(ctxsize);
if (!ctx) {
Print(L"Unable to allocate memory for hash context\n");
return EFI_OUT_OF_RESOURCES;
}
if (!Sha256Init(ctx)) {
Print(L"Unable to initialise hash\n");
status = EFI_OUT_OF_RESOURCES;
goto done;
}
if (MokNew && MokNewSize) {
if (!(Sha256Update(ctx, MokNew, MokNewSize))) {
Print(L"Unable to generate hash\n");
status = EFI_OUT_OF_RESOURCES;
goto done;
}
}
if (!(Sha256Update(ctx, password, pw_length * sizeof(CHAR16)))) {
Print(L"Unable to generate hash\n");
status = EFI_OUT_OF_RESOURCES;
goto done;
}
if (!(Sha256Final(ctx, hash))) {
Print(L"Unable to finalise hash\n");
status = EFI_OUT_OF_RESOURCES;
goto done;
}
status = EFI_SUCCESS;
done:
return status;
}
static EFI_STATUS match_password (void *Data, UINTN DataSize,
UINT8 auth[SHA256_DIGEST_SIZE],
CHAR16 *prompt)
{
EFI_STATUS efi_status;
UINT8 hash[SHA256_DIGEST_SIZE];
CHAR16 password[PASSWORD_MAX];
UINT32 pw_length;
UINT8 fail_count = 0;
while (fail_count < 3) {
if (prompt) {
Print(L"%s", prompt);
} else {
Print(L"Password: ");
}
get_line(&pw_length, password, PASSWORD_MAX, 0);
if (pw_length < PASSWORD_MIN || pw_length > PASSWORD_MAX) {
Print(L"Invalid password length\n");
fail_count++;
continue;
}
efi_status = compute_pw_hash(Data, DataSize, password,
pw_length, hash);
if (efi_status != EFI_SUCCESS) {
Print(L"Unable to generate password hash\n");
fail_count++;
continue;
}
if (CompareMem(auth, hash, SHA256_DIGEST_SIZE) != 0) {
Print(L"Password doesn't match\n");
fail_count++;
continue;
}
break;
}
if (fail_count >= 3)
return EFI_ACCESS_DENIED;
return EFI_SUCCESS;
}
static EFI_STATUS store_keys (void *MokNew, UINTN MokNewSize, int authenticate)
{
EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
EFI_STATUS efi_status;
UINT8 auth[SHA256_DIGEST_SIZE];
UINTN auth_size;
UINT32 attributes;
if (authenticate) {
auth_size = SHA256_DIGEST_SIZE;
efi_status = uefi_call_wrapper(RT->GetVariable, 5, L"MokAuth",
&shim_lock_guid,
&attributes, &auth_size, auth);
if (efi_status != EFI_SUCCESS || auth_size != SHA256_DIGEST_SIZE) {
Print(L"Failed to get MokAuth %d\n", efi_status);
return efi_status;
}
efi_status = match_password(MokNew, MokNewSize, auth, NULL);
if (efi_status != EFI_SUCCESS)
return EFI_ACCESS_DENIED;
}
if (!MokNewSize) {
/* Delete MOK */
efi_status = uefi_call_wrapper(RT->SetVariable, 5, L"MokList",
&shim_lock_guid,
EFI_VARIABLE_NON_VOLATILE
| EFI_VARIABLE_BOOTSERVICE_ACCESS,
0, NULL);
} else {
/* Write new MOK */
efi_status = uefi_call_wrapper(RT->SetVariable, 5, L"MokList",
&shim_lock_guid,
EFI_VARIABLE_NON_VOLATILE
| EFI_VARIABLE_BOOTSERVICE_ACCESS
| EFI_VARIABLE_APPEND_WRITE,
MokNewSize, MokNew);
}
if (efi_status != EFI_SUCCESS) {
Print(L"Failed to set variable %d\n", efi_status);
return efi_status;
}
return EFI_SUCCESS;
}
static UINTN mok_enrollment_prompt (void *MokNew, UINTN MokNewSize, int auth) {
EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
CHAR16 line[1];
UINT32 length;
EFI_STATUS efi_status;
do {
if (!list_keys(MokNew, MokNewSize, L"[Enroll MOK]")) {
return 0;
}
Print(L"Enroll the key(s)? (y/n): ");
get_line (&length, line, 1, 1);
if (line[0] == 'Y' || line[0] == 'y') {
efi_status = store_keys(MokNew, MokNewSize, auth);
if (efi_status != EFI_SUCCESS) {
Print(L"Failed to enroll keys\n");
return -1;
}
if (auth) {
LibDeleteVariable(L"MokNew", &shim_lock_guid);
LibDeleteVariable(L"MokAuth", &shim_lock_guid);
Print(L"\nPress a key to reboot system\n");
Pause();
uefi_call_wrapper(RT->ResetSystem, 4, EfiResetWarm,
EFI_SUCCESS, 0, NULL);
Print(L"Failed to reboot\n");
return -1;
}
return 0;
}
} while (line[0] != 'N' && line[0] != 'n');
return -1;
}
static INTN mok_enrollment_prompt_callback (void *MokNew, void *data2,
void *data3)
{
uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
return mok_enrollment_prompt(MokNew, (UINTN)data2, TRUE);
}
static INTN mok_reset_prompt (void *MokNew, void *data2, void *data3)
{
EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
CHAR16 line[1];
UINT32 length;
EFI_STATUS efi_status;
uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
Print(L"Erase all stored keys? (y/N): ");
get_line (&length, line, 1, 1);
if (line[0] == 'Y' || line[0] == 'y') {
efi_status = store_keys(NULL, 0, TRUE);
if (efi_status != EFI_SUCCESS) {
Print(L"Failed to erase keys\n");
return -1;
}
LibDeleteVariable(L"MokNew", &shim_lock_guid);
LibDeleteVariable(L"MokAuth", &shim_lock_guid);
Print(L"\nPress a key to reboot system\n");
Pause();
uefi_call_wrapper(RT->ResetSystem, 4, EfiResetWarm,
EFI_SUCCESS, 0, NULL);
Print(L"Failed to reboot\n");
return -1;
}
return 0;
}
static EFI_STATUS write_back_mok_list (MokListNode *list, INTN key_num)
{
EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
EFI_STATUS efi_status;
EFI_SIGNATURE_LIST *CertList;
EFI_SIGNATURE_DATA *CertData;
void *Data = NULL, *ptr;
INTN DataSize = 0;
int i;
for (i = 0; i < key_num; i++) {
if (list[i].Mok == NULL)
continue;
DataSize += sizeof(EFI_SIGNATURE_LIST) + sizeof(EFI_GUID);
DataSize += list[i].MokSize;
}
Data = AllocatePool(DataSize);
if (Data == NULL && DataSize != 0)
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->SignatureListSize = list[i].MokSize +
sizeof(EFI_SIGNATURE_LIST) +
sizeof(EFI_SIGNATURE_DATA) - 1;
CertList->SignatureHeaderSize = 0;
CertList->SignatureSize = list[i].MokSize + sizeof(EFI_GUID);
CertData->SignatureOwner = shim_lock_guid;
CopyMem(CertData->SignatureData, list[i].Mok, list[i].MokSize);
ptr = (uint8_t *)ptr + sizeof(EFI_SIGNATURE_LIST) +
sizeof(EFI_GUID) + list[i].MokSize;
}
efi_status = uefi_call_wrapper(RT->SetVariable, 5, L"MokList",
&shim_lock_guid,
EFI_VARIABLE_NON_VOLATILE
| EFI_VARIABLE_BOOTSERVICE_ACCESS,
DataSize, Data);
if (Data)
FreePool(Data);
if (efi_status != EFI_SUCCESS) {
Print(L"Failed to set variable %d\n", efi_status);
return efi_status;
}
return EFI_SUCCESS;
}
static EFI_STATUS delete_keys (void *MokDel, UINTN MokDelSize)
{
EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
EFI_STATUS efi_status;
UINT8 auth[SHA256_DIGEST_SIZE];
UINTN auth_size = SHA256_DIGEST_SIZE;
UINT32 attributes;
void *MokListData = NULL;
UINTN MokListDataSize = 0;
MokListNode *mok, *del_key;
INTN mok_num, del_num;
int i, j;
efi_status = uefi_call_wrapper(RT->GetVariable, 5, L"MokDelAuth",
&shim_lock_guid,
&attributes, &auth_size, auth);
if (efi_status != EFI_SUCCESS || auth_size != SHA256_DIGEST_SIZE) {
Print(L"Failed to get MokDelAuth %d\n", efi_status);
return efi_status;
}
efi_status = match_password(MokDel, MokDelSize, auth, NULL);
if (efi_status != EFI_SUCCESS)
return EFI_ACCESS_DENIED;
efi_status = get_variable(L"MokList", shim_lock_guid, &attributes,
&MokListDataSize, &MokListData);
if (attributes & EFI_VARIABLE_RUNTIME_ACCESS) {
Print(L"MokList is compromised!\nErase all keys in MokList!\n");
if (LibDeleteVariable(L"MokList", &shim_lock_guid) != EFI_SUCCESS) {
Print(L"Failed to erase MokList\n");
}
return EFI_ACCESS_DENIED;
}
/* Nothing to do */
if (!MokListData || MokListDataSize == 0)
return EFI_SUCCESS;
/* Construct lists */
mok_num = count_keys(MokListData, MokListDataSize);
mok = build_mok_list(mok_num, MokListData, MokListDataSize);
del_num = count_keys(MokDel, MokDelSize);
del_key = build_mok_list(del_num, MokDel, MokDelSize);
/* Search and destroy */
for (i = 0; i < del_num; i++) {
UINT32 key_size = del_key[i].MokSize;
void *key = del_key[i].Mok;
for (j = 0; j < mok_num; j++) {
if (mok[j].MokSize == key_size &&
CompareMem(key, mok[j].Mok, key_size) == 0) {
/* Remove the key */
mok[j].Mok = NULL;
mok[j].MokSize = 0;
}
}
}
efi_status = write_back_mok_list(mok, mok_num);
if (MokListData)
FreePool(MokListData);
if (mok)
FreePool(mok);
if (del_key)
FreePool(del_key);
return efi_status;
}
static INTN mok_deletion_prompt (void *MokDel, void *data2, void *data3)
{
EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
UINTN MokDelSize = (UINTN)data2;
CHAR16 line[1];
UINT32 length;
EFI_STATUS efi_status;
do {
if (!list_keys(MokDel, MokDelSize, L"[Delete MOK]")) {
return 0;
}
Print(L"Delete the key(s)? (y/n): ");
get_line (&length, line, 1, 1);
if (line[0] == 'Y' || line[0] == 'y') {
efi_status = delete_keys(MokDel, MokDelSize);
if (efi_status != EFI_SUCCESS) {
Print(L"Failed to delete keys\n");
return -1;
}
LibDeleteVariable(L"MokDel", &shim_lock_guid);
LibDeleteVariable(L"MokDelAuth", &shim_lock_guid);
Print(L"\nPress a key to reboot system\n");
Pause();
uefi_call_wrapper(RT->ResetSystem, 4, EfiResetWarm,
EFI_SUCCESS, 0, NULL);
Print(L"Failed to reboot\n");
return -1;
}
} while (line[0] != 'N' && line[0] != 'n');
return -1;
}
static INTN mok_sb_prompt (void *MokSB, void *data2, void *data3) {
EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
EFI_STATUS efi_status;
UINTN MokSBSize = (UINTN)data2;
MokSBvar *var = MokSB;
CHAR16 pass1, pass2, pass3;
UINT8 fail_count = 0;
UINT32 length;
CHAR16 line[1];
UINT8 sbval = 1;
UINT8 pos1, pos2, pos3;
if (MokSBSize != sizeof(MokSBvar)) {
Print(L"Invalid MokSB variable contents\n");
return -1;
}
uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
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);
Print(L"Enter password character %d: ", pos1 + 1);
get_line(&length, &pass1, 1, 0);
Print(L"Enter password character %d: ", pos2 + 1);
get_line(&length, &pass2, 1, 0);
Print(L"Enter password character %d: ", pos3 + 1);
get_line(&length, &pass3, 1, 0);
if (pass1 != var->Password[pos1] ||
pass2 != var->Password[pos2] ||
pass3 != var->Password[pos3]) {
Print(L"Invalid character\n");
fail_count++;
} else {
break;
}
}
if (fail_count >= 3) {
Print(L"Password limit reached\n");
return -1;
}
if (var->MokSBState == 0) {
Print(L"Disable Secure Boot? (y/n): ");
} else {
Print(L"Enable Secure Boot? (y/n): ");
}
do {
get_line (&length, line, 1, 1);
if (line[0] == 'Y' || line[0] == 'y') {
if (var->MokSBState == 0) {
efi_status = uefi_call_wrapper(RT->SetVariable,
5, L"MokSBState",
&shim_lock_guid,
EFI_VARIABLE_NON_VOLATILE |
EFI_VARIABLE_BOOTSERVICE_ACCESS,
1, &sbval);
if (efi_status != EFI_SUCCESS) {
Print(L"Failed to set Secure Boot state\n");
return -1;
}
} else {
LibDeleteVariable(L"MokSBState",
&shim_lock_guid);
}
LibDeleteVariable(L"MokSB", &shim_lock_guid);
Print(L"Press a key to reboot system\n");
Pause();
uefi_call_wrapper(RT->ResetSystem, 4, EfiResetWarm,
EFI_SUCCESS, 0, NULL);
Print(L"Failed to reboot\n");
return -1;
}
} while (line[0] != 'N' && line[0] != 'n');
return -1;
}
static INTN mok_pw_prompt (void *MokPW, void *data2, void *data3) {
EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
EFI_STATUS efi_status;
UINTN MokPWSize = (UINTN)data2;
UINT8 hash[SHA256_DIGEST_SIZE];
UINT32 length;
CHAR16 line[1];
if (MokPWSize != SHA256_DIGEST_SIZE) {
Print(L"Invalid MokPW variable contents\n");
return -1;
}
uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
SetMem(hash, SHA256_DIGEST_SIZE, 0);
if (CompareMem(MokPW, hash, SHA256_DIGEST_SIZE) == 0) {
Print(L"Clear MOK password? (y/n): ");
do {
get_line (&length, line, 1, 1);
if (line[0] == 'Y' || line[0] == 'y') {
LibDeleteVariable(L"MokPWStore", &shim_lock_guid);
LibDeleteVariable(L"MokPW", &shim_lock_guid);
}
} while (line[0] != 'N' && line[0] != 'n');
return 0;
}
efi_status = match_password(NULL, 0, MokPW, L"Confirm MOK passphrase: ");
if (efi_status != EFI_SUCCESS) {
Print(L"Password limit reached\n");
return -1;
}
Print(L"Set MOK password? (y/n): ");
do {
get_line (&length, line, 1, 1);
if (line[0] == 'Y' || line[0] == 'y') {
efi_status = uefi_call_wrapper(RT->SetVariable, 5,
L"MokPWStore",
&shim_lock_guid,
EFI_VARIABLE_NON_VOLATILE |
EFI_VARIABLE_BOOTSERVICE_ACCESS,
MokPWSize, MokPW);
if (efi_status != EFI_SUCCESS) {
Print(L"Failed to set MOK password\n");
return -1;
}
LibDeleteVariable(L"MokPW", &shim_lock_guid);
Print(L"Press a key to reboot system\n");
Pause();
uefi_call_wrapper(RT->ResetSystem, 4, EfiResetWarm,
EFI_SUCCESS, 0, NULL);
Print(L"Failed to reboot\n");
return -1;
}
} while (line[0] != 'N' && line[0] != 'n');
return 0;
}
static UINTN draw_menu (CHAR16 *header, UINTN lines, struct menu_item *items,
UINTN count) {
UINTN i;
uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut,
EFI_WHITE | EFI_BACKGROUND_BLACK);
Print(L"%s UEFI key management\n\n", SHIM_VENDOR);
if (header)
Print(L"%s", header);
for (i = 0; i < count; i++) {
uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut,
items[i].colour | EFI_BACKGROUND_BLACK);
Print(L" %s\n", items[i].text);
}
uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, 0);
uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, TRUE);
return 2 + lines;
}
static void free_menu (struct menu_item *items, UINTN count) {
UINTN i;
for (i=0; i<count; i++) {
if (items[i].text)
FreePool(items[i].text);
}
FreePool(items);
}
static void update_time (UINTN position, UINTN timeout)
{
uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0,
position);
uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut,
EFI_BLACK | EFI_BACKGROUND_BLACK);
Print(L" ", timeout);
uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0,
position);
uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut,
EFI_WHITE | EFI_BACKGROUND_BLACK);
if (timeout > 1)
Print(L"Booting in %d seconds\n", timeout);
else if (timeout)
Print(L"Booting in %d second\n", timeout);
}
static void run_menu (CHAR16 *header, UINTN lines, struct menu_item *items,
UINTN count, UINTN timeout) {
UINTN index, pos = 0, wait = 0, offset;
EFI_INPUT_KEY key;
EFI_STATUS status;
INTN ret;
if (timeout)
wait = 10000000;
offset = draw_menu (header, lines, items, count);
while (1) {
update_time(count + offset + 1, timeout);
uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut,
0, pos + offset);
status = WaitForSingleEvent(ST->ConIn->WaitForKey, wait);
if (status == EFI_TIMEOUT) {
timeout--;
if (!timeout) {
free_menu(items, count);
return;
}
continue;
}
wait = 0;
timeout = 0;
uefi_call_wrapper(BS->WaitForEvent, 3, 1,
&ST->ConIn->WaitForKey, &index);
uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, ST->ConIn,
&key);
switch(key.ScanCode) {
case SCAN_UP:
if (pos == 0)
continue;
pos--;
continue;
break;
case SCAN_DOWN:
if (pos == (count - 1))
continue;
pos++;
continue;
break;
}
switch(key.UnicodeChar) {
case CHAR_LINEFEED:
case CHAR_CARRIAGE_RETURN:
if (items[pos].callback == NULL) {
free_menu(items, count);
return;
}
ret = items[pos].callback(items[pos].data,
items[pos].data2,
items[pos].data3);
if (ret < 0) {
Print(L"Press a key to continue\n");
Pause();
/* Clear the key in the queue */
uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2,
ST->ConIn, &key);
}
draw_menu (header, lines, items, count);
pos = 0;
break;
}
}
}
static UINTN verify_certificate(void *cert, UINTN size)
{
X509 *X509Cert;
if (!cert || size == 0)
return FALSE;
if (!(X509ConstructCertificate(cert, size, (UINT8 **) &X509Cert)) ||
X509Cert == NULL) {
Print(L"Invalid X509 certificate\n");
Pause();
return FALSE;
}
X509_free(X509Cert);
return TRUE;
}
static INTN file_callback (void *data, void *data2, void *data3) {
EFI_FILE_INFO *buffer = NULL;
UINTN buffersize = 0, mokbuffersize;
EFI_STATUS status;
EFI_FILE *file;
CHAR16 *filename = data;
EFI_FILE *parent = data2;
BOOLEAN hash = !!data3;
EFI_GUID file_info_guid = EFI_FILE_INFO_ID;
EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
EFI_SIGNATURE_LIST *CertList;
EFI_SIGNATURE_DATA *CertData;
void *mokbuffer = NULL;
status = uefi_call_wrapper(parent->Open, 5, parent, &file, filename,
EFI_FILE_MODE_READ, 0);
if (status != EFI_SUCCESS)
return 1;
status = uefi_call_wrapper(file->GetInfo, 4, file, &file_info_guid,
&buffersize, buffer);
if (status == EFI_BUFFER_TOO_SMALL) {
buffer = AllocatePool(buffersize);
status = uefi_call_wrapper(file->GetInfo, 4, file,
&file_info_guid, &buffersize,
buffer);
}
if (!buffer)
return 0;
buffersize = buffer->FileSize;
if (hash) {
void *binary;
UINT8 sha256[SHA256_DIGEST_SIZE];
UINT8 sha1[SHA1_DIGEST_SIZE];
SHIM_LOCK *shim_lock;
EFI_GUID shim_guid = SHIM_LOCK_GUID;
PE_COFF_LOADER_IMAGE_CONTEXT context;
status = LibLocateProtocol(&shim_guid, (VOID **)&shim_lock);
if (status != EFI_SUCCESS)
goto out;
mokbuffersize = sizeof(EFI_SIGNATURE_LIST) + sizeof(EFI_GUID) +
SHA256_DIGEST_SIZE;
mokbuffer = AllocatePool(mokbuffersize);
if (!mokbuffer)
goto out;
binary = AllocatePool(buffersize);
status = uefi_call_wrapper(file->Read, 3, file, &buffersize,
binary);
if (status != EFI_SUCCESS)
goto out;
status = shim_lock->Context(binary, buffersize, &context);
if (status != EFI_SUCCESS)
goto out;
status = shim_lock->Hash(binary, buffersize, &context, sha256,
sha1);
if (status != EFI_SUCCESS)
goto out;
CertList = mokbuffer;
CertList->SignatureType = EfiHashSha256Guid;
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 = buffersize + sizeof(EFI_SIGNATURE_LIST) +
sizeof(EFI_GUID);
mokbuffer = AllocatePool(mokbuffersize);
if (!mokbuffer)
goto out;
CertList = mokbuffer;
CertList->SignatureType = EfiCertX509Guid;
CertList->SignatureSize = 16 + buffersize;
status = uefi_call_wrapper(file->Read, 3, file, &buffersize,
mokbuffer + sizeof(EFI_SIGNATURE_LIST) + 16);
if (status != EFI_SUCCESS)
goto out;
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, buffersize))
goto out;
}
mok_enrollment_prompt(mokbuffer, mokbuffersize, FALSE);
out:
if (buffer)
FreePool(buffer);
if (mokbuffer)
FreePool(mokbuffer);
return 0;
}
static INTN directory_callback (void *data, void *data2, void *data3) {
EFI_FILE_INFO *buffer = NULL;
UINTN buffersize = 0;
EFI_STATUS status;
UINTN dircount = 0, i = 0;
struct menu_item *dircontent;
EFI_FILE *dir;
CHAR16 *filename = data;
EFI_FILE *root = data2;
BOOLEAN hash = !!data3;
status = uefi_call_wrapper(root->Open, 5, root, &dir, filename,
EFI_FILE_MODE_READ, 0);
if (status != EFI_SUCCESS)
return 1;
while (1) {
status = uefi_call_wrapper(dir->Read, 3, dir, &buffersize,
buffer);
if (status == EFI_BUFFER_TOO_SMALL) {
buffer = AllocatePool(buffersize);
status = uefi_call_wrapper(dir->Read, 3, dir,
&buffersize, buffer);
}
if (status != EFI_SUCCESS)
return 1;
if (!buffersize)
break;
if ((StrCmp(buffer->FileName, L".") == 0) ||
(StrCmp(buffer->FileName, L"..") == 0))
continue;
dircount++;
FreePool(buffer);
buffersize = 0;
}
dircount++;
dircontent = AllocatePool(sizeof(struct menu_item) * dircount);
dircontent[0].text = StrDuplicate(L"..");
dircontent[0].callback = NULL;
dircontent[0].colour = EFI_YELLOW;
i++;
uefi_call_wrapper(dir->SetPosition, 2, dir, 0);
while (1) {
status = uefi_call_wrapper(dir->Read, 3, dir, &buffersize,
buffer);
if (status == EFI_BUFFER_TOO_SMALL) {
buffer = AllocatePool(buffersize);
status = uefi_call_wrapper(dir->Read, 3, dir,
&buffersize, buffer);
}
if (status != EFI_SUCCESS)
return 1;
if (!buffersize)
break;
if ((StrCmp(buffer->FileName, L".") == 0) ||
(StrCmp(buffer->FileName, L"..") == 0))
continue;
if (buffer->Attribute & EFI_FILE_DIRECTORY) {
dircontent[i].text = StrDuplicate(buffer->FileName);
dircontent[i].callback = directory_callback;
dircontent[i].data = dircontent[i].text;
dircontent[i].data2 = dir;
dircontent[i].data3 = data3;
dircontent[i].colour = EFI_YELLOW;
} else {
dircontent[i].text = StrDuplicate(buffer->FileName);
dircontent[i].callback = file_callback;
dircontent[i].data = dircontent[i].text;
dircontent[i].data2 = dir;
dircontent[i].data3 = data3;
dircontent[i].colour = EFI_WHITE;
}
i++;
FreePool(buffer);
buffersize = 0;
buffer = NULL;
}
if (hash)
run_menu(HASH_STRING, 2, dircontent, dircount, 0);
else
run_menu(CERT_STRING, 2, dircontent, dircount, 0);
return 0;
}
static INTN filesystem_callback (void *data, void *data2, void *data3) {
EFI_FILE_INFO *buffer = NULL;
UINTN buffersize = 0;
EFI_STATUS status;
UINTN dircount = 0, i = 0;
struct menu_item *dircontent;
EFI_FILE *root = data;
BOOLEAN hash = !!data3;
uefi_call_wrapper(root->SetPosition, 2, root, 0);
while (1) {
status = uefi_call_wrapper(root->Read, 3, root, &buffersize,
buffer);
if (status == EFI_BUFFER_TOO_SMALL) {
buffer = AllocatePool(buffersize);
status = uefi_call_wrapper(root->Read, 3, root,
&buffersize, buffer);
}
if (status != EFI_SUCCESS)
return 1;
if (!buffersize)
break;
if ((StrCmp(buffer->FileName, L".") == 0) ||
(StrCmp(buffer->FileName, L"..") == 0))
continue;
dircount++;
FreePool(buffer);
buffersize = 0;
}
dircount++;
dircontent = AllocatePool(sizeof(struct menu_item) * dircount);
dircontent[0].text = StrDuplicate(L"Return to filesystem list");
dircontent[0].callback = NULL;
dircontent[0].colour = EFI_YELLOW;
i++;
uefi_call_wrapper(root->SetPosition, 2, root, 0);
while (1) {
status = uefi_call_wrapper(root->Read, 3, root, &buffersize,
buffer);
if (status == EFI_BUFFER_TOO_SMALL) {
buffer = AllocatePool(buffersize);
status = uefi_call_wrapper(root->Read, 3, root,
&buffersize, buffer);
}
if (status != EFI_SUCCESS)
return 1;
if (!buffersize)
break;
if ((StrCmp(buffer->FileName, L".") == 0) ||
(StrCmp(buffer->FileName, L"..") == 0))
continue;
if (buffer->Attribute & EFI_FILE_DIRECTORY) {
dircontent[i].text = StrDuplicate(buffer->FileName);
dircontent[i].callback = directory_callback;
dircontent[i].data = dircontent[i].text;
dircontent[i].data2 = root;
dircontent[i].data3 = data3;
dircontent[i].colour = EFI_YELLOW;
} else {
dircontent[i].text = StrDuplicate(buffer->FileName);
dircontent[i].callback = file_callback;
dircontent[i].data = dircontent[i].text;
dircontent[i].data2 = root;
dircontent[i].data3 = data3;
dircontent[i].colour = EFI_WHITE;
}
i++;
FreePool(buffer);
buffer = NULL;
buffersize = 0;
}
if (hash)
run_menu(HASH_STRING, 2, dircontent, dircount, 0);
else
run_menu(CERT_STRING, 2, dircontent, dircount, 0);
return 0;
}
static INTN find_fs (void *data, void *data2, void *data3) {
EFI_GUID fs_guid = SIMPLE_FILE_SYSTEM_PROTOCOL;
UINTN count, i;
UINTN OldSize, NewSize;
EFI_HANDLE *filesystem_handles = NULL;
struct menu_item *filesystems;
BOOLEAN hash = !!data3;
uefi_call_wrapper(BS->LocateHandleBuffer, 5, ByProtocol, &fs_guid,
NULL, &count, &filesystem_handles);
if (!count || !filesystem_handles) {
Print(L"No filesystems?\n");
return 1;
}
count++;
filesystems = AllocatePool(sizeof(struct menu_item) * count);
filesystems[0].text = StrDuplicate(L"Exit");
filesystems[0].callback = NULL;
filesystems[0].colour = EFI_YELLOW;
for (i=1; i<count; i++) {
EFI_HANDLE fs = filesystem_handles[i-1];
EFI_FILE_IO_INTERFACE *fs_interface;
EFI_DEVICE_PATH *path;
EFI_FILE *root;
EFI_STATUS status;
CHAR16 *VolumeLabel = NULL;
EFI_FILE_SYSTEM_INFO *buffer = NULL;
UINTN buffersize = 0;
EFI_GUID file_info_guid = EFI_FILE_INFO_ID;
status = uefi_call_wrapper(BS->HandleProtocol, 3, fs, &fs_guid,
(void **)&fs_interface);
if (status != EFI_SUCCESS || !fs_interface)
continue;
path = DevicePathFromHandle(fs);
status = uefi_call_wrapper(fs_interface->OpenVolume, 2,
fs_interface, &root);
if (status != EFI_SUCCESS || !root)
continue;
status = uefi_call_wrapper(root->GetInfo, 4, root,
&file_info_guid, &buffersize,
buffer);
if (status == EFI_BUFFER_TOO_SMALL) {
buffer = AllocatePool(buffersize);
status = uefi_call_wrapper(root->GetInfo, 4, root,
&file_info_guid,
&buffersize, buffer);
}
if (status == EFI_SUCCESS)
VolumeLabel = buffer->VolumeLabel;
if (path)
filesystems[i].text = DevicePathToStr(path);
else
filesystems[i].text = StrDuplicate(L"Unknown device\n");
if (VolumeLabel) {
OldSize = (StrLen(filesystems[i].text) + 1) * sizeof(CHAR16);
NewSize = OldSize + StrLen(VolumeLabel) * sizeof(CHAR16);
filesystems[i].text = ReallocatePool(filesystems[i].text,
OldSize, NewSize);
StrCat(filesystems[i].text, VolumeLabel);
}
if (buffersize)
FreePool(buffer);
filesystems[i].data = root;
filesystems[i].data2 = NULL;
filesystems[i].data3 = data3;
filesystems[i].callback = filesystem_callback;
filesystems[i].colour = EFI_YELLOW;
}
uefi_call_wrapper(BS->FreePool, 1, filesystem_handles);
if (hash)
run_menu(HASH_STRING, 2, filesystems, count, 0);
else
run_menu(CERT_STRING, 2, filesystems, count, 0);
return 0;
}
static BOOLEAN verify_pw(void)
{
EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
EFI_STATUS efi_status;
UINT8 pwhash[SHA256_DIGEST_SIZE];
UINTN size = SHA256_DIGEST_SIZE;
UINT32 attributes;
efi_status = uefi_call_wrapper(RT->GetVariable, 5, 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_status != EFI_SUCCESS)
return TRUE;
if (attributes & EFI_VARIABLE_RUNTIME_ACCESS)
return TRUE;
uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
efi_status = match_password(NULL, 0, pwhash, L"Enter MOK password: ");
if (efi_status != EFI_SUCCESS) {
Print(L"Password limit reached\n");
return FALSE;
}
return TRUE;
}
static EFI_STATUS enter_mok_menu(EFI_HANDLE image_handle,
void *MokNew, UINTN MokNewSize,
void *MokDel, UINTN MokDelSize,
void *MokSB, UINTN MokSBSize,
void *MokPW, UINTN MokPWSize)
{
struct menu_item *menu_item;
UINT32 MokAuth = 0;
UINT32 MokDelAuth = 0;
UINTN menucount = 3, i = 0;
EFI_STATUS efi_status;
EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
UINT8 auth[SHA256_DIGEST_SIZE];
UINTN auth_size = SHA256_DIGEST_SIZE;
UINT32 attributes;
if (verify_pw() == FALSE)
return EFI_ACCESS_DENIED;
efi_status = uefi_call_wrapper(RT->GetVariable, 5, L"MokAuth",
&shim_lock_guid,
&attributes, &auth_size, auth);
if ((efi_status == EFI_SUCCESS) && (auth_size == SHA256_DIGEST_SIZE))
MokAuth = 1;
efi_status = uefi_call_wrapper(RT->GetVariable, 5, L"MokDelAuth",
&shim_lock_guid,
&attributes, &auth_size, auth);
if ((efi_status == EFI_SUCCESS) && (auth_size == SHA256_DIGEST_SIZE))
MokDelAuth = 1;
if (MokNew || MokAuth)
menucount++;
if (MokDel || MokDelAuth)
menucount++;
if (MokSB)
menucount++;
if (MokPW)
menucount++;
menu_item = AllocateZeroPool(sizeof(struct menu_item) * menucount);
if (!menu_item)
return EFI_OUT_OF_RESOURCES;
menu_item[i].text = StrDuplicate(L"Continue boot");
menu_item[i].colour = EFI_WHITE;
menu_item[i].callback = NULL;
i++;
if (MokNew || MokAuth) {
if (!MokNew) {
menu_item[i].text = StrDuplicate(L"Reset MOK");
menu_item[i].colour = EFI_WHITE;
menu_item[i].callback = mok_reset_prompt;
} else {
menu_item[i].text = StrDuplicate(L"Enroll MOK");
menu_item[i].colour = EFI_WHITE;
menu_item[i].data = MokNew;
menu_item[i].data2 = (void *)MokNewSize;
menu_item[i].callback = mok_enrollment_prompt_callback;
}
i++;
}
if (MokDel || MokDelAuth) {
menu_item[i].text = StrDuplicate(L"Delete MOK");
menu_item[i].colour = EFI_WHITE;
menu_item[i].data = MokDel;
menu_item[i].data2 = (void *)MokDelSize;
menu_item[i].callback = mok_deletion_prompt;
i++;
}
if (MokSB) {
menu_item[i].text = StrDuplicate(L"Change Secure Boot state");
menu_item[i].colour = EFI_WHITE;
menu_item[i].callback = mok_sb_prompt;
menu_item[i].data = MokSB;
menu_item[i].data2 = (void *)MokSBSize;
i++;
}
if (MokPW) {
menu_item[i].text = StrDuplicate(L"Set MOK password");
menu_item[i].colour = EFI_WHITE;
menu_item[i].callback = mok_pw_prompt;
menu_item[i].data = MokPW;
menu_item[i].data2 = (void *)MokPWSize;
i++;
}
menu_item[i].text = StrDuplicate(L"Enroll key from disk");
menu_item[i].colour = EFI_WHITE;
menu_item[i].callback = find_fs;
menu_item[i].data3 = (void *)FALSE;
i++;
menu_item[i].text = StrDuplicate(L"Enroll hash from disk");
menu_item[i].colour = EFI_WHITE;
menu_item[i].callback = find_fs;
menu_item[i].data3 = (void *)TRUE;
i++;
run_menu(NULL, 0, menu_item, menucount, 10);
uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
return 0;
}
static EFI_STATUS check_mok_request(EFI_HANDLE image_handle)
{
EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
UINTN MokNewSize = 0, MokDelSize = 0, MokSBSize = 0, MokPWSize = 0;
void *MokNew = NULL;
void *MokDel = NULL;
void *MokSB = NULL;
void *MokPW = NULL;
MokNew = LibGetVariableAndSize(L"MokNew", &shim_lock_guid, &MokNewSize);
MokDel = LibGetVariableAndSize(L"MokDel", &shim_lock_guid, &MokDelSize);
MokSB = LibGetVariableAndSize(L"MokSB", &shim_lock_guid, &MokSBSize);
MokPW = LibGetVariableAndSize(L"MokPW", &shim_lock_guid, &MokPWSize);
enter_mok_menu(image_handle, MokNew, MokNewSize, MokDel, MokDelSize,
MokSB, MokSBSize, MokPW, MokPWSize);
if (MokNew) {
if (LibDeleteVariable(L"MokNew", &shim_lock_guid) != EFI_SUCCESS) {
Print(L"Failed to delete MokNew\n");
}
FreePool (MokNew);
}
if (MokDel) {
if (LibDeleteVariable(L"MokDel", &shim_lock_guid) != EFI_SUCCESS) {
Print(L"Failed to delete MokDel\n");
}
FreePool (MokDel);
}
if (MokSB) {
if (LibDeleteVariable(L"MokSB", &shim_lock_guid) != EFI_SUCCESS) {
Print(L"Failed to delete MokSB\n");
}
FreePool (MokNew);
}
if (MokPW) {
if (LibDeleteVariable(L"MokPW", &shim_lock_guid) != EFI_SUCCESS) {
Print(L"Failed to delete MokPW\n");
}
FreePool (MokNew);
}
LibDeleteVariable(L"MokAuth", &shim_lock_guid);
LibDeleteVariable(L"MokDelAuth", &shim_lock_guid);
return EFI_SUCCESS;
}
static VOID setup_console (int text)
{
EFI_STATUS status;
EFI_GUID console_control_guid = EFI_CONSOLE_CONTROL_PROTOCOL_GUID;
EFI_CONSOLE_CONTROL_PROTOCOL *concon;
static EFI_CONSOLE_CONTROL_SCREEN_MODE mode =
EfiConsoleControlScreenGraphics;
EFI_CONSOLE_CONTROL_SCREEN_MODE new_mode;
status = LibLocateProtocol(&console_control_guid, (VOID **)&concon);
if (status != EFI_SUCCESS)
return;
if (text) {
new_mode = EfiConsoleControlScreenText;
status = uefi_call_wrapper(concon->GetMode, 4, concon, &mode,
0, 0);
/* If that didn't work, assume it's graphics */
if (status != EFI_SUCCESS)
mode = EfiConsoleControlScreenGraphics;
} else {
new_mode = mode;
}
uefi_call_wrapper(concon->SetMode, 2, concon, new_mode);
}
static EFI_STATUS setup_rand (void)
{
EFI_TIME time;
EFI_STATUS efi_status;
UINT64 seed;
BOOLEAN status;
efi_status = uefi_call_wrapper(RT->GetTime, 2, &time, NULL);
if (efi_status != EFI_SUCCESS)
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_console(1);
setup_rand();
efi_status = check_mok_request(image_handle);
setup_console(0);
return efi_status;
}