Changeset View
Changeset View
Standalone View
Standalone View
stand/efi/libefi/efisecureboot.c
- This file was added.
/*- | |||||
* Copyright (c) 2019 Stormshield. | |||||
* Copyright (c) 2019 Semihalf. | |||||
* All rights reserved. | |||||
* | |||||
* 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. | |||||
*/ | |||||
#include <sys/cdefs.h> | |||||
__FBSDID("$FreeBSD$"); | |||||
#include <stand.h> | |||||
#include <string.h> | |||||
#include <sys/param.h> | |||||
#include <efi.h> | |||||
#include <efilib.h> | |||||
#include <efisecureboot.h> | |||||
#include <Guid/ImageAuthentication.h> | |||||
#include <bearssl.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; | |||||
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 SecureBoot; | |||||
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); | |||||
} | |||||
static br_x509_certificate* | |||||
efi_get_certs(const char *name, size_t *count) | |||||
{ | |||||
UINT8 *database; | |||||
EFI_SIGNATURE_LIST *list; | |||||
EFI_SIGNATURE_DATA *entry; | |||||
br_x509_certificate *certs; | |||||
size_t length; | |||||
size_t cert_count; | |||||
EFI_STATUS status; | |||||
database = NULL; | |||||
length = 0; | |||||
/* Read variable length and allocate proper buffer. */ | |||||
status = efi_getenv(&ImageSecurityDatabaseGUID, name, database, &length); | |||||
if (status != EFI_BUFFER_TOO_SMALL) | |||||
return NULL; | |||||
database = malloc(length); | |||||
if (database == NULL) | |||||
return NULL; | |||||
status = efi_getenv(&ImageSecurityDatabaseGUID, name, database, &length); | |||||
if (status != EFI_SUCCESS) | |||||
return NULL; | |||||
list = (EFI_SIGNATURE_LIST*) database; | |||||
certs = NULL; | |||||
cert_count = 0; | |||||
for(;length >= list->SignatureListSize && length > 0; | |||||
length -= list->SignatureListSize, | |||||
list = (EFI_SIGNATURE_LIST*)((UINT8*)list + list->SignatureListSize)) { | |||||
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) | |||||
return NULL; | |||||
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; | |||||
free(database); | |||||
return certs; | |||||
fail: | |||||
while (cert_count-- > 0) { | |||||
free(certs[cert_count].data); | |||||
free(&certs[cert_count]); | |||||
} | |||||
free(database); | |||||
free(certs); | |||||
return NULL; | |||||
} | |||||
int | |||||
efi_secure_boot_cert_forbidden(const char *cert, size_t cert_length) | |||||
{ | |||||
UINT8 *database; | |||||
EFI_SIGNATURE_LIST *list; | |||||
EFI_SIGNATURE_DATA *entry; | |||||
size_t length; | |||||
size_t header_size; | |||||
size_t digest_size; | |||||
size_t hash_count; | |||||
EFI_STATUS status; | |||||
br_hash_compat_context hash_ctx; | |||||
const br_hash_class *md; | |||||
unsigned char digest[br_sha512_SIZE]; | |||||
length = 0; | |||||
status = efi_getenv(&ImageSecurityDatabaseGUID, "dbx", database, &length); | |||||
if (status != EFI_BUFFER_TOO_SMALL) { | |||||
if (status == EFI_NOT_FOUND) | |||||
return (0); | |||||
return efi_status_to_errno(status); | |||||
} | |||||
database = malloc(length); | |||||
if (database == NULL) | |||||
return ENOMEM; | |||||
status = efi_getenv(&ImageSecurityDatabaseGUID, "dbx", database, &length); | |||||
if (status != EFI_SUCCESS) | |||||
return efi_status_to_errno(status); | |||||
list = (EFI_SIGNATURE_LIST*) database; | |||||
for(;length >= list->SignatureListSize && length > 0; | |||||
length -= list->SignatureListSize, | |||||
list = (EFI_SIGNATURE_LIST*)((UINT8*)list + list->SignatureListSize)) { | |||||
if (memcmp(&efiCertX509Sha256GUID, &list->SignatureType, | |||||
sizeof(EFI_GUID)) == 0) { | |||||
md = &br_sha256_vtable; | |||||
digest_size = br_sha256_SIZE; | |||||
} else if(memcmp(&efiCertX509Sha384GUID, &list->SignatureType, | |||||
sizeof(EFI_GUID)) == 0) { | |||||
md = &br_sha384_vtable; | |||||
digest_size = br_sha384_SIZE; | |||||
} else if(memcmp(&efiCertX509Sha5122UID, &list->SignatureType, | |||||
sizeof(EFI_GUID)) == 0) { | |||||
md = &br_sha512_vtable; | |||||
digest_size = br_sha512_SIZE; | |||||
} else { | |||||
continue; | |||||
} | |||||
md->init(&hash_ctx.vtable); | |||||
md->update(&hash_ctx.vtable, cert, cert_length); | |||||
md->out(&hash_ctx.vtable, digest); | |||||
header_size = sizeof(EFI_SIGNATURE_LIST) + list->SignatureHeaderSize; | |||||
hash_count = list->SignatureListSize - header_size; | |||||
hash_count /= list->SignatureSize; | |||||
entry = (EFI_SIGNATURE_DATA*)((UINT8*)list + header_size); | |||||
while (hash_count-- > 0) { | |||||
if (memcmp(digest, entry->SignatureData, digest_size) == 0) | |||||
return EACCES; | |||||
entry = (EFI_SIGNATURE_DATA*)(entry + list->SignatureSize); | |||||
} | |||||
} | |||||
return 0; | |||||
} | |||||
/* Copy x509 certificates from db */ | |||||
br_x509_certificate* | |||||
efi_secure_boot_get_CAs(size_t *count) | |||||
{ | |||||
return efi_get_certs("db", count); | |||||
} | |||||
/* Copy forbidden certificates from dbx */ | |||||
br_x509_certificate* | |||||
efi_secure_boot_get_forbidden_CAs(size_t *count) | |||||
{ | |||||
return efi_get_certs("dbx", count); | |||||
} |