mirror of
https://git.proxmox.com/git/efi-boot-shim
synced 2025-06-11 21:48:58 +00:00

"gcc -fanalyzer" found two NULL pointer checks we're missing in sbat.c: include/str.h: In function ‘get_sbat_field.part.0’: sbat.c:20:14: error: dereference of NULL ‘offset’ [CWE-476] [-Werror=analyzer-null-dereference] 20 | if (!*offset) and include/str.h: In function ‘parse_sbat’: sbat.c:140:27: error: dereference of NULL ‘current’ [CWE-476] [-Werror=analyzer-null-dereference] 140 | } while (entry && *current != '\0'); Both are simple, and this patch fixes them. Signed-off-by: Peter Jones <pjones@redhat.com>
339 lines
7.6 KiB
C
339 lines
7.6 KiB
C
// SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
/*
|
|
* sbat.c - parse SBAT data from the .sbat section data
|
|
*/
|
|
|
|
#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)
|
|
{
|
|
struct sbat_entry *entry = NULL;
|
|
|
|
entry = AllocateZeroPool(sizeof(*entry));
|
|
if (!entry)
|
|
return EFI_OUT_OF_RESOURCES;
|
|
|
|
*current = get_sbat_field(*current, end, &entry->component_name, ',');
|
|
if (!entry->component_name)
|
|
goto error;
|
|
|
|
*current = get_sbat_field(*current, end, &entry->component_generation,
|
|
',');
|
|
if (!entry->component_generation)
|
|
goto error;
|
|
|
|
*current = get_sbat_field(*current, end, &entry->vendor_name, ',');
|
|
if (!entry->vendor_name)
|
|
goto error;
|
|
|
|
*current =
|
|
get_sbat_field(*current, end, &entry->vendor_package_name, ',');
|
|
if (!entry->vendor_package_name)
|
|
goto error;
|
|
|
|
*current = get_sbat_field(*current, end, &entry->vendor_version, ',');
|
|
if (!entry->vendor_version)
|
|
goto error;
|
|
|
|
*current = get_sbat_field(*current, end, &entry->vendor_url, '\n');
|
|
if (!entry->vendor_url)
|
|
goto error;
|
|
|
|
*sbat_entry = entry;
|
|
|
|
return EFI_SUCCESS;
|
|
|
|
error:
|
|
FreePool(entry);
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
void
|
|
cleanup_sbat_entries(size_t n, struct sbat_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)
|
|
{
|
|
UINT16 sbat_gen, sbat_var_gen;
|
|
|
|
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
|
|
*/
|
|
sbat_gen = atoi(entry->component_generation);
|
|
sbat_var_gen = atoi(sbat_var_entry->component_generation);
|
|
|
|
if (sbat_gen < sbat_var_gen) {
|
|
dprint(L"component %a, generation %d, was revoked by SBAT variable",
|
|
entry->component_name, sbat_gen);
|
|
LogError(L"image did not pass SBAT verification\n");
|
|
return EFI_SECURITY_VIOLATION;
|
|
}
|
|
}
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
void
|
|
cleanup_sbat_var(list_t *entries)
|
|
{
|
|
list_t *pos = NULL, *tmp = NULL;
|
|
struct sbat_var *entry;
|
|
|
|
list_for_each_safe(pos, tmp, entries) {
|
|
entry = list_entry(pos, struct sbat_var, list);
|
|
list_del(&entry->list);
|
|
|
|
if (entry->component_generation)
|
|
FreePool((CHAR8 *)entry->component_name);
|
|
if (entry->component_name)
|
|
FreePool((CHAR8 *)entry->component_generation);
|
|
FreePool(entry);
|
|
}
|
|
}
|
|
|
|
EFI_STATUS
|
|
verify_sbat(size_t n, struct sbat_entry **entries)
|
|
{
|
|
unsigned int i;
|
|
list_t *pos = NULL;
|
|
EFI_STATUS efi_status = EFI_SUCCESS;
|
|
struct sbat_var *sbat_var_entry;
|
|
|
|
if (list_empty(&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);
|
|
efi_status = verify_single_entry(entries[i], sbat_var_entry);
|
|
if (EFI_ERROR(efi_status))
|
|
return efi_status;
|
|
}
|
|
}
|
|
|
|
dprint(L"all entries from SBAT section verified\n");
|
|
return efi_status;
|
|
}
|
|
|
|
static BOOLEAN
|
|
is_utf8_bom(CHAR8 *buf, size_t bufsize)
|
|
{
|
|
unsigned char bom[] = { 0xEF, 0xBB, 0xBF };
|
|
|
|
return CompareMem(buf, bom, MIN(sizeof(bom), bufsize)) == 0;
|
|
}
|
|
|
|
static struct sbat_var *
|
|
new_entry(const CHAR8 *comp_name, const CHAR8 *comp_gen)
|
|
{
|
|
struct sbat_var *new_entry = AllocatePool(sizeof(*new_entry));
|
|
|
|
if (!new_entry)
|
|
return NULL;
|
|
|
|
INIT_LIST_HEAD(&new_entry->list);
|
|
new_entry->component_name = comp_name;
|
|
new_entry->component_generation = comp_gen;
|
|
|
|
return new_entry;
|
|
}
|
|
|
|
EFI_STATUS
|
|
add_entry(list_t *list, const CHAR8 *comp_name, const CHAR8 *comp_gen)
|
|
{
|
|
struct sbat_var *new;
|
|
|
|
new = new_entry(comp_name, comp_gen);
|
|
if (!new)
|
|
return EFI_OUT_OF_RESOURCES;
|
|
|
|
list_add_tail(&new->list, list);
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
EFI_STATUS
|
|
parse_sbat_var(list_t *entries)
|
|
{
|
|
UINT8 *data = 0;
|
|
UINTN datasize, i;
|
|
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);
|
|
if (EFI_ERROR(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;
|
|
|
|
dprint(L"SBAT variable data:\n");
|
|
|
|
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;
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
// vim:fenc=utf-8:tw=75:noet
|