Skip to content

Commit

Permalink
shim: add HSIStatus feature
Browse files Browse the repository at this point in the history
hughsie asked me if I can make shim tell userland what kinds of accesses
are allowed to the heap, stack, and allocations on the running platform,
so that these could be reported up through fwupd's Host Security ID
program (see https://fwupd.github.io/libfwupdplugin/hsi.html ).

This adds a new config-only (i.e. not a UEFI variable) variable
generated during boot, "/sys/firmware/efi/mok-variables/HSIStatus",
which tells us those properties as well as if the EFI Memory Attribute
Protocol is present.

Signed-off-by: Peter Jones <[email protected]>
  • Loading branch information
vathpela committed May 23, 2024
1 parent 1774e0f commit d20c801
Show file tree
Hide file tree
Showing 8 changed files with 142 additions and 2 deletions.
10 changes: 10 additions & 0 deletions MokVars.txt
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,13 @@ to trust CA keys in the MokList. BS,NV

MokListTrustedRT: A copy of MokListTrusted made available to the kernel
at runtime. RT

HSIStatus: Status of various security features:
heap-is-executable: 0: heap allocations are not executable by default
1: heap allocations are executable
stack-is-executable: 0: UEFI stack is not executable
1: UEFI stack is executable
ro-sections-are-writable: 0: read-only sections are not writable
1: read-only sections are writable
has-memory-attribute-protocol: 0: platform does not provide the EFI Memory Attribute Protocol
1: platform does provide the EFI Memory Attribute Protocol
1 change: 1 addition & 0 deletions globals.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ verification_method_t verification_method;
int loader_is_participating;

UINT8 user_insecure_mode;
UINTN hsi_status = 0;
UINT8 ignore_db;
UINT8 trust_mok_list;
UINT8 mok_policy = 0;
Expand Down
12 changes: 11 additions & 1 deletion include/mok.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ typedef enum {

struct mok_state_variable;
typedef vendor_addend_category_t (vendor_addend_categorizer_t)(struct mok_state_variable *);
typedef UINTN (mok_variable_format_helper_t)(char *buf, size_t sz, struct mok_state_variable *);
typedef UINTN (mok_variable_format_helper_t)(UINT8 *buf, size_t sz, struct mok_state_variable *);

/*
* MoK variables that need to have their storage validated.
Expand Down Expand Up @@ -125,5 +125,15 @@ struct mok_variable_config_entry {
*/
#define MOK_POLICY_REQUIRE_NX 1

extern UINTN hsi_status;
/* heap is executable */
#define SHIM_HSI_STATUS_HEAPX 0x00000001ULL
/* stack is executable */
#define SHIM_HSI_STATUS_STACKX 0x00000002ULL
/* read-only sections are writable */
#define SHIM_HSI_STATUS_ROW 0x00000004ULL
/* platform provides the EFI Memory Attribute Protocol */
#define SHIM_HSI_STATUS_HASMAP 0x00000008ULL

#endif /* !SHIM_MOK_H_ */
// vim:fenc=utf-8:tw=75:noet
2 changes: 2 additions & 0 deletions include/pe.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,7 @@ relocate_coff (PE_COFF_LOADER_IMAGE_CONTEXT *context,
EFI_IMAGE_SECTION_HEADER *Section,
void *orig, void *data);

void get_hsi_mem_info(void);

#endif /* !PE_H_ */
// vim:fenc=utf-8:tw=75:noet
6 changes: 6 additions & 0 deletions include/test-data-efivars-1.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,5 +106,11 @@ static const unsigned char test_data_efivars_1_MokListTrustedRT[] ={
0x01
};

static const unsigned char test_data_efivars_1_HSIStatus[] =
"heap-is-executable: 0\n"
"stack-is-executable: 0\n"
"ro-sections-are-writable: 0\n"
"has-memory-attribute-protocol: 0\n";

#endif /* !TEST_DATA_EFIVARS_1_H_ */
// vim:fenc=utf-8:tw=75:noet
50 changes: 50 additions & 0 deletions mok.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,48 @@ static BOOLEAN check_var(CHAR16 *varname)
efi_status_; \
})

static UINTN
format_hsi_status(UINT8 *buf, size_t sz,
struct mok_state_variable *msv UNUSED)
{
UINT8 heapx[] = "heap-is-executable: X\n";
UINT8 stackx[] = "stack-is-executable: X\n";
UINT8 row[] = "ro-sections-are-writable: X\n";
UINT8 hasmap[] = "has-memory-attribute-protocol: X\n";
UINTN pos;

UINTN ret = sizeof(heapx) + sizeof(stackx) +
sizeof(row) + sizeof(hasmap) - 3;

if (buf == 0 || sz < ret) {
return ret;
}

pos = sizeof(heapx) - 3;
heapx[pos] = (hsi_status & SHIM_HSI_STATUS_HEAPX) ? '1' : '0';

pos = sizeof(stackx) - 3;
stackx[pos] = (hsi_status & SHIM_HSI_STATUS_STACKX) ? '1' : '0';

pos = sizeof(row) - 3;
row[pos] = (hsi_status & SHIM_HSI_STATUS_ROW) ? '1' : '0';

pos = sizeof(hasmap) - 3;
hasmap[pos] = (hsi_status & SHIM_HSI_STATUS_HEAPX) ? '1' : '0';

memcpy(buf, heapx, sizeof(heapx) - 1);
pos = sizeof(heapx) - 1;
memcpy(buf+pos, stackx, sizeof(stackx) - 1);
pos += sizeof(stackx) - 1;
memcpy(buf+pos, row, sizeof(row) - 1);
pos += sizeof(row) - 1;
memcpy(buf+pos, hasmap, sizeof(hasmap) - 1);
pos += sizeof(hasmap) - 1;
buf[pos] = '\0';

return ret;
}

/*
* If the OS has set any of these variables we need to drop into MOK and
* handle them appropriately
Expand Down Expand Up @@ -197,6 +239,14 @@ struct mok_state_variable mok_state_variable_data[] = {
.pcr = 14,
.state = &mok_policy,
},
{.name = L"HSIStatus",
.name8 = "HSIStatus",
.rtname = L"HSIStatus",
.rtname8 = "HSIStatus",
.guid = &SHIM_LOCK_GUID,
.flags = MOK_VARIABLE_CONFIG_ONLY,
.format = format_hsi_status,
},
{ NULL, }
};
size_t n_mok_state_variables = sizeof(mok_state_variable_data) / sizeof(mok_state_variable_data[0]);
Expand Down
59 changes: 58 additions & 1 deletion pe.c
Original file line number Diff line number Diff line change
Expand Up @@ -442,8 +442,11 @@ get_mem_attrs (uintptr_t addr, size_t size, uint64_t *attrs)

efi_status = LibLocateProtocol(&EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID,
(VOID **)&proto);
if (EFI_ERROR(efi_status) || !proto)
if (EFI_ERROR(efi_status) || !proto) {
if (!EFI_ERROR(efi_status))
efi_status = EFI_UNSUPPORTED;
return efi_status;
}

if (!IS_PAGE_ALIGNED(physaddr) || !IS_PAGE_ALIGNED(size) || size == 0 || attrs == NULL) {
dprint(L"%a called on 0x%llx-0x%llx and attrs 0x%llx\n",
Expand Down Expand Up @@ -943,4 +946,58 @@ handle_image (void *data, unsigned int datasize,
return EFI_SUCCESS;
}

void
get_hsi_mem_info(void)
{
EFI_STATUS efi_status;
uintptr_t addr;
uint64_t attrs = 0;
uint32_t *tmp_alloc;

addr = ((uintptr_t)&get_hsi_mem_info) & ~EFI_PAGE_MASK;

efi_status = get_mem_attrs(addr, EFI_PAGE_SIZE, &attrs);
if (EFI_ERROR(efi_status)) {
error:
/*
* In this case we can't actually tell anything, so assume
* and report the worst case scenario.
*/
hsi_status = SHIM_HSI_STATUS_HEAPX |
SHIM_HSI_STATUS_STACKX |
SHIM_HSI_STATUS_ROW;
return;
}

hsi_status = SHIM_HSI_STATUS_HASMAP;
if (attrs & MEM_ATTR_W) {
hsi_status |= SHIM_HSI_STATUS_ROW;
}

addr = ((uintptr_t)&addr) & ~EFI_PAGE_MASK;
efi_status = get_mem_attrs(addr, EFI_PAGE_SIZE, &attrs);
if (EFI_ERROR(efi_status)) {
goto error;
}

if (attrs & MEM_ATTR_X) {
hsi_status |= SHIM_HSI_STATUS_STACKX;
}

tmp_alloc = AllocatePool(EFI_PAGE_SIZE);
if (!tmp_alloc) {
goto error;
}

addr = ((uintptr_t)tmp_alloc) & ~EFI_PAGE_MASK;
efi_status = get_mem_attrs(addr, EFI_PAGE_MASK, &attrs);
FreePool(tmp_alloc);
if (EFI_ERROR(efi_status)) {
goto error;
}
if (attrs & MEM_ATTR_X) {
hsi_status |= SHIM_HSI_STATUS_HEAPX;
}
}

// vim:fenc=utf-8:tw=75:noet
4 changes: 4 additions & 0 deletions test-mok-mirror.c
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,10 @@ test_mok_mirror_0(void)
.data_size = sizeof(test_data_efivars_1_MokListTrustedRT),
.data = test_data_efivars_1_MokListTrustedRT
},
{.name = "HSIStatus",
.data_size = sizeof(test_data_efivars_1_HSIStatus),
.data = test_data_efivars_1_HSIStatus
},
{.name = { 0, },
.data_size = 0,
.data = NULL,
Expand Down

0 comments on commit d20c801

Please sign in to comment.