diff --git a/include/sbat.h b/include/sbat.h index eaac2ea22..1aaeb80a1 100644 --- a/include/sbat.h +++ b/include/sbat.h @@ -8,6 +8,13 @@ #include "shim.h" +struct sbat_var { + const CHAR8 *component_generation; + const CHAR8 *component_name_size; + const CHAR8 *component_name; + struct sbat_var *next; +}; + struct sbat_entry { const CHAR8 *component_name; const CHAR8 *component_generation; @@ -22,6 +29,15 @@ struct sbat { struct sbat_entry **entries; }; +struct sbat_var* parse_sbat_var(); + +struct sbat_var* add_entry(struct sbat_var*, const CHAR8 *comp_gen, + const CHAR8 *comp_name_size, const CHAR8 *comp_name); + +struct sbat_var* new_entry(const CHAR8 *comp_gen, const CHAR8 *comp_name_size, + const CHAR8 *comp_name); +EFI_STATUS verify_sbat (struct sbat *sbat, struct sbat_var *sbat_var); + EFI_STATUS parse_sbat(char *sbat_base, size_t sbat_size, char *buffer, struct sbat *sbat); diff --git a/pe.c b/pe.c index acc02bb88..dced525b8 100644 --- a/pe.c +++ b/pe.c @@ -1043,6 +1043,7 @@ handle_image (void *data, unsigned int datasize, unsigned int i; struct sbat sbat = { 0 }; struct sbat_entry *entry = NULL; + struct sbat_var *var = NULL; if (SBATBase && SBATSize) { char *sbat_data; @@ -1074,17 +1075,30 @@ handle_image (void *data, unsigned int datasize, entry->vendor_version, entry->vendor_url); } + var = parse_sbat_var(); + if (var == NULL) + console_print(L"SBAT variable not read"); } else { perror(L"SBAT data not found\n"); return EFI_UNSUPPORTED; } - efi_status = verify_buffer(data, datasize, &context, sha256hash, sha1hash); - + if (sbat.entries && var ) + efi_status = verify_sbat(&sbat, var); + /* Add EFI_SECURITY_VIOLATION if variable is set, + and binary has no SBAT section ?*/ if (sbat.entries) for (i = 0; i < sbat.size; i++) FreePool(sbat.entries[i]); + if (var) { + while (var != NULL) { + struct sbat_var* tmp; + tmp = var; + var = var->next; + FreePool(tmp); + } + } if (EFI_ERROR(efi_status)) { if (verbose) diff --git a/sbat.c b/sbat.c index 3f587c104..7efcb3e6a 100644 --- a/sbat.c +++ b/sbat.c @@ -4,6 +4,7 @@ */ #include "sbat.h" +#include CHAR8 * get_sbat_field(CHAR8 *current, CHAR8 *end, const CHAR8 ** field, char delim) @@ -120,4 +121,98 @@ EFI_STATUS parse_sbat(char *sbat_base, size_t sbat_size, char *buffer, return efi_status; } +EFI_STATUS verify_sbat (struct sbat *sbat, struct sbat_var *sbat_var_root) +{ + unsigned int i; + struct sbat_entry *entry = NULL; + for (i = 0; i < sbat->size; i++) { + entry = sbat->entries[i]; + struct sbat_var *sbat_var_entry = sbat_var_root; + while (sbat_var_entry != NULL) { + if (strcmp(entry->component_name,sbat_var_entry->component_name) == 0) { + dprint(L"component %a has a matching SBAT variable entry, verifying\n", entry->component_name); + /* atoi returns zero for failed conversion, so essentially + badly parsed component_generation will be treated as zero + */ + if (atoi(entry->component_generation) < atoi(sbat_var_entry->component_generation)) { + dprint(L"component's %a generation: %d. Conflicts with SBAT variable generation %d\n", + entry->component_name, + atoi(entry->component_generation), + atoi(sbat_var_entry->component_generation)); + LogError(L"image did not pass SBAT verification\n"); + return EFI_SECURITY_VIOLATION; + } + } + sbat_var_entry = sbat_var_entry->next; + } + } + dprint(L"all entries from SBAT section verified\n"); + return EFI_SUCCESS; +} + +static BOOLEAN is_utf8_bom(CHAR8 *buf) +{ + unsigned char bom[] = { 0xEF,0xBB,0xBF }; + return !!CompareMem(buf, bom, MIN(sizeof(bom), sizeof(buf))); +} + +struct sbat_var* new_entry(const CHAR8 *comp_gen, const CHAR8 *comp_name_size, + const CHAR8 *comp_name) +{ + struct sbat_var *new_entry = AllocatePool(sizeof(*new_entry)); + new_entry->next = NULL; + new_entry->component_generation = comp_gen; + new_entry->component_name_size = comp_name_size; + new_entry->component_name = comp_name; + return new_entry; +} + +struct sbat_var* add_entry(struct sbat_var *n, const CHAR8 *comp_gen, const CHAR8 *comp_name_size, + const CHAR8 *comp_name) +{ + if ( n == NULL ) + return NULL; + while (n->next) + n = n->next; + return (n->next = new_entry(comp_gen, comp_name_size, comp_name)); +} + +struct sbat_var* parse_sbat_var() +{ + UINT8 *data = 0; + UINTN datasize; + EFI_STATUS efi_status; + + efi_status = get_variable(L"SBAT", &data, &datasize, SHIM_LOCK_GUID); + if (EFI_ERROR(efi_status)) { + return NULL; + } + + struct sbat_var *root = new_entry((CHAR8 *)"0",(CHAR8 *)"0",(CHAR8 *)"entries"); + struct sbat_var *nodename = root; + CHAR8 *start = (CHAR8 *) data; + CHAR8 *end = (CHAR8 *) data + datasize; + while ((*end == '\r' || *end == '\n') && end < start) + end--; + *end = '\0'; + if (is_utf8_bom(start)) + start += 3; + dprint(L"SBAT variable data:\n"); + while (start[0] != '\0') { + const CHAR8 *comp_name_size, *comp_gen, *comp_name; + + start = get_sbat_field(start, end, &comp_gen, ','); + + start = get_sbat_field(start, end, &comp_name_size, ','); + + start = get_sbat_field(start, end, &comp_name, '\n'); + + dprint(L"component %a with generation %a\n",comp_name, comp_gen); + + add_entry(nodename, comp_gen, comp_name_size, comp_name); + nodename = nodename->next; + } + return root; +} + // vim:fenc=utf-8:tw=75:noet