diff options
Diffstat (limited to 'lib/libsecureboot/efi/efi_variables.c')
-rw-r--r-- | lib/libsecureboot/efi/efi_variables.c | 274 |
1 files changed, 274 insertions, 0 deletions
diff --git a/lib/libsecureboot/efi/efi_variables.c b/lib/libsecureboot/efi/efi_variables.c new file mode 100644 index 000000000000..3c36bc66f788 --- /dev/null +++ b/lib/libsecureboot/efi/efi_variables.c @@ -0,0 +1,274 @@ +/*- + * 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. + */ + +#include <sys/cdefs.h> +#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)); +} |