summaryrefslogtreecommitdiff
path: root/shim.c
diff options
context:
space:
mode:
Diffstat (limited to 'shim.c')
-rw-r--r--shim.c286
1 files changed, 243 insertions, 43 deletions
diff --git a/shim.c b/shim.c
index 4437898a..633163a0 100644
--- a/shim.c
+++ b/shim.c
@@ -627,11 +627,13 @@ verify_buffer_authenticode (char *data, int datasize,
return EFI_SECURITY_VIOLATION;
}
- if (context->SecDir->Size >= size) {
+ if (checked_add(context->SecDir->Size, context->SecDir->VirtualAddress, &offset) ||
+ offset > size) {
perror(L"Certificate Database size is too large\n");
return EFI_INVALID_PARAMETER;
}
+ offset = 0;
ret_efi_status = EFI_NOT_FOUND;
do {
WIN_CERTIFICATE_EFI_PKCS *sig = NULL;
@@ -642,6 +644,12 @@ verify_buffer_authenticode (char *data, int datasize,
if (!sig)
break;
+ if ((uint64_t)(uintptr_t)&sig[1]
+ > (uint64_t)(uintptr_t)data + datasize) {
+ perror(L"Certificate size is too large for secruity database");
+ return EFI_INVALID_PARAMETER;
+ }
+
sz = offset + offsetof(WIN_CERTIFICATE_EFI_PKCS, Hdr.dwLength)
+ sizeof(sig->Hdr.dwLength);
if (sz > context->SecDir->Size) {
@@ -709,6 +717,12 @@ verify_buffer_sbat (char *data, int datasize,
Section = context->FirstSection;
for (i = 0; i < context->NumberOfSections; i++, Section++) {
+ if ((uint64_t)(uintptr_t)&Section[1]
+ > (uintptr_t)(uintptr_t)data + datasize) {
+ perror(L"Section exceeds bounds of image\n");
+ return EFI_UNSUPPORTED;
+ }
+
if (CompareMem(Section->Name, ".sbat\0\0\0", 8) != 0)
continue;
@@ -731,11 +745,17 @@ verify_buffer_sbat (char *data, int datasize,
* and ignore the section if it isn't. */
if (Section->SizeOfRawData &&
Section->SizeOfRawData >= Section->Misc.VirtualSize) {
+ uint64_t boundary;
SBATBase = ImageAddress(data, datasize,
Section->PointerToRawData);
SBATSize = Section->SizeOfRawData;
dprint(L"sbat section base:0x%lx size:0x%lx\n",
SBATBase, SBATSize);
+ if (checked_add((uint64_t)(uintptr_t)SBATBase, SBATSize, &boundary) ||
+ (boundary > (uint64_t)(uintptr_t)data + datasize)) {
+ perror(L"Section exceeds bounds of image\n");
+ return EFI_UNSUPPORTED;
+ }
}
}
@@ -753,11 +773,11 @@ verify_buffer (char *data, int datasize,
{
EFI_STATUS efi_status;
- efi_status = verify_buffer_sbat(data, datasize, context);
+ efi_status = verify_buffer_authenticode(data, datasize, context, sha256hash, sha1hash);
if (EFI_ERROR(efi_status))
return efi_status;
- return verify_buffer_authenticode(data, datasize, context, sha256hash, sha1hash);
+ return verify_buffer_sbat(data, datasize, context);
}
static int
@@ -1050,6 +1070,23 @@ restore_loaded_image(VOID)
CopyMem(shim_li, &shim_li_bak, sizeof(shim_li_bak));
}
+/* If gets used on static data it probably needs boundary checking */
+void
+str16_to_str8(CHAR16 *str16, CHAR8 **str8)
+{
+ int i = 0;
+
+ while (str16[i++] != '\0');
+ *str8 = (CHAR8 *)AllocatePool((i + 1) * sizeof (CHAR8));
+
+ i = 0;
+ while (str16[i] != '\0') {
+ (*str8)[i] = (CHAR8)str16[i];
+ i++;
+ }
+ (*str8)[i] = '\0';
+}
+
/*
* Load and run an EFI executable
*/
@@ -1059,6 +1096,7 @@ EFI_STATUS read_image(EFI_HANDLE image_handle, CHAR16 *ImagePath,
EFI_STATUS efi_status;
void *sourcebuffer = NULL;
UINT64 sourcesize = 0;
+ CHAR8 *netbootname;
/*
* We need to refer to the loaded image protocol on the running
@@ -1082,11 +1120,13 @@ EFI_STATUS read_image(EFI_HANDLE image_handle, CHAR16 *ImagePath,
}
if (findNetboot(shim_li->DeviceHandle)) {
- efi_status = parseNetbootinfo(image_handle);
+ str16_to_str8(ImagePath, &netbootname);
+ efi_status = parseNetbootinfo(image_handle, netbootname);
if (EFI_ERROR(efi_status)) {
perror(L"Netboot parsing failed: %r\n", efi_status);
return EFI_PROTOCOL_ERROR;
}
+ FreePool(netbootname);
efi_status = FetchNetbootimage(image_handle, &sourcebuffer,
&sourcesize);
if (EFI_ERROR(efi_status)) {
@@ -1097,12 +1137,14 @@ EFI_STATUS read_image(EFI_HANDLE image_handle, CHAR16 *ImagePath,
*data = sourcebuffer;
*datasize = sourcesize;
} else if (find_httpboot(shim_li->DeviceHandle)) {
+ str16_to_str8(ImagePath, &netbootname);
efi_status = httpboot_fetch_buffer (image_handle,
&sourcebuffer,
- &sourcesize);
+ &sourcesize,
+ netbootname);
if (EFI_ERROR(efi_status)) {
- perror(L"Unable to fetch HTTP image: %r\n",
- efi_status);
+ perror(L"Unable to fetch HTTP image %a: %r\n",
+ netbootname, efi_status);
return efi_status;
}
*data = sourcebuffer;
@@ -1207,7 +1249,7 @@ EFI_STATUS init_grub(EFI_HANDLE image_handle)
efi_status = start_image(image_handle, MOK_MANAGER);
if (EFI_ERROR(efi_status)) {
console_print(L"start_image() returned %r\n", efi_status);
- msleep(2000000);
+ usleep(2000000);
return efi_status;
}
@@ -1222,7 +1264,7 @@ EFI_STATUS init_grub(EFI_HANDLE image_handle)
console_print(
L"start_image() returned %r, falling back to default loader\n",
efi_status);
- msleep(2000000);
+ usleep(2000000);
load_options = NULL;
load_options_size = 0;
efi_status = start_image(image_handle, DEFAULT_LOADER);
@@ -1230,7 +1272,7 @@ EFI_STATUS init_grub(EFI_HANDLE image_handle)
if (EFI_ERROR(efi_status)) {
console_print(L"start_image() returned %r\n", efi_status);
- msleep(2000000);
+ usleep(2000000);
}
return efi_status;
@@ -1275,24 +1317,10 @@ EFI_STATUS set_second_stage (EFI_HANDLE image_handle)
return EFI_SUCCESS;
}
-static void *
-ossl_malloc(size_t num)
-{
- return AllocatePool(num);
-}
-
-static void
-ossl_free(void *addr)
-{
- FreePool(addr);
-}
-
static void
init_openssl(void)
{
- CRYPTO_set_mem_functions(ossl_malloc, NULL, ossl_free);
OPENSSL_init();
- CRYPTO_set_mem_functions(ossl_malloc, NULL, ossl_free);
ERR_load_ERR_strings();
ERR_load_BN_strings();
ERR_load_RSA_strings();
@@ -1391,6 +1419,96 @@ uninstall_shim_protocols(void)
#endif
}
+static void
+check_section_helper(char *section_name, int len, void **pointer,
+ EFI_IMAGE_SECTION_HEADER *Section, void *data,
+ int datasize, size_t minsize)
+{
+ if (CompareMem(Section->Name, section_name, len) == 0) {
+ *pointer = ImageAddress(data, datasize, Section->PointerToRawData);
+ if (Section->SizeOfRawData < minsize) {
+ dprint(L"found and rejected %.*a bad size\n", len, section_name);
+ dprint(L"minsize: %d\n", minsize);
+ dprint(L"rawsize: %d\n", Section->SizeOfRawData);
+ return ;
+ }
+ if (!*pointer) {
+ return ;
+ }
+ dprint(L"found %.*a\n", len, section_name);
+ }
+}
+
+#define check_section(section_name, pointer, section, data, datasize, minsize) \
+ check_section_helper(section_name, sizeof(section_name) - 1, pointer, \
+ section, data, datasize, minsize)
+
+EFI_STATUS
+load_revocations_file(EFI_HANDLE image_handle, CHAR16 *PathName)
+{
+ EFI_STATUS efi_status = EFI_SUCCESS;
+ PE_COFF_LOADER_IMAGE_CONTEXT context;
+ EFI_IMAGE_SECTION_HEADER *Section;
+ int datasize = 0;
+ void *data = NULL;
+ unsigned int i;
+ char *sbat_var_automatic = NULL;
+ char *sbat_var_latest = NULL;
+ uint8_t *ssps_automatic = NULL;
+ uint8_t *sspv_automatic = NULL;
+ uint8_t *ssps_latest = NULL;
+ uint8_t *sspv_latest = NULL;
+
+ efi_status = read_image(image_handle, L"revocations.efi", &PathName,
+ &data, &datasize);
+ if (EFI_ERROR(efi_status))
+ return efi_status;
+
+ efi_status = verify_image(data, datasize, shim_li, &context);
+ if (EFI_ERROR(efi_status)) {
+ dprint(L"revocations failed to verify\n");
+ return efi_status;
+ }
+ dprint(L"verified revocations\n");
+
+ Section = context.FirstSection;
+ for (i = 0; i < context.NumberOfSections; i++, Section++) {
+ dprint(L"checking section \"%c%c%c%c%c%c%c%c\"\n", (char *)Section->Name);
+ check_section(".sbata\0\0", (void **)&sbat_var_automatic, Section,
+ data, datasize, sizeof(SBAT_VAR_ORIGINAL));
+ check_section(".sbatl\0\0", (void **)&sbat_var_latest, Section,
+ data, datasize, sizeof(SBAT_VAR_ORIGINAL));
+ check_section(".sspva\0\0", (void **)&sspv_automatic, Section,
+ data, datasize, SSPVER_SIZE);
+ check_section(".sspsa\0\0", (void **)&ssps_automatic, Section,
+ data, datasize, SSPSIG_SIZE);
+ check_section(".sspvl\0\0", (void **)&sspv_latest, Section,
+ data, datasize, SSPVER_SIZE);
+ check_section(".sspsl\0\0", (void **)&ssps_latest, Section,
+ data, datasize, SSPSIG_SIZE);
+ }
+
+ if (sbat_var_latest && sbat_var_automatic) {
+ dprint(L"attempting to update SBAT_LEVEL\n");
+ efi_status = set_sbat_uefi_variable(sbat_var_automatic,
+ sbat_var_latest);
+ } else {
+ dprint(L"no data for SBAT_LEVEL\n");
+ }
+
+ if ((sspv_automatic && ssps_automatic) || (sspv_latest && ssps_latest)) {
+ dprint(L"attempting to update SkuSiPolicy\n");
+ efi_status = set_ssp_uefi_variable(sspv_automatic, ssps_automatic,
+ sspv_latest, ssps_latest);
+
+ } else {
+ dprint(L"no data for SkuSiPolicy\n");
+ }
+
+ FreePool(data);
+ return efi_status;
+}
+
EFI_STATUS
load_cert_file(EFI_HANDLE image_handle, CHAR16 *filename, CHAR16 *PathName)
{
@@ -1437,9 +1555,12 @@ load_cert_file(EFI_HANDLE image_handle, CHAR16 *filename, CHAR16 *PathName)
return EFI_SUCCESS;
}
-/* Read additional certificates from files (after verifying signatures) */
+/*
+ * Read additional certificates and SBAT Level requirements from files
+ * (after verifying signatures)
+ */
EFI_STATUS
-load_certs(EFI_HANDLE image_handle)
+load_unbundled_trust(EFI_HANDLE image_handle)
{
EFI_STATUS efi_status;
EFI_LOADED_IMAGE *li = NULL;
@@ -1450,6 +1571,7 @@ load_certs(EFI_HANDLE image_handle)
EFI_FILE_IO_INTERFACE *drive;
UINTN buffersize = 0;
void *buffer = NULL;
+ BOOLEAN search_revocations = TRUE;
efi_status = gBS->HandleProtocol(image_handle, &EFI_LOADED_IMAGE_GUID,
(void **)&li);
@@ -1466,7 +1588,15 @@ load_certs(EFI_HANDLE image_handle)
efi_status = gBS->HandleProtocol(device, &EFI_SIMPLE_FILE_SYSTEM_GUID,
(void **)&drive);
if (EFI_ERROR(efi_status)) {
- perror(L"Failed to find fs: %r\n", efi_status);
+ dprint(L"Failed to find fs on local drive (netboot?): %r \n",
+ efi_status);
+ /*
+ * Network boot cases do not support reading a directory. Try
+ * to read revocations.efi to pull in any unbundled SBATLevel
+ * updates unconditionally in those cases. This may produce
+ * console noise when the file is not present.
+ */
+ load_cert_file(image_handle, REVOCATIONFILE, PathName);
goto done;
}
@@ -1482,23 +1612,83 @@ load_certs(EFI_HANDLE image_handle)
goto done;
}
- while (1) {
- int old = buffersize;
+ if (!secure_mode())
+ goto done;
+
+
+ while (true) {
+ UINTN old = buffersize;
+
efi_status = dir->Read(dir, &buffersize, buffer);
if (efi_status == EFI_BUFFER_TOO_SMALL) {
+ if (buffersize == old) {
+ /*
+ * Some UEFI drivers or firmwares are not compliant with
+ * the EFI_FILE_PROTOCOL.Read() specs and do not return the
+ * required buffer size along with EFI_BUFFER_TOO_SMALL.
+ * Work around this by progressively increasing the buffer
+ * size, up to a certain point, until the call succeeds.
+ */
+ perror(L"Error reading directory %s - non-compliant UEFI driver or firmware!\n",
+ PathName);
+ buffersize = (buffersize < 4) ? 4 : buffersize * 2;
+ if (buffersize > 1024)
+ goto done;
+ }
buffer = ReallocatePool(buffer, old, buffersize);
+ if (buffer == NULL) {
+ perror(L"Failed to read directory %s - %r\n",
+ PathName, EFI_OUT_OF_RESOURCES);
+ goto done;
+ }
continue;
} else if (EFI_ERROR(efi_status)) {
perror(L"Failed to read directory %s - %r\n", PathName,
- efi_status);
+ efi_status);
goto done;
}
info = (EFI_FILE_INFO *)buffer;
- if (buffersize == 0 || !info)
- goto done;
+ if (buffersize == 0 || !info) {
+ if (search_revocations) {
+ search_revocations = FALSE;
+ efi_status = root->Open(root, &dir, PathName,
+ EFI_FILE_MODE_READ, 0);
+ if (EFI_ERROR(efi_status)) {
+ perror(L"Failed to open %s - %r\n",
+ PathName, efi_status);
+ goto done;
+ }
+ continue;
+ } else {
+ goto done;
+ }
+ }
- if (StrnCaseCmp(info->FileName, L"shim_certificate", 16) == 0) {
+ /*
+ * In the event that there are unprocessed revocation
+ * additions, they could be intended to ban any *new* trust
+ * anchors we find here. With that in mind, we always want to
+ * do a pass of loading revocations before we try to add
+ * anything new to our allowlist. This is done by making two
+ * passes over the directory, first to search for the
+ * revocations.efi file then to search for shim_certificate.efi
+ */
+ if (search_revocations &&
+ StrCaseCmp(info->FileName, REVOCATIONFILE) == 0) {
+ load_revocations_file(image_handle, PathName);
+ search_revocations = FALSE;
+ efi_status = root->Open(root, &dir, PathName,
+ EFI_FILE_MODE_READ, 0);
+ if (EFI_ERROR(efi_status)) {
+ perror(L"Failed to open %s - %r\n",
+ PathName, efi_status);
+ goto done;
+ }
+ }
+
+ if (!search_revocations &&
+ StrCaseCmp(info->FileName, L"shim_certificate.efi") == 0) {
load_cert_file(image_handle, info->FileName, PathName);
}
}
@@ -1637,7 +1827,7 @@ devel_egress(devel_egress_action action UNUSED)
console_print(L"Waiting to %a...", reasons[action]);
for (size_t sleepcount = 0; sleepcount < 10; sleepcount++) {
console_print(L"%d...", 10 - sleepcount);
- msleep(1000000);
+ usleep(1000000);
}
console_print(L"\ndoing %a\n", action);
@@ -1708,7 +1898,7 @@ efi_main (EFI_HANDLE passed_image_handle, EFI_SYSTEM_TABLE *passed_systab)
*/
debug_hook();
- efi_status = set_sbat_uefi_variable();
+ efi_status = set_sbat_uefi_variable_internal();
if (EFI_ERROR(efi_status) && secure_mode()) {
perror(L"%s variable initialization failed\n", SBAT_VAR_NAME);
msg = SET_SBAT;
@@ -1717,13 +1907,19 @@ efi_main (EFI_HANDLE passed_image_handle, EFI_SYSTEM_TABLE *passed_systab)
dprint(L"%s variable initialization failed: %r\n",
SBAT_VAR_NAME, efi_status);
}
+ efi_status = set_ssp_uefi_variable_internal();
+ if (EFI_ERROR(efi_status)) {
+ dprint(L"%s variable initialization failed: %r\n",
+ SSPVER_VAR_NAME, efi_status);
+ }
+ dprint(L"%s variable initialization done\n", SSPVER_VAR_NAME);
if (secure_mode()) {
char *sbat_start = (char *)&_sbat;
char *sbat_end = (char *)&_esbat;
INIT_LIST_HEAD(&sbat_var);
- efi_status = parse_sbat_var(&sbat_var);
+ efi_status = parse_sbat_var(&sbat_var, NULL);
if (EFI_ERROR(efi_status)) {
perror(L"Parsing %s variable failed: %r\n",
SBAT_VAR_NAME, efi_status);
@@ -1743,11 +1939,9 @@ efi_main (EFI_HANDLE passed_image_handle, EFI_SYSTEM_TABLE *passed_systab)
init_openssl();
- if (secure_mode()) {
- efi_status = load_certs(global_image_handle);
- if (EFI_ERROR(efi_status)) {
- LogError(L"Failed to load addon certificates\n");
- }
+ efi_status = load_unbundled_trust(global_image_handle);
+ if (EFI_ERROR(efi_status)) {
+ LogError(L"Failed to load addon certificates / sbat level\n");
}
/*
@@ -1775,12 +1969,18 @@ die:
#if defined(ENABLE_SHIM_DEVEL)
devel_egress(COLD_RESET);
#else
- msleep(5000000);
+ usleep(5000000);
RT->ResetSystem(EfiResetShutdown, EFI_SECURITY_VIOLATION,
0, NULL);
#endif
}
+ /*
+ * This variable is supposed to be set by second stages, so ensure it is
+ * not set when we are starting up.
+ */
+ (void) del_variable(SHIM_RETAIN_PROTOCOL_VAR_NAME, SHIM_LOCK_GUID);
+
efi_status = shim_init();
if (EFI_ERROR(efi_status)) {
msg = SHIM_INIT;
@@ -1792,7 +1992,7 @@ die:
*/
if (user_insecure_mode) {
console_print(L"Booting in insecure mode\n");
- msleep(2000000);
+ usleep(2000000);
}
/*