freebsd-skq/lib/libsecureboot/efi/efi_variables.c
Marcin Wojtas 13ea0450a9 Extend libsecureboot(old libve) to obtain trusted certificates from UEFI and implement revocation
UEFI related headers were copied from edk2.

A new build option "MK_LOADER_EFI_SECUREBOOT" was added to allow
loading of trusted anchors from UEFI.

Certificate revocation support is also introduced.
The forbidden certificates are loaded from dbx variable.
Verification fails in two cases:

There is a direct match between cert in dbx and the one in the chain.
The CA used to sign the chain is found in dbx.
One can also insert a hash of TBS section of a certificate into dbx.
In this case verifications fails only if a direct match with a
certificate in chain is found.

Submitted by: Kornel Duleba <mindal@semihalf.com>
Reviewed by: sjg
Obtained from: Semihalf
Sponsored by: Stormshield
Differential Revision:	https://reviews.freebsd.org/D19093
2019-03-06 06:39:42 +00:00

279 lines
7.4 KiB
C

/*-
* Copyright (c) 2019 Stormshield.
* Copyright (c) 2019 Semihalf.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* $FreeBSD$
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <stand.h>
#include <string.h>
#include <efi.h>
#include <efilib.h>
#include <Guid/ImageAuthentication.h>
#define NEED_BRSSL_H
#include "../libsecureboot-priv.h"
#include <brssl.h>
static EFI_GUID ImageSecurityDatabaseGUID = EFI_IMAGE_SECURITY_DATABASE_GUID;
static EFI_GUID efiCertX509GUID = EFI_CERT_X509_GUID;
static EFI_GUID efiCertX509Sha256GUID = EFI_CERT_X509_SHA256_GUID;
static EFI_GUID efiCertX509Sha384GUID = EFI_CERT_X509_SHA384_GUID;
static EFI_GUID efiCertX509Sha5122UID = EFI_CERT_X509_SHA512_GUID;
/*
* Check if Secure Boot is enabled in firmware.
* We evaluate two variables - Secure Boot and Setup Mode.
* Secure Boot is enforced only if the first one equals 1 and the other 0.
*/
int
efi_secure_boot_enabled(void)
{
UINT8 SecureBoot;
UINT8 SetupMode;
size_t length;
EFI_STATUS status;
length = sizeof(SecureBoot);
status = efi_global_getenv("SecureBoot", &SecureBoot, &length);
if (status != EFI_SUCCESS) {
if (status == EFI_NOT_FOUND)
return (0);
printf("Failed to read \"SecureBoot\" variable\n");
return (-efi_status_to_errno(status));
}
length = sizeof(SetupMode);
status = efi_global_getenv("SetupMode", &SetupMode, &length);
if (status != EFI_SUCCESS)
SetupMode = 0;
printf(" SecureBoot: %d, SetupMode: %d\n", SecureBoot, SetupMode);
return (SecureBoot == 1 && SetupMode == 0);
}
/*
* Iterate through UEFI variable and extract X509 certificates from it.
* The EFI_* structures and related guids are defined in UEFI standard.
*/
static br_x509_certificate*
efi_get_certs(const char *name, size_t *count)
{
br_x509_certificate *certs;
UINT8 *database;
EFI_SIGNATURE_LIST *list;
EFI_SIGNATURE_DATA *entry;
size_t db_size;
ssize_t cert_count;
EFI_STATUS status;
database = NULL;
certs = NULL;
db_size = 0;
cert_count = 0;
/*
* Read variable length and allocate memory for it
*/
status = efi_getenv(&ImageSecurityDatabaseGUID, name, database, &db_size);
if (status != EFI_BUFFER_TOO_SMALL)
return (NULL);
database = malloc(db_size);
if (database == NULL)
return (NULL);
status = efi_getenv(&ImageSecurityDatabaseGUID, name, database, &db_size);
if (status != EFI_SUCCESS)
goto fail;
for (list = (EFI_SIGNATURE_LIST*) database;
db_size >= list->SignatureListSize && db_size > 0;
db_size -= list->SignatureListSize,
list = (EFI_SIGNATURE_LIST*)
((UINT8*)list + list->SignatureListSize)) {
/* We are only interested in entries containing X509 certs. */
if (memcmp(&efiCertX509GUID,
&list->SignatureType,
sizeof(EFI_GUID)) != 0) {
continue;
}
entry = (EFI_SIGNATURE_DATA*)
((UINT8*)list +
sizeof(EFI_SIGNATURE_LIST) +
list->SignatureHeaderSize);
certs = realloc(certs,
(cert_count + 1) * sizeof(br_x509_certificate));
if (certs == NULL) {
cert_count = 0;
goto fail;
}
certs[cert_count].data_len = list->SignatureSize - sizeof(EFI_GUID);
certs[cert_count].data = malloc(certs[cert_count].data_len);
if (certs[cert_count].data == NULL)
goto fail;
memcpy(certs[cert_count].data,
entry->SignatureData,
certs[cert_count].data_len);
cert_count++;
}
*count = cert_count;
xfree(database);
return (certs);
fail:
free_certificates(certs, cert_count);
xfree(database);
return (NULL);
}
/*
* Extract digests from UEFI "dbx" variable.
* UEFI standard specifies three types of digest - sha256, sha386, sha512.
*/
hash_data*
efi_get_forbidden_digests(size_t *count)
{
UINT8 *database;
hash_data *digests;
EFI_SIGNATURE_LIST *list;
EFI_SIGNATURE_DATA *entry;
size_t db_size, header_size, hash_size;
int digest_count, entry_count;
EFI_STATUS status;
db_size = 0;
digest_count = 0;
database = NULL;
digests = NULL;
status = efi_getenv(&ImageSecurityDatabaseGUID, "dbx", database, &db_size);
if (status != EFI_BUFFER_TOO_SMALL)
return (NULL);
database = malloc(db_size);
if (database == NULL)
return (NULL);
status = efi_getenv(&ImageSecurityDatabaseGUID, "dbx", database, &db_size);
if (status != EFI_SUCCESS)
goto fail;
for (list = (EFI_SIGNATURE_LIST*) database;
db_size >= list->SignatureListSize && db_size > 0;
db_size -= list->SignatureListSize,
list = (EFI_SIGNATURE_LIST*)
((UINT8*)list + list->SignatureListSize)) {
/* We are only interested in entries that contain digests. */
if (memcmp(&efiCertX509Sha256GUID, &list->SignatureType,
sizeof(EFI_GUID)) == 0) {
hash_size = br_sha256_SIZE;
} else if (memcmp(&efiCertX509Sha384GUID, &list->SignatureType,
sizeof(EFI_GUID)) == 0) {
hash_size = br_sha384_SIZE;
} else if (memcmp(&efiCertX509Sha5122UID, &list->SignatureType,
sizeof(EFI_GUID)) == 0) {
hash_size = br_sha512_SIZE;
} else {
continue;
}
/*
* A single entry can have multiple digests
* of the same type for some reason.
*/
header_size = sizeof(EFI_SIGNATURE_LIST) + list->SignatureHeaderSize;
/* Calculate the number of entries basing on structure size */
entry_count = list->SignatureListSize - header_size;
entry_count /= list->SignatureSize;
entry = (EFI_SIGNATURE_DATA*)((UINT8*)list + header_size);
while (entry_count-- > 0) {
digests = realloc(digests,
(digest_count + 1) * sizeof(hash_data));
if (digests == NULL) {
digest_count = 0;
goto fail;
}
digests[digest_count].data = malloc(hash_size);
if (digests[digest_count].data == NULL)
goto fail;
memcpy(digests[digest_count].data,
entry->SignatureData,
hash_size);
digests[digest_count].hash_size = hash_size;
entry = (EFI_SIGNATURE_DATA*)(entry + list->SignatureSize);
digest_count++;
}
}
xfree(database);
if (count != NULL)
*count = digest_count;
return (digests);
fail:
while (digest_count--)
xfree(digests[digest_count].data);
xfree(database);
xfree(digests);
return (NULL);
}
/* Copy x509 certificates from db */
br_x509_certificate*
efi_get_trusted_certs(size_t *count)
{
return (efi_get_certs("db", count));
}
/* Copy forbidden certificates from dbx */
br_x509_certificate*
efi_get_forbidden_certs(size_t *count)
{
return (efi_get_certs("dbx", count));
}