diff options
| author | Josh Boyer <jwboyer@fedoraproject.org> | 2013-10-01 11:49:22 -0400 |
|---|---|---|
| committer | Peter Jones <pjones@redhat.com> | 2013-10-02 11:29:34 -0400 |
| commit | ef0383d008aa2114392f27125601514ae97b16a1 (patch) | |
| tree | e7ad226ad04a7d2b869af495ff2f360b7e5880fc | |
| parent | 91c5a05037598425757b3a9ab76619ff2f48fc20 (diff) | |
| download | efi-boot-shim-ef0383d008aa2114392f27125601514ae97b16a1.tar.gz efi-boot-shim-ef0383d008aa2114392f27125601514ae97b16a1.zip | |
Add support for disabling db for verification
Provide a mechanism for a physically present end user to disable the use
of db when doing signature verification. This is handled by the OS passing
down a variable that contains a UINT32 and a SHA256 hash. If this variable
is present, MokManager prompts the user to choose whether to enable or
disable the use of db for verification purposes (depending on the value of
the UINT32). They are then asked to type the passphrase that matches the
hash. This then saves a boot services variable which is checked by shim,
and if set will cause shim to not use db for verification purposes. If
db is to be ignored, shim will export a runtime variable called
'MokIgnoreDB' for the OS to query at runtime.
Signed-off-by: Josh Boyer <jwboyer@fedoraproject.org>
| -rw-r--r-- | MokManager.c | 153 | ||||
| -rw-r--r-- | MokVars.txt | 23 | ||||
| -rw-r--r-- | shim.c | 107 |
3 files changed, 264 insertions, 19 deletions
diff --git a/MokManager.c b/MokManager.c index de0eb59a..f5ed379c 100644 --- a/MokManager.c +++ b/MokManager.c @@ -50,6 +50,12 @@ typedef struct { CHAR16 Password[SB_PASSWORD_LEN]; } __attribute__ ((packed)) MokSBvar; +typedef struct { + UINT32 MokDBState; + UINT32 PWLen; + CHAR16 Password[SB_PASSWORD_LEN]; +} __attribute__ ((packed)) MokDBvar; + static EFI_STATUS get_sha1sum (void *Data, int DataSize, UINT8 *hash) { EFI_STATUS status; @@ -1116,6 +1122,118 @@ static INTN mok_sb_prompt (void *MokSB, UINTN MokSBSize) { return -1; } +static INTN mok_db_prompt (void *MokDB, UINTN MokDBSize) { + EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; + 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; + + if (MokDBSize != sizeof(MokDBvar)) { + console_notify(L"Invalid MokDB variable contents"); + return -1; + } + + uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut); + + 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 -1; + } + 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 -1; + } + 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 -1; + } + pass3 = get_password_charater(str); + FreePool(str); + + 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) { + console_notify(L"Password limit reached"); + return -1; + } + + if (var->MokDBState == 0) + ret = console_yes_no((CHAR16 *[]){L"Ignore DB certs/hashes", NULL}); + else + ret = console_yes_no((CHAR16 *[]){L"Use DB certs/hashes", NULL}); + + if (ret == 0) { + LibDeleteVariable(L"MokDB", &shim_lock_guid); + return -1; + } + + if (var->MokDBState == 0) { + efi_status = uefi_call_wrapper(RT->SetVariable, + 5, L"MokDBState", + &shim_lock_guid, + EFI_VARIABLE_NON_VOLATILE | + EFI_VARIABLE_BOOTSERVICE_ACCESS, + 1, &dbval); + if (efi_status != EFI_SUCCESS) { + console_notify(L"Failed to set DB state"); + return -1; + } + } else { + LibDeleteVariable(L"MokDBState", &shim_lock_guid); + } + + console_notify(L"The system must now be rebooted"); + uefi_call_wrapper(RT->ResetSystem, 4, EfiResetWarm, + EFI_SUCCESS, 0, NULL); + console_notify(L"Failed to reboot"); + return -1; +} + static INTN mok_pw_prompt (void *MokPW, UINTN MokPWSize) { EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; EFI_STATUS efi_status; @@ -1517,6 +1635,7 @@ typedef enum { MOK_DELETE_MOK, MOK_CHANGE_SB, MOK_SET_PW, + MOK_CHANGE_DB, MOK_KEY_ENROLL, MOK_HASH_ENROLL } mok_menu_item; @@ -1525,7 +1644,8 @@ 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) + void *MokPW, UINTN MokPWSize, + void *MokDB, UINTN MokDBSize) { CHAR16 **menu_strings; mok_menu_item *menu_item; @@ -1572,6 +1692,9 @@ static EFI_STATUS enter_mok_menu(EFI_HANDLE image_handle, if (MokPW) menucount++; + if (MokDB) + menucount++; + menu_strings = AllocateZeroPool(sizeof(CHAR16 *) * (menucount + 1)); if (!menu_strings) @@ -1618,6 +1741,12 @@ static EFI_STATUS enter_mok_menu(EFI_HANDLE image_handle, i++; } + if (MokDB) { + menu_strings[i] = L"Change DB state"; + menu_item[i] = MOK_CHANGE_DB; + i++; + } + menu_strings[i] = L"Enroll key from disk"; menu_item[i] = MOK_KEY_ENROLL; i++; @@ -1656,6 +1785,9 @@ static EFI_STATUS enter_mok_menu(EFI_HANDLE image_handle, case MOK_SET_PW: mok_pw_prompt(MokPW, MokPWSize); break; + case MOK_CHANGE_DB: + mok_db_prompt(MokDB, MokDBSize); + break; case MOK_KEY_ENROLL: mok_key_enroll(); break; @@ -1679,11 +1811,13 @@ out: 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; + UINTN MokNewSize = 0, MokDelSize = 0, MokSBSize = 0, MokPWSize = 0, + MokDBSize = 0; void *MokNew = NULL; void *MokDel = NULL; void *MokSB = NULL; void *MokPW = NULL; + void *MokDB = NULL; EFI_STATUS status; status = get_variable(L"MokNew", (UINT8 **)&MokNew, &MokNewSize, @@ -1726,8 +1860,18 @@ static EFI_STATUS check_mok_request(EFI_HANDLE image_handle) console_error(L"Could not retrieve MokPW", status); } + status = get_variable(L"MokDB", (UINT8 **)&MokDB, &MokDBSize, + shim_lock_guid); + if (status == EFI_SUCCESS) { + if (LibDeleteVariable(L"MokDB", &shim_lock_guid) != EFI_SUCCESS) { + console_notify(L"Failed to delete MokDB"); + } + } else if (EFI_ERROR(status) && status != EFI_NOT_FOUND) { + console_error(L"Could not retrieve MokDB", status); + } + enter_mok_menu(image_handle, MokNew, MokNewSize, MokDel, MokDelSize, - MokSB, MokSBSize, MokPW, MokPWSize); + MokSB, MokSBSize, MokPW, MokPWSize, MokDB, MokDBSize); if (MokNew) FreePool (MokNew); @@ -1741,6 +1885,9 @@ static EFI_STATUS check_mok_request(EFI_HANDLE image_handle) if (MokPW) FreePool (MokPW); + if (MokDB) + FreePool (MokDB); + LibDeleteVariable(L"MokAuth", &shim_lock_guid); LibDeleteVariable(L"MokDelAuth", &shim_lock_guid); diff --git a/MokVars.txt b/MokVars.txt index 74f09083..cac5349f 100644 --- a/MokVars.txt +++ b/MokVars.txt @@ -25,6 +25,23 @@ three randomly chosen characters from the password. If successful, they will then be prompted to change the signature validation according to MokSBState. BS,RT,NV +MokDB: Set by MokUtil when requesting a change in state of validation +using db hashes and certs. A packed structure as follows: + +typedef struct { + UINT32 MokDBState; + UINT32 PWLen; + CHAR16 Password[PASSWORD_MAX]; +} __attribute__ ((packed)) MokDBvar; + +If MokDBState is 0, the user will be prompted to disable usage of db for +validation. Otherwise, the user will be prompted to allow it. PWLen +is the length of the password, in characters. Password is a UCS-2 +representation of the password. The user will be prompted to enter +three randomly chosen characters from the password. If successful, +they will then be prompted to change the signature validation +according to MokDBState. BS,RT,NV + MokNew: Set by MokUtil when requesting the addition or removal of keys from MokList. Is an EFI_SIGNATURE_LIST as described in the UEFI specification. BS,RT,NV @@ -46,6 +63,12 @@ MokListRT: A copy of MokList made available to the kernel at runtime. RT MokSBState: An 8-bit unsigned integer. If 1, shim will switch to insecure mode. BS,NV +MokDBState: An 8-bit unsigned integer. If 1, shim will not use db for +verification. BS,NV + +MokIgnoreDB: An 8-bit unsigned integer. This allows the OS to query whether +or not to import DB certs for its own verification purposes. + MokPWStore: A SHA-256 representation of the password set by the user via MokPW. The user will be prompted to enter this password in order to interact with MokManager. @@ -86,6 +86,7 @@ int loader_is_participating; #define EFI_IMAGE_SECURITY_DATABASE_GUID { 0xd719b2cb, 0x3d3a, 0x4596, { 0xa3, 0xbc, 0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f }} UINT8 insecure_mode; +UINT8 ignore_db; typedef enum { DATA_FOUND, @@ -393,28 +394,31 @@ static EFI_STATUS check_whitelist (WIN_CERTIFICATE_EFI_PKCS *cert, EFI_GUID secure_var = EFI_IMAGE_SECURITY_DATABASE_GUID; EFI_GUID shim_var = SHIM_LOCK_GUID; - if (check_db_hash(L"db", secure_var, sha256hash, SHA256_DIGEST_SIZE, - EFI_CERT_SHA256_GUID) == DATA_FOUND) { - update_verification_method(VERIFIED_BY_HASH); - return EFI_SUCCESS; - } - if (check_db_hash(L"db", secure_var, sha1hash, SHA1_DIGEST_SIZE, - EFI_CERT_SHA1_GUID) == DATA_FOUND) { - verification_method = VERIFIED_BY_HASH; - update_verification_method(VERIFIED_BY_HASH); - return EFI_SUCCESS; + if (!ignore_db) { + if (check_db_hash(L"db", secure_var, sha256hash, SHA256_DIGEST_SIZE, + EFI_CERT_SHA256_GUID) == DATA_FOUND) { + update_verification_method(VERIFIED_BY_HASH); + return EFI_SUCCESS; + } + if (check_db_hash(L"db", secure_var, sha1hash, SHA1_DIGEST_SIZE, + EFI_CERT_SHA1_GUID) == DATA_FOUND) { + verification_method = VERIFIED_BY_HASH; + update_verification_method(VERIFIED_BY_HASH); + return EFI_SUCCESS; + } + if (check_db_cert(L"db", secure_var, cert, sha256hash) == DATA_FOUND) { + verification_method = VERIFIED_BY_CERT; + update_verification_method(VERIFIED_BY_CERT); + return EFI_SUCCESS; + } } + if (check_db_hash(L"MokList", shim_var, sha256hash, SHA256_DIGEST_SIZE, EFI_CERT_SHA256_GUID) == DATA_FOUND) { verification_method = VERIFIED_BY_HASH; update_verification_method(VERIFIED_BY_HASH); return EFI_SUCCESS; } - if (check_db_cert(L"db", secure_var, cert, sha256hash) == DATA_FOUND) { - verification_method = VERIFIED_BY_CERT; - update_verification_method(VERIFIED_BY_CERT); - return EFI_SUCCESS; - } if (check_db_cert(L"MokList", shim_var, cert, sha256hash) == DATA_FOUND) { verification_method = VERIFIED_BY_CERT; update_verification_method(VERIFIED_BY_CERT); @@ -1428,7 +1432,7 @@ EFI_STATUS check_mok_request(EFI_HANDLE image_handle) if (check_var(L"MokNew") || check_var(L"MokSB") || check_var(L"MokPW") || check_var(L"MokAuth") || - check_var(L"MokDel")) { + check_var(L"MokDel") || check_var(L"MokDB")) { efi_status = start_image(image_handle, MOK_MANAGER); if (efi_status != EFI_SUCCESS) { @@ -1482,6 +1486,71 @@ static EFI_STATUS check_mok_sb (void) } /* + * Verify that MokDBState is valid, and if appropriate set ignore db mode + */ + +static EFI_STATUS check_mok_db (void) +{ + EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; + EFI_STATUS status = EFI_SUCCESS; + UINT8 *MokDBState = NULL; + UINTN MokDBStateSize = 0; + UINT32 attributes; + + status = get_variable_attr(L"MokDBState", &MokDBState, &MokDBStateSize, + shim_lock_guid, &attributes); + + if (status != EFI_SUCCESS) + return EFI_ACCESS_DENIED; + + ignore_db = 0; + + /* + * Delete and ignore the variable if it's been set from or could be + * modified by the OS + */ + if (attributes & EFI_VARIABLE_RUNTIME_ACCESS) { + Print(L"MokDBState is compromised! Clearing it\n"); + if (LibDeleteVariable(L"MokDBState", &shim_lock_guid) != EFI_SUCCESS) { + Print(L"Failed to erase MokDBState\n"); + } + status = EFI_ACCESS_DENIED; + } else { + if (*(UINT8 *)MokDBState == 1) { + ignore_db = 1; + } + } + + FreePool(MokDBState); + + return status; +} + +static EFI_STATUS mok_ignore_db() +{ + EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; + EFI_STATUS efi_status = EFI_SUCCESS; + UINT8 Data = 1; + UINTN DataSize = sizeof(UINT8); + + check_mok_db(); + + if (ignore_db) { + efi_status = uefi_call_wrapper(RT->SetVariable, 5, L"MokIgnoreDB", + &shim_lock_guid, + EFI_VARIABLE_BOOTSERVICE_ACCESS + | EFI_VARIABLE_RUNTIME_ACCESS, + DataSize, (void *)&Data); + if (efi_status != EFI_SUCCESS) { + Print(L"Failed to set MokIgnoreDB %d\n", efi_status); + } + } + + return efi_status; + +} + +/* * Check the load options to specify the second stage loader */ EFI_STATUS set_second_stage (EFI_HANDLE image_handle) @@ -1653,6 +1722,12 @@ EFI_STATUS efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *passed_systab) efi_status = mirror_mok_list(); /* + * Create the runtime MokIgnoreDB variable so the kernel can make + * use of it + */ + efi_status = mok_ignore_db(); + + /* * Hand over control to the second stage bootloader */ |
