Skip to content

Commit

Permalink
Don't re-parse the SBAT EFI variable for each binary we load.
Browse files Browse the repository at this point in the history
On a typical boot we validate at least two binaries; parsing the SBAT
EFI variable each time, when it should not be changing, is not worth the
effort.

This patch moves the parsing out to some setup code, instead of doing it
during the verification stage.

Signed-off-by: Peter Jones <[email protected]>
  • Loading branch information
martinezjavier authored and vathpela committed Feb 19, 2021
1 parent 1e78d70 commit ea1c872
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 65 deletions.
3 changes: 2 additions & 1 deletion include/sbat.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ struct sbat_var {
const CHAR8 *component_generation;
list_t list;
};
extern list_t sbat_var;

EFI_STATUS parse_sbat_var(list_t *entries);
void cleanup_sbat_var(list_t *entries);
Expand All @@ -27,7 +28,7 @@ struct sbat_entry {
EFI_STATUS parse_sbat(char *sbat_base, size_t sbat_size, size_t *sbats, struct sbat_entry ***sbat);
void cleanup_sbat_entries(size_t n, struct sbat_entry **entries);

EFI_STATUS verify_sbat(size_t n, struct sbat_entry **entries, list_t *var_entries);
EFI_STATUS verify_sbat(size_t n, struct sbat_entry **entries);

#endif /* !SBAT_H_ */
// vim:fenc=utf-8:tw=75:noet
93 changes: 35 additions & 58 deletions pe.c
Original file line number Diff line number Diff line change
Expand Up @@ -1040,67 +1040,49 @@ handle_image (void *data, unsigned int datasize,

if (secure_mode ()) {
unsigned int i;
EFI_STATUS efi_status;
size_t n;
struct sbat_entry **entries;
struct sbat_entry *entry = NULL;
list_t sbat_var_entries;
INIT_LIST_HEAD(&sbat_var_entries);

if (SBATBase && SBATSize) {
char *sbat_data;
size_t sbat_size;

sbat_size = SBATSize + 1;
sbat_data = AllocatePool(sbat_size);
if (!sbat_data) {
console_print(L"Failed to allocate SBAT buffer\n");
return EFI_OUT_OF_RESOURCES;
}
CopyMem(sbat_data, SBATBase, SBATSize);
sbat_data[SBATSize] = '\0';

efi_status = parse_sbat(sbat_data, sbat_size, &n, &entries);
if (EFI_ERROR(efi_status)) {
perror(L"SBAT data not correct: %r\n",
efi_status);
return efi_status;
}
struct sbat_entry **entries = NULL;
char *sbat_data;
size_t sbat_size;

dprint(L"SBAT data\n");
for (i = 0; i < n; i++) {
entry = entries[i];
dprint(L"%a, %a, %a, %a, %a, %a\n",
entry->component_name,
entry->component_generation,
entry->vendor_name,
entry->vendor_package_name,
entry->vendor_version,
entry->vendor_url);
}
} else {
perror(L"SBAT data not found\n");
return EFI_UNSUPPORTED;
if (SBATBase == NULL || SBATSize == 0) {
if (list_empty(&sbat_var))
return EFI_SUCCESS;
dprint(L"No .sbat section data\n");
return EFI_SECURITY_VIOLATION;
}

efi_status = parse_sbat_var(&sbat_var_entries);
/*
* Until a SBAT variable is installed into the systems, it is expected that
* attempting to parse the variable will fail with an EFI_NOT_FOUND error.
*
* Do not consider that error fatal for now.
*/
if (EFI_ERROR(efi_status) && efi_status != EFI_NOT_FOUND) {
perror(L"Parsing SBAT variable failed: %r\n",
efi_status);
sbat_size = SBATSize + 1;
sbat_data = AllocatePool(sbat_size);
if (!sbat_data) {
console_print(L"Failed to allocate .sbat section buffer\n");
return EFI_OUT_OF_RESOURCES;
}
CopyMem(sbat_data, SBATBase, SBATSize);
sbat_data[SBATSize] = '\0';

efi_status = parse_sbat(sbat_data, sbat_size, &n, &entries);
FreePool(sbat_data);
if (EFI_ERROR(efi_status)) {
perror(L"Could not parse .sbat section data: %r\n", efi_status);
return efi_status;
}

if (efi_status == EFI_SUCCESS)
efi_status = verify_sbat(n, entries, &sbat_var_entries);
if (efi_status == EFI_NOT_FOUND)
efi_status = EFI_SUCCESS;
dprint(L"SBAT data\n");
for (i = 0; i < n; i++) {
dprint(L"%a, %a, %a, %a, %a, %a\n",
entries[i]->component_name,
entries[i]->component_generation,
entries[i]->vendor_name,
entries[i]->vendor_package_name,
entries[i]->vendor_version,
entries[i]->vendor_url);
}

efi_status = verify_sbat(n, entries);
if (entries)
for (i = 0; i < n; i++)
FreePool(entries[i]);
if (EFI_ERROR(efi_status)) {
if (verbose)
console_print(L"Verification failed: %r\n", efi_status);
Expand All @@ -1111,11 +1093,6 @@ handle_image (void *data, unsigned int datasize,

efi_status = verify_buffer(data, datasize,
&context, sha256hash, sha1hash);

if (entries)
for (i = 0; i < n; i++)
FreePool(entries[i]);

if (EFI_ERROR(efi_status)) {
if (verbose)
console_print(L"Verification failed: %r\n", efi_status);
Expand Down
11 changes: 5 additions & 6 deletions sbat.c
Original file line number Diff line number Diff line change
Expand Up @@ -194,20 +194,20 @@ cleanup_sbat_var(list_t *entries)
}

EFI_STATUS
verify_sbat(size_t n, struct sbat_entry **entries, list_t *sbat_entries)
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 (!entries || list_empty(sbat_entries)) {
dprint(L"SBAT variable not present or malformed\n");
return EFI_INVALID_PARAMETER;
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_entries) {
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))
Expand All @@ -216,7 +216,6 @@ verify_sbat(size_t n, struct sbat_entry **entries, list_t *sbat_entries)
}

dprint(L"all entries from SBAT section verified\n");
cleanup_sbat_var(sbat_entries);
return efi_status;
}

Expand Down
21 changes: 21 additions & 0 deletions shim.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ static CHAR16 *second_stage;
void *load_options;
UINT32 load_options_size;

list_t sbat_var;

/*
* The vendor certificate used for validating the second stage loader
*/
Expand Down Expand Up @@ -1751,6 +1753,8 @@ shim_init(void)
void
shim_fini(void)
{
cleanup_sbat_var(&sbat_var);

/*
* Remove our protocols
*/
Expand Down Expand Up @@ -1853,11 +1857,13 @@ efi_main (EFI_HANDLE passed_image_handle, EFI_SYSTEM_TABLE *passed_systab)
CHAR16 *msgs[] = {
L"import_mok_state() failed",
L"shim_init() failed",
L"import of SBAT data failed",
NULL
};
enum {
IMPORT_MOK_STATE,
SHIM_INIT,
IMPORT_SBAT,
} msg = IMPORT_MOK_STATE;

/*
Expand Down Expand Up @@ -1888,6 +1894,21 @@ efi_main (EFI_HANDLE passed_image_handle, EFI_SYSTEM_TABLE *passed_systab)
*/
debug_hook();

INIT_LIST_HEAD(&sbat_var);
efi_status = parse_sbat_var(&sbat_var);
/*
* Until a SBAT variable is installed into the systems, it is expected that
* attempting to parse the variable will fail with an EFI_NOT_FOUND error.
*
* Do not consider that error fatal for now.
*/
if (EFI_ERROR(efi_status) && efi_status != EFI_NOT_FOUND) {
perror(L"Parsing SBAT variable failed: %r\n",
efi_status);
msg = IMPORT_SBAT;
goto die;
}

/*
* Before we do anything else, validate our non-volatile,
* boot-services-only state variables are what we think they are.
Expand Down

0 comments on commit ea1c872

Please sign in to comment.