efi-boot-shim/lib/variables.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

341 lines
8.9 KiB
C

/*
* Copyright 2012 <James.Bottomley@HansenPartnership.com>
*
* see COPYING file
*
* Portions of this file are a direct cut and paste from Tianocore
* (http://tianocore.sf.net)
*
* SecurityPkg/VariableAuthenticated/SecureBootConfigDxe/SecureBootConfigImpl.c
*
* Copyright (c) 2011 - 2012, Intel Corporation. All rights reserved.<BR>
* This program and the accompanying materials
* are licensed and made available under the terms and conditions of the BSD License
* which accompanies this distribution. The full text of the license may be found
* at
* http://opensource.org/licenses/bsd-license.php
*
* THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
* WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
*
*/
#include <efi.h>
#include <efilib.h>
#include <efiauthenticated.h>
#include <variables.h>
#include <guid.h>
#include <console.h>
#include <sha256.h>
#include <errors.h>
EFI_STATUS
variable_create_esl(void *cert, int cert_len, EFI_GUID *type, EFI_GUID *owner,
void **out, int *outlen)
{
*outlen = cert_len + sizeof(EFI_SIGNATURE_LIST) + sizeof(EFI_GUID);
*out = AllocateZeroPool(*outlen);
if (!*out)
return EFI_OUT_OF_RESOURCES;
EFI_SIGNATURE_LIST *sl = *out;
sl->SignatureHeaderSize = 0;
sl->SignatureType = *type;
sl->SignatureSize = cert_len + sizeof(EFI_GUID);
sl->SignatureListSize = *outlen;
EFI_SIGNATURE_DATA *sd = *out + sizeof(EFI_SIGNATURE_LIST);
if (owner)
sd->SignatureOwner = *owner;
CopyMem(sd->SignatureData, cert, cert_len);
return EFI_SUCCESS;
}
EFI_STATUS
CreateTimeBasedPayload (
IN OUT UINTN *DataSize,
IN OUT UINT8 **Data
)
{
EFI_STATUS Status;
UINT8 *NewData;
UINT8 *Payload;
UINTN PayloadSize;
EFI_VARIABLE_AUTHENTICATION_2 *DescriptorData;
UINTN DescriptorSize;
EFI_TIME Time;
EFI_GUID efi_cert_type = EFI_CERT_TYPE_PKCS7_GUID;
if (Data == NULL || DataSize == NULL) {
return EFI_INVALID_PARAMETER;
}
//
// In Setup mode or Custom mode, the variable does not need to be signed but the
// parameters to the SetVariable() call still need to be prepared as authenticated
// variable. So we create EFI_VARIABLE_AUTHENTICATED_2 descriptor without certificate
// data in it.
//
Payload = *Data;
PayloadSize = *DataSize;
DescriptorSize = OFFSET_OF(EFI_VARIABLE_AUTHENTICATION_2, AuthInfo) + OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData);
NewData = (UINT8*) AllocateZeroPool (DescriptorSize + PayloadSize);
if (NewData == NULL) {
return EFI_OUT_OF_RESOURCES;
}
if ((Payload != NULL) && (PayloadSize != 0)) {
CopyMem (NewData + DescriptorSize, Payload, PayloadSize);
}
DescriptorData = (EFI_VARIABLE_AUTHENTICATION_2 *) (NewData);
ZeroMem (&Time, sizeof (EFI_TIME));
Status = uefi_call_wrapper(RT->GetTime,2, &Time, NULL);
if (EFI_ERROR (Status)) {
FreePool(NewData);
return Status;
}
Time.Pad1 = 0;
Time.Nanosecond = 0;
Time.TimeZone = 0;
Time.Daylight = 0;
Time.Pad2 = 0;
CopyMem (&DescriptorData->TimeStamp, &Time, sizeof (EFI_TIME));
DescriptorData->AuthInfo.Hdr.dwLength = OFFSET_OF (WIN_CERTIFICATE_UEFI_GUID, CertData);
DescriptorData->AuthInfo.Hdr.wRevision = 0x0200;
DescriptorData->AuthInfo.Hdr.wCertificateType = WIN_CERT_TYPE_EFI_GUID;
DescriptorData->AuthInfo.CertType = efi_cert_type;
/* we're expecting an EFI signature list, so don't free the input since
* it might not be in a pool */
#if 0
if (Payload != NULL) {
FreePool(Payload);
}
#endif
*DataSize = DescriptorSize + PayloadSize;
*Data = NewData;
return EFI_SUCCESS;
}
EFI_STATUS
SetSecureVariable(CHAR16 *var, UINT8 *Data, UINTN len, EFI_GUID owner,
UINT32 options, int createtimebased)
{
EFI_SIGNATURE_LIST *Cert;
UINTN DataSize;
EFI_STATUS efi_status;
/* Microsoft request: Bugs in some UEFI platforms mean that PK or any
* other secure variable can be updated or deleted programmatically,
* so prevent */
if (!variable_is_setupmode())
return EFI_SECURITY_VIOLATION;
if (createtimebased) {
int ds;
efi_status = variable_create_esl(Data, len, &X509_GUID, NULL,
(void **)&Cert, &ds);
if (efi_status != EFI_SUCCESS) {
Print(L"Failed to create %s certificate %d\n", var, efi_status);
return efi_status;
}
DataSize = ds;
} else {
/* we expect an efi signature list rather than creating it */
Cert = (EFI_SIGNATURE_LIST *)Data;
DataSize = len;
}
efi_status = CreateTimeBasedPayload(&DataSize, (UINT8 **)&Cert);
if (efi_status != EFI_SUCCESS) {
Print(L"Failed to create time based payload %d\n", efi_status);
return efi_status;
}
efi_status = uefi_call_wrapper(RT->SetVariable, 5, var, &owner,
EFI_VARIABLE_NON_VOLATILE
| EFI_VARIABLE_RUNTIME_ACCESS
| EFI_VARIABLE_BOOTSERVICE_ACCESS
| EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS
| options,
DataSize, Cert);
return efi_status;
}
UINT64
GetOSIndications(void)
{
UINT64 indications;
UINTN DataSize = sizeof(indications);
EFI_STATUS efi_status;
efi_status = uefi_call_wrapper(RT->GetVariable, 5, L"OsIndicationsSupported", &GV_GUID, NULL, &DataSize, &indications);
if (efi_status != EFI_SUCCESS)
return 0;
return indications;
}
EFI_STATUS
SETOSIndicationsAndReboot(UINT64 indications)
{
UINTN DataSize = sizeof(indications);
EFI_STATUS efi_status;
efi_status = uefi_call_wrapper(RT->SetVariable, 5, L"OsIndications",
&GV_GUID,
EFI_VARIABLE_NON_VOLATILE
| EFI_VARIABLE_RUNTIME_ACCESS
| EFI_VARIABLE_BOOTSERVICE_ACCESS,
DataSize, &indications);
if (efi_status != EFI_SUCCESS)
return efi_status;
uefi_call_wrapper(RT->ResetSystem, 4, EfiResetWarm, EFI_SUCCESS, 0, NULL);
/* does not return */
return EFI_SUCCESS;
}
EFI_STATUS
get_variable_attr(CHAR16 *var, UINT8 **data, UINTN *len, EFI_GUID owner,
UINT32 *attributes)
{
EFI_STATUS efi_status;
*len = 0;
efi_status = uefi_call_wrapper(RT->GetVariable, 5, var, &owner,
NULL, len, NULL);
if (efi_status != EFI_BUFFER_TOO_SMALL)
return efi_status;
*data = AllocateZeroPool(*len);
if (!data)
return EFI_OUT_OF_RESOURCES;
efi_status = uefi_call_wrapper(RT->GetVariable, 5, var, &owner,
attributes, len, *data);
if (efi_status != EFI_SUCCESS) {
FreePool(*data);
*data = NULL;
}
return efi_status;
}
EFI_STATUS
get_variable(CHAR16 *var, UINT8 **data, UINTN *len, EFI_GUID owner)
{
return get_variable_attr(var, data, len, owner, NULL);
}
EFI_STATUS
find_in_esl(UINT8 *Data, UINTN DataSize, UINT8 *key, UINTN keylen)
{
EFI_SIGNATURE_LIST *CertList;
certlist_for_each_certentry(CertList, Data, DataSize, DataSize) {
if (CertList->SignatureSize != keylen + sizeof(EFI_GUID))
continue;
EFI_SIGNATURE_DATA *Cert;
certentry_for_each_cert(Cert, CertList)
if (CompareMem (Cert->SignatureData, key, keylen) == 0)
return EFI_SUCCESS;
}
return EFI_NOT_FOUND;
}
EFI_STATUS
find_in_variable_esl(CHAR16* var, EFI_GUID owner, UINT8 *key, UINTN keylen)
{
UINTN DataSize;
UINT8 *Data;
EFI_STATUS status;
status = get_variable(var, &Data, &DataSize, owner);
if (status != EFI_SUCCESS)
return status;
status = find_in_esl(Data, DataSize, key, keylen);
FreePool(Data);
return status;
}
int
variable_is_setupmode(void)
{
/* set to 1 because we return true if SetupMode doesn't exist */
UINT8 SetupMode = 1;
UINTN DataSize = sizeof(SetupMode);
uefi_call_wrapper(RT->GetVariable, 5, L"SetupMode", &GV_GUID, NULL,
&DataSize, &SetupMode);
return SetupMode;
}
int
variable_is_secureboot(void)
{
/* return false if variable doesn't exist */
UINT8 SecureBoot = 0;
UINTN DataSize;
DataSize = sizeof(SecureBoot);
uefi_call_wrapper(RT->GetVariable, 5, L"SecureBoot", &GV_GUID, NULL,
&DataSize, &SecureBoot);
return SecureBoot;
}
EFI_STATUS
variable_enroll_hash(CHAR16 *var, EFI_GUID owner,
UINT8 hash[SHA256_DIGEST_SIZE])
{
EFI_STATUS status;
if (find_in_variable_esl(var, owner, hash, SHA256_DIGEST_SIZE)
== EFI_SUCCESS)
/* hash already present */
return EFI_ALREADY_STARTED;
UINT8 sig[sizeof(EFI_SIGNATURE_LIST) + sizeof(EFI_SIGNATURE_DATA) - 1 + SHA256_DIGEST_SIZE];
EFI_SIGNATURE_LIST *l = (void *)sig;
EFI_SIGNATURE_DATA *d = (void *)sig + sizeof(EFI_SIGNATURE_LIST);
SetMem(sig, 0, sizeof(sig));
l->SignatureType = EFI_CERT_SHA256_GUID;
l->SignatureListSize = sizeof(sig);
l->SignatureSize = 16 +32; /* UEFI defined */
CopyMem(&d->SignatureData, hash, SHA256_DIGEST_SIZE);
d->SignatureOwner = MOK_OWNER;
if (CompareGuid(&owner, &SIG_DB) == 0)
status = SetSecureVariable(var, sig, sizeof(sig), owner,
EFI_VARIABLE_APPEND_WRITE, 0);
else
status = uefi_call_wrapper(RT->SetVariable, 5, var, &owner,
EFI_VARIABLE_NON_VOLATILE
| EFI_VARIABLE_BOOTSERVICE_ACCESS
| EFI_VARIABLE_APPEND_WRITE,
sizeof(sig), sig);
return status;
}