efi-boot-shim/lib/security_policy.c
Matthew Garrett 17857eb8b5 Port MokManager to Linux Foundation loader UI code
This is the first stage of porting the MokManager UI to the UI code used
by the Linux Foundation UEFI loader.
2013-09-26 11:57:59 -04:00

392 lines
9.9 KiB
C

/*
* Copyright 2012 <James.Bottomley@HansenPartnership.com>
*
* see COPYING file
*
* Install and remove a platform security2 override policy
*/
#include <efi.h>
#include <efilib.h>
#include <guid.h>
#include <sha256.h>
#include <variables.h>
#include <simple_file.h>
#include <errors.h>
#include <security_policy.h>
/*
* See the UEFI Platform Initialization manual (Vol2: DXE) for this
*/
struct _EFI_SECURITY2_PROTOCOL;
struct _EFI_SECURITY_PROTOCOL;
typedef struct _EFI_SECURITY2_PROTOCOL EFI_SECURITY2_PROTOCOL;
typedef struct _EFI_SECURITY_PROTOCOL EFI_SECURITY_PROTOCOL;
typedef EFI_DEVICE_PATH EFI_DEVICE_PATH_PROTOCOL;
typedef EFI_STATUS (EFIAPI *EFI_SECURITY_FILE_AUTHENTICATION_STATE) (
const EFI_SECURITY_PROTOCOL *This,
UINT32 AuthenticationStatus,
const EFI_DEVICE_PATH_PROTOCOL *File
);
typedef EFI_STATUS (EFIAPI *EFI_SECURITY2_FILE_AUTHENTICATION) (
const EFI_SECURITY2_PROTOCOL *This,
const EFI_DEVICE_PATH_PROTOCOL *DevicePath,
VOID *FileBuffer,
UINTN FileSize,
BOOLEAN BootPolicy
);
struct _EFI_SECURITY2_PROTOCOL {
EFI_SECURITY2_FILE_AUTHENTICATION FileAuthentication;
};
struct _EFI_SECURITY_PROTOCOL {
EFI_SECURITY_FILE_AUTHENTICATION_STATE FileAuthenticationState;
};
static UINT8 *security_policy_esl = NULL;
static UINTN security_policy_esl_len;
static EFI_STATUS
security_policy_check_mok(void *data, UINTN len)
{
EFI_STATUS status;
UINT8 hash[SHA256_DIGEST_SIZE];
UINT32 attr;
UINT8 *VarData;
UINTN VarLen;
/* first check is MokSBState. If we're in insecure mode, boot
* anyway regardless of dbx contents */
status = get_variable_attr(L"MokSBState", &VarData, &VarLen,
MOK_OWNER, &attr);
if (status == EFI_SUCCESS) {
UINT8 MokSBState = VarData[0];
FreePool(VarData);
if ((attr & EFI_VARIABLE_RUNTIME_ACCESS) == 0
&& MokSBState)
return EFI_SUCCESS;
}
status = sha256_get_pecoff_digest_mem(data, len, hash);
if (status != EFI_SUCCESS)
return status;
if (find_in_variable_esl(L"dbx", SIG_DB, hash, SHA256_DIGEST_SIZE)
== EFI_SUCCESS)
/* MOK list cannot override dbx */
return EFI_SECURITY_VIOLATION;
status = get_variable_attr(L"MokList", &VarData, &VarLen, MOK_OWNER,
&attr);
if (status != EFI_SUCCESS)
goto check_tmplist;
FreePool(VarData);
if (attr & EFI_VARIABLE_RUNTIME_ACCESS)
goto check_tmplist;
if (find_in_variable_esl(L"MokList", MOK_OWNER, hash, SHA256_DIGEST_SIZE) == EFI_SUCCESS)
return EFI_SUCCESS;
check_tmplist:
if (security_policy_esl
&& find_in_esl(security_policy_esl, security_policy_esl_len, hash,
SHA256_DIGEST_SIZE) == EFI_SUCCESS)
return EFI_SUCCESS;
return EFI_SECURITY_VIOLATION;
}
static EFI_SECURITY_FILE_AUTHENTICATION_STATE esfas = NULL;
static EFI_SECURITY2_FILE_AUTHENTICATION es2fa = NULL;
static EFI_STATUS thunk_security_policy_authentication(
const EFI_SECURITY_PROTOCOL *This,
UINT32 AuthenticationStatus,
const EFI_DEVICE_PATH_PROTOCOL *DevicePath
)
__attribute__((unused));
static EFI_STATUS thunk_security2_policy_authentication(
const EFI_SECURITY2_PROTOCOL *This,
const EFI_DEVICE_PATH_PROTOCOL *DevicePath,
VOID *FileBuffer,
UINTN FileSize,
BOOLEAN BootPolicy
)
__attribute__((unused));
static __attribute__((used)) EFI_STATUS
security2_policy_authentication (
const EFI_SECURITY2_PROTOCOL *This,
const EFI_DEVICE_PATH_PROTOCOL *DevicePath,
VOID *FileBuffer,
UINTN FileSize,
BOOLEAN BootPolicy
)
{
EFI_STATUS status, auth;
/* Chain original security policy */
status = uefi_call_wrapper(es2fa, 5, This, DevicePath, FileBuffer,
FileSize, BootPolicy);
/* if OK, don't bother with MOK check */
if (status == EFI_SUCCESS)
return status;
auth = security_policy_check_mok(FileBuffer, FileSize);
if (auth == EFI_SECURITY_VIOLATION || auth == EFI_ACCESS_DENIED)
/* return previous status, which is the correct one
* for the platform: may be either EFI_ACCESS_DENIED
* or EFI_SECURITY_VIOLATION */
return status;
return auth;
}
static __attribute__((used)) EFI_STATUS
security_policy_authentication (
const EFI_SECURITY_PROTOCOL *This,
UINT32 AuthenticationStatus,
const EFI_DEVICE_PATH_PROTOCOL *DevicePathConst
)
{
EFI_STATUS status, fail_status;
EFI_DEVICE_PATH *DevPath
= DuplicateDevicePath((EFI_DEVICE_PATH *)DevicePathConst),
*OrigDevPath = DevPath;
EFI_HANDLE h;
EFI_FILE *f;
VOID *FileBuffer;
UINTN FileSize;
CHAR16* DevPathStr;
/* Chain original security policy */
status = uefi_call_wrapper(esfas, 3, This, AuthenticationStatus,
DevicePathConst);
/* if OK avoid checking MOK: It's a bit expensive to
* read the whole file in again (esfas already did this) */
if (status == EFI_SUCCESS)
goto out;
/* capture failure status: may be either EFI_ACCESS_DENIED or
* EFI_SECURITY_VIOLATION */
fail_status = status;
status = uefi_call_wrapper(BS->LocateDevicePath, 3,
&SIMPLE_FS_PROTOCOL, &DevPath, &h);
if (status != EFI_SUCCESS)
goto out;
DevPathStr = DevicePathToStr(DevPath);
status = simple_file_open_by_handle(h, DevPathStr, &f,
EFI_FILE_MODE_READ);
FreePool(DevPathStr);
if (status != EFI_SUCCESS)
goto out;
status = simple_file_read_all(f, &FileSize, &FileBuffer);
simple_file_close(f);
if (status != EFI_SUCCESS)
goto out;
status = security_policy_check_mok(FileBuffer, FileSize);
FreePool(FileBuffer);
if (status == EFI_ACCESS_DENIED || status == EFI_SECURITY_VIOLATION)
/* return what the platform originally said */
status = fail_status;
out:
FreePool(OrigDevPath);
return status;
}
/* Nasty: ELF and EFI have different calling conventions. Here is the map for
* calling ELF -> EFI
*
* 1) rdi -> rcx (32 saved)
* 2) rsi -> rdx (32 saved)
* 3) rdx -> r8 ( 32 saved)
* 4) rcx -> r9 (32 saved)
* 5) r8 -> 32(%rsp) (48 saved)
* 6) r9 -> 40(%rsp) (48 saved)
* 7) pad+0(%rsp) -> 48(%rsp) (64 saved)
* 8) pad+8(%rsp) -> 56(%rsp) (64 saved)
* 9) pad+16(%rsp) -> 64(%rsp) (80 saved)
* 10) pad+24(%rsp) -> 72(%rsp) (80 saved)
* 11) pad+32(%rsp) -> 80(%rsp) (96 saved)
*
* So for a five argument callback, the map is ignore the first two arguments
* and then map (EFI -> ELF) assuming pad = 0.
*
* ARG4 -> ARG1
* ARG3 -> ARG2
* ARG5 -> ARG3
* ARG6 -> ARG4
* ARG11 -> ARG5
*
* Calling conventions also differ over volatile and preserved registers in
* MS: RBX, RBP, RDI, RSI, R12, R13, R14, and R15 are considered nonvolatile .
* In ELF: Registers %rbp, %rbx and %r12 through %r15 “belong” to the calling
* function and the called function is required to preserve their values.
*
* This means when accepting a function callback from MS -> ELF, we have to do
* separate preservation on %rdi, %rsi before swizzling the arguments and
* handing off to the ELF function.
*/
asm (
".type security2_policy_authentication,@function\n"
"thunk_security2_policy_authentication:\n\t"
"mov 0x28(%rsp), %r10 # ARG5\n\t"
"push %rdi\n\t"
"push %rsi\n\t"
"mov %r10, %rdi\n\t"
"subq $8, %rsp # space for storing stack pad\n\t"
"mov $0x08, %rax\n\t"
"mov $0x10, %r10\n\t"
"and %rsp, %rax\n\t"
"cmovnz %rax, %r11\n\t"
"cmovz %r10, %r11\n\t"
"subq %r11, %rsp\n\t"
"addq $8, %r11\n\t"
"mov %r11, (%rsp)\n\t"
"# five argument swizzle\n\t"
"mov %rdi, %r10\n\t"
"mov %rcx, %rdi\n\t"
"mov %rdx, %rsi\n\t"
"mov %r8, %rdx\n\t"
"mov %r9, %rcx\n\t"
"mov %r10, %r8\n\t"
"callq security2_policy_authentication@PLT\n\t"
"mov (%rsp), %r11\n\t"
"addq %r11, %rsp\n\t"
"pop %rsi\n\t"
"pop %rdi\n\t"
"ret\n"
);
asm (
".type security_policy_authentication,@function\n"
"thunk_security_policy_authentication:\n\t"
"push %rdi\n\t"
"push %rsi\n\t"
"subq $8, %rsp # space for storing stack pad\n\t"
"mov $0x08, %rax\n\t"
"mov $0x10, %r10\n\t"
"and %rsp, %rax\n\t"
"cmovnz %rax, %r11\n\t"
"cmovz %r10, %r11\n\t"
"subq %r11, %rsp\n\t"
"addq $8, %r11\n\t"
"mov %r11, (%rsp)\n\t"
"# three argument swizzle\n\t"
"mov %rcx, %rdi\n\t"
"mov %rdx, %rsi\n\t"
"mov %r8, %rdx\n\t"
"callq security_policy_authentication@PLT\n\t"
"mov (%rsp), %r11\n\t"
"addq %r11, %rsp\n\t"
"pop %rsi\n\t"
"pop %rdi\n\t"
"ret\n"
);
EFI_STATUS
security_policy_install(void)
{
EFI_SECURITY_PROTOCOL *security_protocol;
EFI_SECURITY2_PROTOCOL *security2_protocol = NULL;
EFI_STATUS status;
if (esfas)
/* Already Installed */
return EFI_ALREADY_STARTED;
/* Don't bother with status here. The call is allowed
* to fail, since SECURITY2 was introduced in PI 1.2.1
* If it fails, use security2_protocol == NULL as indicator */
uefi_call_wrapper(BS->LocateProtocol, 3,
&SECURITY2_PROTOCOL_GUID, NULL,
&security2_protocol);
status = uefi_call_wrapper(BS->LocateProtocol, 3,
&SECURITY_PROTOCOL_GUID, NULL,
&security_protocol);
if (status != EFI_SUCCESS)
/* This one is mandatory, so there's a serious problem */
return status;
if (security2_protocol) {
es2fa = security2_protocol->FileAuthentication;
security2_protocol->FileAuthentication =
thunk_security2_policy_authentication;
}
esfas = security_protocol->FileAuthenticationState;
security_protocol->FileAuthenticationState =
thunk_security_policy_authentication;
return EFI_SUCCESS;
}
EFI_STATUS
security_policy_uninstall(void)
{
EFI_STATUS status;
if (esfas) {
EFI_SECURITY_PROTOCOL *security_protocol;
status = uefi_call_wrapper(BS->LocateProtocol, 3,
&SECURITY_PROTOCOL_GUID, NULL,
&security_protocol);
if (status != EFI_SUCCESS)
return status;
security_protocol->FileAuthenticationState = esfas;
esfas = NULL;
} else {
/* nothing installed */
return EFI_NOT_STARTED;
}
if (es2fa) {
EFI_SECURITY2_PROTOCOL *security2_protocol;
status = uefi_call_wrapper(BS->LocateProtocol, 3,
&SECURITY2_PROTOCOL_GUID, NULL,
&security2_protocol);
if (status != EFI_SUCCESS)
return status;
security2_protocol->FileAuthentication = es2fa;
es2fa = NULL;
}
return EFI_SUCCESS;
}
void
security_protocol_set_hashes(unsigned char *esl, int len)
{
security_policy_esl = esl;
security_policy_esl_len = len;
}