diff options
author | Steve McIntyre <steve@einval.com> | 2021-03-23 23:49:46 +0000 |
---|---|---|
committer | Steve McIntyre <steve@einval.com> | 2021-03-23 23:49:46 +0000 |
commit | 1251a7ba86fc40a6aad8b4fecdbca2b61808d9fa (patch) | |
tree | 2125fda549aaca55cb49a48d54be77dec7fbf3df /sbat.c | |
parent | 85b409232ce89b34626df9d72abedf5d4f5ccef6 (diff) | |
parent | 031e5cce385d3f96b1caa1d53495332a7eb03749 (diff) | |
download | efi-boot-shim-debian/15.3-1.tar.gz efi-boot-shim-debian/15.3-1.zip |
Update upstream source from tag 'upstream/15.3'debian/15.3-1
Update to upstream version '15.3'
with Debian dir 1b484f1c1ac270604a5a1451b34de4b0865c6211
Diffstat (limited to 'sbat.c')
-rw-r--r-- | sbat.c | 493 |
1 files changed, 267 insertions, 226 deletions
@@ -4,157 +4,111 @@ */ #include "shim.h" -#include "string.h" - -CHAR8 * -get_sbat_field(CHAR8 *current, CHAR8 *end, const CHAR8 **field, char delim) -{ - CHAR8 *offset; - - if (!field || !current || !end || current >= end) - return NULL; - - offset = strchrnula(current, delim); - *field = current; - - if (!offset || !*offset) - return NULL; - - *offset = '\0'; - return offset + 1; -} EFI_STATUS -parse_sbat_entry(CHAR8 **current, CHAR8 *end, struct sbat_entry **sbat_entry) +parse_sbat_section(char *section_base, size_t section_size, + size_t *n_entries, + struct sbat_section_entry ***entriesp) { - struct sbat_entry *entry = NULL; + struct sbat_section_entry *entry = NULL, **entries; + EFI_STATUS efi_status = EFI_SUCCESS; + list_t csv, *pos = NULL; + char * end = section_base + section_size - 1; + size_t allocsz = 0; + size_t n; + char *strtab; - entry = AllocateZeroPool(sizeof(*entry)); - if (!entry) - return EFI_OUT_OF_RESOURCES; + if (!section_base || !section_size || !n_entries || !entriesp) + return EFI_INVALID_PARAMETER; - *current = get_sbat_field(*current, end, &entry->component_name, ','); - if (!entry->component_name) - goto error; + INIT_LIST_HEAD(&csv); - *current = get_sbat_field(*current, end, &entry->component_generation, - ','); - if (!entry->component_generation) - goto error; + efi_status = + parse_csv_data(section_base, end, SBAT_SECTION_COLUMNS, &csv); + if (EFI_ERROR(efi_status)) { + return efi_status; + } + + n = 0; + list_for_each(pos, &csv) { + struct csv_row * row; + size_t i; - *current = get_sbat_field(*current, end, &entry->vendor_name, ','); - if (!entry->vendor_name) - goto error; + row = list_entry(pos, struct csv_row, list); - *current = - get_sbat_field(*current, end, &entry->vendor_package_name, ','); - if (!entry->vendor_package_name) - goto error; + if (row->n_columns < SBAT_SECTION_COLUMNS) { + efi_status = EFI_INVALID_PARAMETER; + goto err; + } - *current = get_sbat_field(*current, end, &entry->vendor_version, ','); - if (!entry->vendor_version) - goto error; + allocsz += sizeof(struct sbat_section_entry *); + allocsz += sizeof(struct sbat_section_entry); + for (i = 0; i < row->n_columns; i++) { + if (row->columns[i][0] == '\000') { + efi_status = EFI_INVALID_PARAMETER; + goto err; + } + allocsz += strlen(row->columns[i]) + 1; + } + n++; + } - *current = get_sbat_field(*current, end, &entry->vendor_url, '\n'); - if (!entry->vendor_url) - goto error; + strtab = AllocateZeroPool(allocsz); + if (!strtab) { + efi_status = EFI_OUT_OF_RESOURCES; + goto err; + } - *sbat_entry = entry; + entries = (struct sbat_section_entry **)strtab; + strtab += sizeof(struct sbat_section_entry *) * n; + entry = (struct sbat_section_entry *)strtab; + strtab += sizeof(struct sbat_section_entry) * n; + n = 0; + + list_for_each(pos, &csv) { + struct csv_row * row; + size_t i; + const char **ptrs[] = { + &entry->component_name, + &entry->component_generation, + &entry->vendor_name, + &entry->vendor_package_name, + &entry->vendor_version, + &entry->vendor_url, + }; - return EFI_SUCCESS; -error: - FreePool(entry); - return EFI_INVALID_PARAMETER; + row = list_entry(pos, struct csv_row, list); + for (i = 0; i < row->n_columns; i++) { + *(ptrs[i]) = strtab; + strtab = stpcpy(strtab, row->columns[i]) + 1; + } + entries[n] = entry; + entry++; + n++; + } + *entriesp = entries; + *n_entries = n; +err: + free_csv_list(&csv); + return efi_status; } void -cleanup_sbat_entries(size_t n, struct sbat_entry **entries) +cleanup_sbat_section_entries(size_t n, struct sbat_section_entry **entries) { - size_t i; - if (!n || !entries) return; - for (i = 0; i < n; i++) { - if (entries[i]) { - FreePool(entries[i]); - entries[i] = NULL; - } - } FreePool(entries); } EFI_STATUS -parse_sbat(char *sbat_base, size_t sbat_size, size_t *sbats, struct sbat_entry ***sbat) -{ - CHAR8 *current = (CHAR8 *)sbat_base; - CHAR8 *end = (CHAR8 *)sbat_base + sbat_size; - EFI_STATUS efi_status = EFI_SUCCESS; - struct sbat_entry *entry = NULL; - struct sbat_entry **entries; - size_t i = 0; - size_t pages = 1; - size_t n = PAGE_SIZE / sizeof(*entry); - - if (!sbat_base || sbat_size == 0 || !sbats || !sbat) - return EFI_INVALID_PARAMETER; - - if (current == end) - return EFI_INVALID_PARAMETER; - - *sbats = 0; - *sbat = 0; - - entries = AllocateZeroPool(pages * PAGE_SIZE); - if (!entries) - return EFI_OUT_OF_RESOURCES; - - do { - entry = NULL; - efi_status = parse_sbat_entry(¤t, end, &entry); - if (EFI_ERROR(efi_status)) - goto error; - - if (end < current) { - efi_status = EFI_INVALID_PARAMETER; - goto error; - } - - if (i >= n) { - struct sbat_entry **new_entries; - unsigned int osize = PAGE_SIZE * pages; - unsigned int nsize = osize + PAGE_SIZE; - - new_entries = ReallocatePool(entries, osize, nsize); - if (!new_entries) { - efi_status = EFI_OUT_OF_RESOURCES; - goto error; - } - entries = new_entries; - ZeroMem(&entries[i], PAGE_SIZE); - pages += 1; - n = nsize / sizeof(entry); - } - entries[i++] = entry; - } while (entry && current && *current != '\0'); - - *sbats = i; - *sbat = entries; - - return efi_status; -error: - perror(L"Failed to parse SBAT data: %r\n", efi_status); - cleanup_sbat_entries(i, entries); - return efi_status; -} - -EFI_STATUS -verify_single_entry(struct sbat_entry *entry, struct sbat_var *sbat_var_entry) +verify_single_entry(struct sbat_section_entry *entry, struct sbat_var_entry *sbat_var_entry) { UINT16 sbat_gen, sbat_var_gen; - if (strcmp(entry->component_name, sbat_var_entry->component_name) == 0) { + if (strcmp((const char *)entry->component_name, (const char *)sbat_var_entry->component_name) == 0) { dprint(L"component %a has a matching SBAT variable entry, verifying\n", entry->component_name); @@ -162,8 +116,8 @@ verify_single_entry(struct sbat_entry *entry, struct sbat_var *sbat_var_entry) * atoi returns zero for failed conversion, so essentially * badly parsed component_generation will be treated as zero */ - sbat_gen = atoi(entry->component_generation); - sbat_var_gen = atoi(sbat_var_entry->component_generation); + sbat_gen = atoi((const char *)entry->component_generation); + sbat_var_gen = atoi((const char *)sbat_var_entry->component_generation); if (sbat_gen < sbat_var_gen) { dprint(L"component %a, generation %d, was revoked by SBAT variable", @@ -179,160 +133,247 @@ void cleanup_sbat_var(list_t *entries) { list_t *pos = NULL, *tmp = NULL; - struct sbat_var *entry; + struct sbat_var_entry *entry; + void *first = NULL; list_for_each_safe(pos, tmp, entries) { - entry = list_entry(pos, struct sbat_var, list); - list_del(&entry->list); + entry = list_entry(pos, struct sbat_var_entry, list); - if (entry->component_generation) - FreePool((CHAR8 *)entry->component_name); - if (entry->component_name) - FreePool((CHAR8 *)entry->component_generation); - FreePool(entry); + if ((uintptr_t)entry < (uintptr_t)first && entry != NULL) + first = entry; + + list_del(&entry->list); } + if (first) + FreePool(first); } EFI_STATUS -verify_sbat(size_t n, struct sbat_entry **entries) +verify_sbat_helper(list_t *local_sbat_var, size_t n, struct sbat_section_entry **entries) { unsigned int i; list_t *pos = NULL; EFI_STATUS efi_status = EFI_SUCCESS; - struct sbat_var *sbat_var_entry; + struct sbat_var_entry *sbat_var_entry; - if (list_empty(&sbat_var)) { + if (list_empty(local_sbat_var)) { dprint(L"SBAT variable not present\n"); return EFI_SUCCESS; } for (i = 0; i < n; i++) { - list_for_each(pos, &sbat_var) { - sbat_var_entry = list_entry(pos, struct sbat_var, list); + list_for_each(pos, local_sbat_var) { + sbat_var_entry = list_entry(pos, struct sbat_var_entry, list); efi_status = verify_single_entry(entries[i], sbat_var_entry); if (EFI_ERROR(efi_status)) - return efi_status; + goto out; } } - dprint(L"all entries from SBAT section verified\n"); +out: + dprint(L"finished verifying SBAT data: %r\n", efi_status); return efi_status; } -static BOOLEAN -is_utf8_bom(CHAR8 *buf, size_t bufsize) +EFI_STATUS +verify_sbat(size_t n, struct sbat_section_entry **entries) { - unsigned char bom[] = { 0xEF, 0xBB, 0xBF }; + EFI_STATUS efi_status; - return CompareMem(buf, bom, MIN(sizeof(bom), bufsize)) == 0; + efi_status = verify_sbat_helper(&sbat_var, n, entries); + return efi_status; } -static struct sbat_var * -new_entry(const CHAR8 *comp_name, const CHAR8 *comp_gen) +EFI_STATUS +parse_sbat_var_data(list_t *entry_list, UINT8 *data, UINTN datasize) { - struct sbat_var *new_entry = AllocatePool(sizeof(*new_entry)); + struct sbat_var_entry *entry = NULL, **entries; + EFI_STATUS efi_status = EFI_SUCCESS; + list_t csv, *pos = NULL; + char * start = (char *)data; + char * end = (char *)data + datasize - 1; + size_t allocsz = 0; + size_t n; + char *strtab; + + if (!entry_list|| !data || datasize == 0) + return EFI_INVALID_PARAMETER; - if (!new_entry) - return NULL; + INIT_LIST_HEAD(&csv); - INIT_LIST_HEAD(&new_entry->list); - new_entry->component_name = comp_name; - new_entry->component_generation = comp_gen; + efi_status = parse_csv_data(start, end, SBAT_VAR_COLUMNS, &csv); + if (EFI_ERROR(efi_status)) { + return efi_status; + } - return new_entry; -} + n = 0; + list_for_each(pos, &csv) { + struct csv_row * row; + size_t i; -EFI_STATUS -add_entry(list_t *list, const CHAR8 *comp_name, const CHAR8 *comp_gen) -{ - struct sbat_var *new; + row = list_entry(pos, struct csv_row, list); - new = new_entry(comp_name, comp_gen); - if (!new) - return EFI_OUT_OF_RESOURCES; + if (row->n_columns < SBAT_VAR_REQUIRED_COLUMNS) { + efi_status = EFI_INVALID_PARAMETER; + goto err; + } - list_add_tail(&new->list, list); - return EFI_SUCCESS; + + allocsz += sizeof(struct sbat_var_entry *); + allocsz += sizeof(struct sbat_var_entry); + for (i = 0; i < row->n_columns; i++) { + if (!row->columns[i][0]) { + efi_status = EFI_INVALID_PARAMETER; + goto err; + } + allocsz += strlen(row->columns[i]) + 1; + } + n++; + } + + strtab = AllocateZeroPool(allocsz); + if (!strtab) { + efi_status = EFI_OUT_OF_RESOURCES; + goto err; + } + + INIT_LIST_HEAD(entry_list); + + entries = (struct sbat_var_entry **)strtab; + strtab += sizeof(struct sbat_var_entry *) * n; + entry = (struct sbat_var_entry *)strtab; + strtab += sizeof(struct sbat_var_entry) * n; + n = 0; + + list_for_each(pos, &csv) { + struct csv_row * row; + size_t i; + const char **ptrs[] = { + &entry->component_name, + &entry->component_generation, + &entry->sbat_datestamp, + }; + + row = list_entry(pos, struct csv_row, list); + for (i = 0; i < row->n_columns; i++) { + *(ptrs[i]) = strtab; + strtab = stpcpy(strtab, row->columns[i]) + 1; + } + INIT_LIST_HEAD(&entry->list); + list_add_tail(&entry->list, entry_list); + entries[n] = entry; + entry++; + n++; + } +err: + free_csv_list(&csv); + return efi_status; } EFI_STATUS parse_sbat_var(list_t *entries) { UINT8 *data = 0; - UINTN datasize, i; + UINTN datasize; EFI_STATUS efi_status; - char delim; if (!entries) return EFI_INVALID_PARAMETER; - INIT_LIST_HEAD(entries); - - efi_status = get_variable(L"SBAT", &data, &datasize, SHIM_LOCK_GUID); + efi_status = get_variable(SBAT_VAR_NAME, &data, &datasize, SHIM_LOCK_GUID); if (EFI_ERROR(efi_status)) { - LogError(L"Failed to read SBAT variable\n", - efi_status); + LogError(L"Failed to read SBAT variable\n", efi_status); return efi_status; } - CHAR8 *start = (CHAR8 *)data; - CHAR8 *end = (CHAR8 *)data + datasize; - if (is_utf8_bom(start, datasize)) - start += 3; + /* + * We've intentionally made sure there's a NUL byte on all variable + * allocations, so use that here. + */ + return parse_sbat_var_data(entries, data, datasize+1); +} - dprint(L"SBAT variable data:\n"); +static bool +check_sbat_var_attributes(UINT32 attributes) +{ +#ifdef ENABLE_SHIM_DEVEL + return attributes == UEFI_VAR_NV_BS_RT; +#else + return attributes == UEFI_VAR_NV_BS || + attributes == UEFI_VAR_NV_BS_TIMEAUTH; +#endif +} - while (start[0] != '\0') { - const CHAR8 *fields[2] = { - NULL, - }; - for (i = 0; i < 3; i++) { - const CHAR8 *tmp; - /* - * on third iteration we check if we had extra stuff on line while parsing - * component_name. If delimeter on 2nd iteration was ',', this means that - * we have comments after component_name. get_sbat_field in this if condition - * parses comments, if they are present and drops them. - */ - if (i == 2 && start) { - if (delim == ',') { - start = get_sbat_field(start, end, &tmp, - '\n'); - } - break; - } - delim = ','; - /* we do not want to jump to next line and grab stuff from that - */ - if ((strchrnula(start, '\n') - start + 1) <= - (strchrnula(start, ',') - start + 1)) { - delim = '\n'; - if (i == 0) - goto error; - } - if (!start) { - goto error; - } - start = get_sbat_field(start, end, &tmp, delim); - /* to be replaced when we have strdupa() - */ - fields[i] = strndupa(tmp, strlen(tmp)); - if (!fields[i]) { - goto error; - } +EFI_STATUS +set_sbat_uefi_variable(void) +{ + EFI_STATUS efi_status = EFI_SUCCESS; + UINT32 attributes = 0; + + UINT8 *sbat = NULL; + UINTN sbatsize = 0; + + efi_status = get_variable_attr(SBAT_VAR_NAME, &sbat, &sbatsize, + SHIM_LOCK_GUID, &attributes); + /* + * Always set the SBAT UEFI variable if it fails to read. + * + * Don't try to set the SBAT UEFI variable if attributes match and + * the signature matches. + */ + if (EFI_ERROR(efi_status)) { + dprint(L"SBAT read failed %r\n", efi_status); + } else if (check_sbat_var_attributes(attributes) && + sbatsize >= strlen(SBAT_VAR_SIG "1") && + strncmp((const char *)sbat, SBAT_VAR_SIG, + strlen(SBAT_VAR_SIG))) { + dprint("SBAT variable is %d bytes, attributes are 0x%08x\n", + sbatsize, attributes); + FreePool(sbat); + return EFI_SUCCESS; + } else { + FreePool(sbat); + + /* delete previous variable */ + dprint("%s variable is %d bytes, attributes are 0x%08x\n", + SBAT_VAR_NAME, sbatsize, attributes); + dprint("Deleting %s variable.\n", SBAT_VAR_NAME); + efi_status = set_variable(SBAT_VAR_NAME, SHIM_LOCK_GUID, + attributes, 0, ""); + if (EFI_ERROR(efi_status)) { + dprint(L"SBAT variable delete failed %r\n", efi_status); + return efi_status; } - dprint(L"component %a with generation %a\n", fields[0], fields[1]); - efi_status = - add_entry(entries, fields[0], fields[1]); - if (EFI_ERROR(efi_status)) - goto error; } - FreePool(data); - return EFI_SUCCESS; -error: - perror(L"failed to parse SBAT variable\n"); - cleanup_sbat_var(entries); - FreePool(data); - return EFI_INVALID_PARAMETER; + + /* set variable */ + efi_status = set_variable(SBAT_VAR_NAME, SHIM_LOCK_GUID, SBAT_VAR_ATTRS, + sizeof(SBAT_VAR)-1, SBAT_VAR); + if (EFI_ERROR(efi_status)) { + dprint(L"SBAT variable writing failed %r\n", efi_status); + return efi_status; + } + + /* verify that the expected data is there */ + efi_status = get_variable(SBAT_VAR_NAME, &sbat, &sbatsize, + SHIM_LOCK_GUID); + if (EFI_ERROR(efi_status)) { + dprint(L"SBAT read failed %r\n", efi_status); + return efi_status; + } + + if (sbatsize != strlen(SBAT_VAR) || + strncmp((const char *)sbat, SBAT_VAR, strlen(SBAT_VAR)) != 0) { + dprint("new sbatsize is %d, expected %d\n", sbatsize, + strlen(SBAT_VAR)); + efi_status = EFI_INVALID_PARAMETER; + } else { + dprint(L"SBAT variable initialization succeeded\n"); + } + + FreePool(sbat); + + return efi_status; } + // vim:fenc=utf-8:tw=75:noet |