summaryrefslogtreecommitdiff
path: root/sbat.c
diff options
context:
space:
mode:
Diffstat (limited to 'sbat.c')
-rw-r--r--sbat.c493
1 files changed, 267 insertions, 226 deletions
diff --git a/sbat.c b/sbat.c
index 446bed1a..89c08417 100644
--- a/sbat.c
+++ b/sbat.c
@@ -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(&current, 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