blob: 0947bd505c1c8643c53895456c32bfbcf5541949 [file] [log] [blame]
/*
* 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 = RT->GetTime(&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 = RT->SetVariable(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 = RT->GetVariable(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 = RT->SetVariable(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;
RT->ResetSystem(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 = RT->GetVariable(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 = RT->GetVariable(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
hashes_in_esl(UINT8 *Data, UINTN DataSize, EFI_GUID *hashes[])
{
int count = 0;
EFI_SIGNATURE_LIST *CertList;
certlist_for_each_certentry(CertList, Data, DataSize, DataSize) {
int i;
for (i = 0; i < count; i++) {
if (CompareGuid(&CertList->SignatureType, hashes[i]) == 0)
goto found_skip;
}
for (i = 0; i < allowed_hashes_size; i++) {
if (CompareGuid(&CertList->SignatureType, allowed_hashes[i]) == 0)
goto found;
}
found_skip:
continue;
found:
hashes[count++] = allowed_hashes[i];
}
return count;
}
int
hashes_in_variable(CHAR16* var, EFI_GUID owner, EFI_GUID *hashes[])
{
UINTN DataSize;
UINT8 *Data;
EFI_STATUS status;
int count;
status = get_variable(var, &Data, &DataSize, owner);
if (status == EFI_NOT_FOUND)
return 0;
if (status != EFI_SUCCESS)
return -1;
count = hashes_in_esl(Data, DataSize, hashes);
FreePool(Data);
return count;
}
int
variable_is_setupmode(void)
{
/* set to 1 because we return true if SetupMode doesn't exist */
UINT8 SetupMode = 1;
UINTN DataSize = sizeof(SetupMode);
RT->GetVariable(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);
RT->GetVariable(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, sizeof(sig), 0);
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 = RT->SetVariable(var, &owner,
EFI_VARIABLE_NON_VOLATILE
| EFI_VARIABLE_BOOTSERVICE_ACCESS
| EFI_VARIABLE_APPEND_WRITE,
sizeof(sig), sig);
return status;
}