Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for NX (W^X) mitigations. #459

Merged
merged 6 commits into from
May 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions globals.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ int loader_is_participating;
UINT8 user_insecure_mode;
UINT8 ignore_db;
UINT8 trust_mok_list;
UINT8 mok_policy = 0;

UINT32 verbose = 0;

Expand Down
2 changes: 1 addition & 1 deletion include/guid.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ extern EFI_GUID EFI_SECURE_BOOT_DB_GUID;
extern EFI_GUID EFI_SIMPLE_FILE_SYSTEM_GUID;
extern EFI_GUID SECURITY_PROTOCOL_GUID;
extern EFI_GUID SECURITY2_PROTOCOL_GUID;
extern EFI_GUID EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID;
extern EFI_GUID SHIM_LOCK_GUID;

extern EFI_GUID MOK_VARIABLE_STORE;

#endif /* SHIM_GUID_H */
5 changes: 5 additions & 0 deletions include/mok.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,5 +100,10 @@ struct mok_variable_config_entry {
UINT8 data[];
};

/*
* bit definitions for MokPolicy
*/
#define MOK_POLICY_REQUIRE_NX 1

#endif /* !SHIM_MOK_H_ */
// vim:fenc=utf-8:tw=75:noet
54 changes: 47 additions & 7 deletions include/peimage.h
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,24 @@ typedef struct {
EFI_IMAGE_DATA_DIRECTORY DataDirectory[EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES];
} EFI_IMAGE_OPTIONAL_HEADER64;

#define EFI_IMAGE_DLLCHARACTERISTICS_RESERVED_0001 0x0001
#define EFI_IMAGE_DLLCHARACTERISTICS_RESERVED_0002 0x0002
#define EFI_IMAGE_DLLCHARACTERISTICS_RESERVED_0004 0x0004
#define EFI_IMAGE_DLLCHARACTERISTICS_RESERVED_0008 0x0008
#if 0 /* This is not in the PE spec. */
#define EFI_IMAGE_DLLCHARACTERISTICS_RESERVED_0010 0x0010
#endif
#define EFI_IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA 0x0020
#define EFI_IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE 0x0040
#define EFI_IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY 0x0080
#define EFI_IMAGE_DLLCHARACTERISTICS_NX_COMPAT 0x0100
#define EFI_IMAGE_DLLCHARACTERISTICS_NO_ISOLATION 0x0200
#define EFI_IMAGE_DLLCHARACTERISTICS_NO_SEH 0x0400
#define EFI_IMAGE_DLLCHARACTERISTICS_NO_BIND 0x0800
#define EFI_IMAGE_DLLCHARACTERISTICS_APPCONTAINER 0x1000
#define EFI_IMAGE_DLLCHARACTERISTICS_WDM_DRIVER 0x2000
#define EFI_IMAGE_DLLCHARACTERISTICS_GUARD_CF 0x4000
#define EFI_IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE 0x8000

///
/// @attention
Expand Down Expand Up @@ -303,24 +321,46 @@ typedef struct {
//
// Section Flags Values
//
#define EFI_IMAGE_SCN_TYPE_NO_PAD 0x00000008 ///< Reserved.
#define EFI_IMAGE_SCN_RESERVED_00000000 0x00000000
#define EFI_IMAGE_SCN_RESERVED_00000001 0x00000001
#define EFI_IMAGE_SCN_RESERVED_00000002 0x00000002
#define EFI_IMAGE_SCN_RESERVED_00000004 0x00000004
#define EFI_IMAGE_SCN_TYPE_NO_PAD 0x00000008
#define EFI_IMAGE_SCN_RESERVED_00000010 0x00000010
#define EFI_IMAGE_SCN_CNT_CODE 0x00000020
#define EFI_IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040
#define EFI_IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080

#define EFI_IMAGE_SCN_LNK_OTHER 0x00000100 ///< Reserved.
#define EFI_IMAGE_SCN_LNK_INFO 0x00000200 ///< Section contains comments or some other type of information.
#define EFI_IMAGE_SCN_LNK_REMOVE 0x00000800 ///< Section contents will not become part of image.
#define EFI_IMAGE_SCN_LNK_OTHER 0x00000100
#define EFI_IMAGE_SCN_LNK_INFO 0x00000200
#define EFI_IMAGE_SCN_RESERVED_00000400 0x00000400
#define EFI_IMAGE_SCN_LNK_REMOVE 0x00000800
#define EFI_IMAGE_SCN_LNK_COMDAT 0x00001000

#define EFI_IMAGE_SCN_RESERVED_00002000 0x00002000
#define EFI_IMAGE_SCN_RESERVED_00004000 0x00004000
#define EFI_IMAGE_SCN_GPREL 0x00008000
/*
* PE 9.3 says both IMAGE_SCN_MEM_PURGEABLE and IMAGE_SCN_MEM_16BIT are
* 0x00020000, but I think it's wrong. --pjones
*/
#define EFI_IMAGE_SCN_MEM_PURGEABLE 0x00010000 // "Reserved for future use."
#define EFI_IMAGE_SCN_MEM_16BIT 0x00020000 // "Reserved for future use."
#define EFI_IMAGE_SCN_MEM_LOCKED 0x00040000 // "Reserved for future use."
#define EFI_IMAGE_SCN_MEM_PRELOAD 0x00080000 // "Reserved for future use."
#define EFI_IMAGE_SCN_ALIGN_1BYTES 0x00100000
#define EFI_IMAGE_SCN_ALIGN_2BYTES 0x00200000
#define EFI_IMAGE_SCN_ALIGN_4BYTES 0x00300000
#define EFI_IMAGE_SCN_ALIGN_8BYTES 0x00400000
#define EFI_IMAGE_SCN_ALIGN_16BYTES 0x00500000
#define EFI_IMAGE_SCN_ALIGN_32BYTES 0x00600000
#define EFI_IMAGE_SCN_ALIGN_64BYTES 0x00700000

#define EFI_IMAGE_SCN_ALIGN_128BYTES 0x00800000
#define EFI_IMAGE_SCN_ALIGN_256BYTES 0x00900000
#define EFI_IMAGE_SCN_ALIGN_512BYTES 0x00a00000
#define EFI_IMAGE_SCN_ALIGN_1024BYTES 0x00b00000
#define EFI_IMAGE_SCN_ALIGN_2048BYTES 0x00c00000
#define EFI_IMAGE_SCN_ALIGN_4096BYTES 0x00d00000
#define EFI_IMAGE_SCN_ALIGN_8192BYTES 0x00e00000
#define EFI_IMAGE_SCN_LNK_NRELOC_OVFL 0x01000000
#define EFI_IMAGE_SCN_MEM_DISCARDABLE 0x02000000
#define EFI_IMAGE_SCN_MEM_NOT_CACHED 0x04000000
#define EFI_IMAGE_SCN_MEM_NOT_PAGED 0x08000000
Expand Down
2 changes: 1 addition & 1 deletion lib/guid.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,6 @@ EFI_GUID EFI_SECURE_BOOT_DB_GUID = { 0xd719b2cb, 0x3d3a, 0x4596, { 0xa3, 0xbc,
EFI_GUID EFI_SIMPLE_FILE_SYSTEM_GUID = SIMPLE_FILE_SYSTEM_PROTOCOL;
EFI_GUID SECURITY_PROTOCOL_GUID = { 0xA46423E3, 0x4617, 0x49f1, {0xB9, 0xFF, 0xD1, 0xBF, 0xA9, 0x11, 0x58, 0x39 } };
EFI_GUID SECURITY2_PROTOCOL_GUID = { 0x94ab2f58, 0x1438, 0x4ef1, {0x91, 0x52, 0x18, 0x94, 0x1a, 0x3a, 0x0e, 0x68 } };

EFI_GUID EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID = { 0xf4560cf6, 0x40ec, 0x4b4a, {0xa1, 0x92, 0xbf, 0x1d, 0x57, 0xd0, 0xb1, 0x89} };
EFI_GUID SHIM_LOCK_GUID = {0x605dab50, 0xe046, 0x4300, {0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23 } };
EFI_GUID MOK_VARIABLE_STORE = {0xc451ed2b, 0x9694, 0x45d3, {0xba, 0xba, 0xed, 0x9f, 0x89, 0x88, 0xa3, 0x89} };
13 changes: 13 additions & 0 deletions mok.c
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,19 @@ struct mok_state_variable mok_state_variable_data[] = {
.pcr = 14,
.state = &trust_mok_list,
},
{.name = L"MokPolicy",
.name8 = "MokPolicy",
.rtname = L"MokPolicyRT",
.rtname8 = "MokPolicyRT",
.guid = &SHIM_LOCK_GUID,
.yes_attr = EFI_VARIABLE_BOOTSERVICE_ACCESS |
EFI_VARIABLE_NON_VOLATILE,
.no_attr = EFI_VARIABLE_RUNTIME_ACCESS,
.flags = MOK_MIRROR_DELETE_FIRST |
MOK_VARIABLE_LOG,
.pcr = 14,
.state = &mok_policy,
},
{ NULL, }
};
size_t n_mok_state_variables = sizeof(mok_state_variable_data) / sizeof(mok_state_variable_data[0]);
Expand Down
205 changes: 205 additions & 0 deletions pe.c
Original file line number Diff line number Diff line change
Expand Up @@ -696,6 +696,7 @@ read_header(void *data, unsigned int datasize,
EFI_IMAGE_OPTIONAL_HEADER_UNION *PEHdr = data;
unsigned long HeaderWithoutDataDir, SectionHeaderOffset, OptHeaderSize;
unsigned long FileAlignment = 0;
UINT16 DllFlags;

if (datasize < sizeof (PEHdr->Pe32)) {
perror(L"Invalid image\n");
Expand Down Expand Up @@ -790,13 +791,21 @@ read_header(void *data, unsigned int datasize,
context->EntryPoint = PEHdr->Pe32Plus.OptionalHeader.AddressOfEntryPoint;
context->RelocDir = &PEHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC];
context->SecDir = &PEHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY];
DllFlags = PEHdr->Pe32Plus.OptionalHeader.DllCharacteristics;
} else {
context->ImageAddress = PEHdr->Pe32.OptionalHeader.ImageBase;
context->EntryPoint = PEHdr->Pe32.OptionalHeader.AddressOfEntryPoint;
context->RelocDir = &PEHdr->Pe32.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC];
context->SecDir = &PEHdr->Pe32.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY];
DllFlags = PEHdr->Pe32.OptionalHeader.DllCharacteristics;
}

if ((mok_policy & MOK_POLICY_REQUIRE_NX) &&
!(DllFlags & EFI_IMAGE_DLLCHARACTERISTICS_NX_COMPAT)) {
perror(L"Policy requires NX, but image does not support NX\n");
return EFI_UNSUPPORTED;
}

context->FirstSection = (EFI_IMAGE_SECTION_HEADER *)((char *)PEHdr + PEHdr->Pe32.FileHeader.SizeOfOptionalHeader + sizeof(UINT32) + sizeof(EFI_IMAGE_FILE_HEADER));

if (context->ImageSize < context->SizeOfHeaders) {
Expand Down Expand Up @@ -878,6 +887,139 @@ verify_sbat_section(char *SBATBase, size_t SBATSize)
return efi_status;
}

static inline uint64_t
shim_mem_attrs_to_uefi_mem_attrs (uint64_t attrs)
{
uint64_t ret = EFI_MEMORY_RP |
EFI_MEMORY_RO |
EFI_MEMORY_XP;

if (attrs & MEM_ATTR_R)
ret &= ~EFI_MEMORY_RP;

if (attrs & MEM_ATTR_W)
ret &= ~EFI_MEMORY_RO;

if (attrs & MEM_ATTR_X)
ret &= ~EFI_MEMORY_XP;

return ret;
}

static inline uint64_t
uefi_mem_attrs_to_shim_mem_attrs (uint64_t attrs)
{
uint64_t ret = MEM_ATTR_R |
MEM_ATTR_W |
MEM_ATTR_X;

if (attrs & EFI_MEMORY_RP)
ret &= ~MEM_ATTR_R;

if (attrs & EFI_MEMORY_RO)
ret &= ~MEM_ATTR_W;

if (attrs & EFI_MEMORY_XP)
ret &= ~MEM_ATTR_X;

return ret;
}

static EFI_STATUS
get_mem_attrs (uintptr_t addr, size_t size, uint64_t *attrs)
{
EFI_MEMORY_ATTRIBUTE_PROTOCOL *proto = NULL;
EFI_PHYSICAL_ADDRESS physaddr = addr;
EFI_STATUS efi_status;

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

if (physaddr & 0xfff || size & 0xfff || size == 0 || attrs == NULL) {
dprint(L"%a called on 0x%llx-0x%llx and attrs 0x%llx\n",
__func__, (unsigned long long)physaddr,
(unsigned long long)(physaddr+size-1),
attrs);
return EFI_SUCCESS;
}

efi_status = proto->GetMemoryAttributes(proto, physaddr, size, attrs);
*attrs = uefi_mem_attrs_to_shim_mem_attrs (*attrs);

return efi_status;
}

static EFI_STATUS
update_mem_attrs(uintptr_t addr, uint64_t size,
uint64_t set_attrs, uint64_t clear_attrs)
{
EFI_MEMORY_ATTRIBUTE_PROTOCOL *proto = NULL;
EFI_PHYSICAL_ADDRESS physaddr = addr;
EFI_STATUS efi_status, ret;
uint64_t before = 0, after = 0, uefi_set_attrs, uefi_clear_attrs;

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

efi_status = get_mem_attrs (addr, size, &before);
if (EFI_ERROR(efi_status))
dprint(L"get_mem_attrs(0x%llx, 0x%llx, 0x%llx) -> 0x%lx\n",
(unsigned long long)addr, (unsigned long long)size,
&before, efi_status);

if (physaddr & 0xfff || size & 0xfff || size == 0) {
dprint(L"%a called on 0x%llx-0x%llx (size 0x%llx) +%a%a%a -%a%a%a\n",
__func__, (unsigned long long)physaddr,
(unsigned long long)(physaddr + size - 1),
(unsigned long long)size,
(set_attrs & MEM_ATTR_R) ? "r" : "",
(set_attrs & MEM_ATTR_W) ? "w" : "",
(set_attrs & MEM_ATTR_X) ? "x" : "",
(clear_attrs & MEM_ATTR_R) ? "r" : "",
(clear_attrs & MEM_ATTR_W) ? "w" : "",
(clear_attrs & MEM_ATTR_X) ? "x" : "");
return 0;
}

uefi_set_attrs = shim_mem_attrs_to_uefi_mem_attrs (set_attrs);
dprint("translating set_attrs from 0x%lx to 0x%lx\n", set_attrs, uefi_set_attrs);
uefi_clear_attrs = shim_mem_attrs_to_uefi_mem_attrs (clear_attrs);
dprint("translating clear_attrs from 0x%lx to 0x%lx\n", clear_attrs, uefi_clear_attrs);
efi_status = EFI_SUCCESS;
if (uefi_set_attrs)
efi_status = proto->SetMemoryAttributes(proto, physaddr, size, uefi_set_attrs);
if (!EFI_ERROR(efi_status) && uefi_clear_attrs)
efi_status = proto->ClearMemoryAttributes(proto, physaddr, size, uefi_clear_attrs);
ret = efi_status;

efi_status = get_mem_attrs (addr, size, &after);
if (EFI_ERROR(efi_status))
dprint(L"get_mem_attrs(0x%llx, %llu, 0x%llx) -> 0x%lx\n",
(unsigned long long)addr, (unsigned long long)size,
&after, efi_status);

dprint(L"set +%a%a%a -%a%a%a on 0x%llx-0x%llx before:%c%c%c after:%c%c%c\n",
(set_attrs & MEM_ATTR_R) ? "r" : "",
(set_attrs & MEM_ATTR_W) ? "w" : "",
(set_attrs & MEM_ATTR_X) ? "x" : "",
(clear_attrs & MEM_ATTR_R) ? "r" : "",
(clear_attrs & MEM_ATTR_W) ? "w" : "",
(clear_attrs & MEM_ATTR_X) ? "x" : "",
(unsigned long long)addr, (unsigned long long)(addr + size - 1),
(before & MEM_ATTR_R) ? 'r' : '-',
(before & MEM_ATTR_W) ? 'w' : '-',
(before & MEM_ATTR_X) ? 'x' : '-',
(after & MEM_ATTR_R) ? 'r' : '-',
(after & MEM_ATTR_W) ? 'w' : '-',
(after & MEM_ATTR_X) ? 'x' : '-');

return ret;
}

EFI_STATUS verify_image(void *data, unsigned int datasize,
EFI_LOADED_IMAGE *li,
PE_COFF_LOADER_IMAGE_CONTEXT *context)
Expand Down Expand Up @@ -1013,6 +1155,11 @@ handle_image (void *data, unsigned int datasize,
}

buffer = (void *)ALIGN_VALUE((unsigned long)*alloc_address, alignment);
dprint(L"Loading 0x%llx bytes at 0x%llx\n",
(unsigned long long)context.ImageSize,
(unsigned long long)(uintptr_t)buffer);
update_mem_attrs((uintptr_t)buffer, alloc_size, MEM_ATTR_R|MEM_ATTR_W,
MEM_ATTR_X);

CopyMem(buffer, data, context.SizeOfHeaders);

Expand Down Expand Up @@ -1049,6 +1196,20 @@ handle_image (void *data, unsigned int datasize,
!Section->Misc.VirtualSize)
continue;

/*
* Skip sections that aren't marked readable.
*/
if (!(Section->Characteristics & EFI_IMAGE_SCN_MEM_READ))
continue;

if (!(Section->Characteristics & EFI_IMAGE_SCN_MEM_DISCARDABLE) &&
(Section->Characteristics & EFI_IMAGE_SCN_MEM_WRITE) &&
(Section->Characteristics & EFI_IMAGE_SCN_MEM_EXECUTE) &&
(mok_policy & MOK_POLICY_REQUIRE_NX)) {
perror(L"Section %d is writable and executable\n", i);
return EFI_UNSUPPORTED;
}

base = ImageAddress (buffer, context.ImageSize,
Section->VirtualAddress);
end = ImageAddress (buffer, context.ImageSize,
Expand Down Expand Up @@ -1159,6 +1320,50 @@ handle_image (void *data, unsigned int datasize,
}
}

/*
* Now set the page permissions appropriately.
*/
Section = context.FirstSection;
for (i = 0; i < context.NumberOfSections; i++, Section++) {
uint64_t set_attrs = MEM_ATTR_R;
uint64_t clear_attrs = MEM_ATTR_W|MEM_ATTR_X;
uintptr_t addr;
uint64_t length;

/*
* Skip discardable sections with zero size
*/
if ((Section->Characteristics & EFI_IMAGE_SCN_MEM_DISCARDABLE) &&
!Section->Misc.VirtualSize)
continue;

/*
* Skip sections that aren't marked readable.
*/
if (!(Section->Characteristics & EFI_IMAGE_SCN_MEM_READ))
continue;

base = ImageAddress (buffer, context.ImageSize,
Section->VirtualAddress);
end = ImageAddress (buffer, context.ImageSize,
Section->VirtualAddress
+ Section->Misc.VirtualSize - 1);

addr = (uintptr_t)base;
length = (uintptr_t)end - (uintptr_t)base + 1;

if (Section->Characteristics & EFI_IMAGE_SCN_MEM_WRITE) {
set_attrs |= MEM_ATTR_W;
clear_attrs &= ~MEM_ATTR_W;
}
if (Section->Characteristics & EFI_IMAGE_SCN_MEM_EXECUTE) {
set_attrs |= MEM_ATTR_X;
clear_attrs &= ~MEM_ATTR_X;
}
update_mem_attrs(addr, length, set_attrs, clear_attrs);
}


/*
* grub needs to know its location and size in memory, so fix up
* the loaded image protocol values
Expand Down
Loading