/* * Copyright (C) 2025 Michael Brown . * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * You can also choose to distribute this program under the terms of * the Unmodified Binary Distribution Licence (as given in the file * COPYING.UBDL), provided that you have satisfied its requirements. */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); FILE_SECBOOT ( PERMITTED ); /** @file * * EFI CA certificates * */ #include #include #include #include #include #include #include #include #include #include /** List of EFI CA certificates */ static struct x509_chain efi_cacerts = { .refcnt = REF_INIT ( ref_no_free ), .links = LIST_HEAD_INIT ( efi_cacerts.links ), }; /** * Retrieve EFI CA certificate * * @v data TlsCaCertificate variable data * @v len Length of TlsCaCertificate * @v offset Offset within data * @v next Next offset, or negative error */ static int efi_cacert ( const void *data, size_t len, size_t offset ) { struct asn1_cursor *cursor; struct x509_certificate *cert; int next; int rc; /* Extract ASN.1 object */ next = efisig_asn1 ( data, len, offset, &cursor ); if ( next < 0 ) { rc = next; DBGC ( &efi_cacerts, "EFICA could not parse at +%#zx: %s\n", offset, strerror ( rc ) ); goto err_asn1; } /* Append to list of EFI CA certificates */ if ( ( rc = x509_append_raw ( &efi_cacerts, cursor->data, cursor->len ) ) != 0 ) { DBGC ( &efi_cacerts, "EFICA could not append at +%#zx: %s\n", offset, strerror ( rc ) ); goto err_append; } cert = x509_last ( &efi_cacerts ); DBGC ( &efi_cacerts, "EFICA found certificate %s\n", x509_name ( cert ) ); /* Mark certificate as valid (i.e. trusted) if permitted */ if ( allow_trust_override ) { DBGC ( &efi_cacerts, "EFICA trusting certificate %s\n", x509_name ( cert ) ); x509_set_valid ( cert, NULL, &root_certificates ); } /* Free ASN.1 object */ free ( cursor ); return next; err_append: free ( cursor ); err_asn1: return rc; } /** * Retrieve all EFI CA certificates * * @ret rc Return status code */ static int efi_cacert_all ( void ) { EFI_RUNTIME_SERVICES *rs = efi_systab->RuntimeServices; EFI_GUID *guid = &efi_tls_ca_certificate_guid; static CHAR16 *wname = EFI_TLS_CA_CERTIFICATE_VARIABLE; int offset = 0; UINT32 attrs; UINTN size; void *data; EFI_STATUS efirc; int rc; /* Get variable length */ size = 0; if ( ( efirc = rs->GetVariable ( wname, guid, &attrs, &size, NULL ) ) != EFI_BUFFER_TOO_SMALL ) { rc = -EEFI ( efirc ); DBGC ( &efi_cacerts, "EFICA could not get %ls size: %s\n", wname, strerror ( rc ) ); goto err_len; } /* Allocate temporary buffer */ data = malloc ( size ); if ( ! data ) { rc = -ENOMEM; goto err_alloc; } /* Read variable */ if ( ( efirc = rs->GetVariable ( wname, guid, &attrs, &size, data ) ) != 0 ) { rc = -EEFI ( efirc ); DBGC ( &efi_cacerts, "EFICA could not read %ls: %s\n", wname, strerror ( rc ) ); goto err_get; } /* Parse certificates */ while ( ( ( size_t ) offset ) < size ) { offset = efi_cacert ( data, size, offset ); if ( offset < 0 ) { rc = offset; goto err_cacert; } } /* Success */ rc = 0; err_cacert: err_get: free ( data ); err_alloc: err_len: return rc; } /** * Initialise EFI CA certificates * */ static void efi_cacert_init ( void ) { int rc; /* Initialise all certificates */ if ( ( rc = efi_cacert_all() ) != 0 ) { DBGC ( &efi_cacert, "EFICA could not initialise: %s\n", strerror ( rc ) ); /* Nothing we can do at this point */ return; } } /** EFI CA certificates initialisation function */ struct init_fn efi_cacert_init_fn __init_fn ( INIT_LATE ) = { .name = "eficacert", .initialise = efi_cacert_init, }; /** * Discard any EFI CA certificates * */ static void efi_cacert_shutdown ( int booting __unused ) { /* Drop our references to the certificates */ DBGC ( &efi_cacert, "EFICA discarding certificates\n" ); x509_truncate ( &efi_cacerts, NULL ); assert ( list_empty ( &efi_cacerts.links ) ); } /** EFI CA certificates shutdown function */ struct startup_fn efi_cacert_shutdown_fn __startup_fn ( STARTUP_NORMAL ) = { .name = "efi_cacert", .shutdown = efi_cacert_shutdown, };