/* * 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 signature lists * */ #include #include #include #include #include #include #include #include #include #include /** * Find EFI signature list entry * * @v data EFI signature list * @v len Length of EFI signature list * @v start Starting offset to update * @v lhdr Signature list header to fill in * @v dhdr Signature data header to fill in * @ret rc Return status code */ static int efisig_find ( const void *data, size_t len, size_t *start, const EFI_SIGNATURE_LIST **lhdr, const EFI_SIGNATURE_DATA **dhdr ) { size_t offset; size_t remaining; size_t skip; size_t dlen; /* Scan through signature list */ offset = 0; while ( 1 ) { /* Read list header */ assert ( offset <= len ); remaining = ( len - offset ); if ( remaining < sizeof ( **lhdr ) ) { DBGC ( data, "EFISIG [%#zx,%#zx) truncated header " "at +%#zx\n", *start, len, offset ); return -EINVAL; } *lhdr = ( data + offset ); /* Get length of this signature list */ if ( remaining < (*lhdr)->SignatureListSize ) { DBGC ( data, "EFISIG [%#zx,%#zx) truncated list at " "+%#zx\n", *start, len, offset ); return -EINVAL; } remaining = (*lhdr)->SignatureListSize; /* Get length of each signature in list */ dlen = (*lhdr)->SignatureSize; if ( dlen < sizeof ( **dhdr ) ) { DBGC ( data, "EFISIG [%#zx,%#zx) underlength " "signatures at +%#zx\n", *start, len, offset ); return -EINVAL; } /* Strip list header (including variable portion) */ if ( ( remaining < sizeof ( **lhdr ) ) || ( ( remaining - sizeof ( **lhdr ) ) < (*lhdr)->SignatureHeaderSize ) ) { DBGC ( data, "EFISIG [%#zx,%#zx) malformed header at " "+%#zx\n", *start, len, offset ); return -EINVAL; } skip = ( sizeof ( **lhdr ) + (*lhdr)->SignatureHeaderSize ); offset += skip; remaining -= skip; /* Read signatures */ for ( ; remaining ; offset += dlen, remaining -= dlen ) { /* Check length */ if ( remaining < dlen ) { DBGC ( data, "EFISIG [%#zx,%#zx) truncated " "at +%#zx\n", *start, len, offset ); return -EINVAL; } /* Continue until we find the requested signature */ if ( offset < *start ) continue; /* Read data header */ *dhdr = ( data + offset ); DBGC2 ( data, "EFISIG [%#zx,%#zx) %s ", offset, ( offset + dlen ), efi_guid_ntoa ( &(*lhdr)->SignatureType ) ); DBGC2 ( data, "owner %s\n", efi_guid_ntoa ( &(*dhdr)->SignatureOwner ) ); *start = offset; return 0; } } } /** * Extract ASN.1 object from EFI signature list * * @v data EFI signature list * @v len Length of EFI signature list * @v offset Offset within image * @v cursor ASN.1 cursor to fill in * @ret next Offset to next image, or negative error * * The caller is responsible for eventually calling free() on the * allocated ASN.1 cursor. */ int efisig_asn1 ( const void *data, size_t len, size_t offset, struct asn1_cursor **cursor ) { const EFI_SIGNATURE_LIST *lhdr; const EFI_SIGNATURE_DATA *dhdr; int ( * asn1 ) ( const void *data, size_t len, size_t offset, struct asn1_cursor **cursor ); size_t skip = offsetof ( typeof ( *dhdr ), SignatureData ); int next; int rc; /* Locate signature list entry */ if ( ( rc = efisig_find ( data, len, &offset, &lhdr, &dhdr ) ) != 0 ) goto err_entry; len = ( offset + lhdr->SignatureSize ); /* Parse as PEM or DER based on first character */ asn1 = ( ( dhdr->SignatureData[0] == ASN1_SEQUENCE ) ? der_asn1 : pem_asn1 ); DBGC2 ( data, "EFISIG [%#zx,%#zx) extracting %s\n", offset, len, ( ( asn1 == der_asn1 ) ? "DER" : "PEM" ) ); next = asn1 ( data, len, ( offset + skip ), cursor ); if ( next < 0 ) { rc = next; DBGC ( data, "EFISIG [%#zx,%#zx) could not extract ASN.1: " "%s\n", offset, len, strerror ( rc ) ); goto err_asn1; } /* Check that whole entry was consumed */ if ( ( ( unsigned int ) next ) != len ) { DBGC ( data, "EFISIG [%#zx,%#zx) malformed data\n", offset, len ); rc = -EINVAL; goto err_whole; } return len; err_whole: free ( *cursor ); err_asn1: err_entry: return rc; } /** * Probe EFI signature list image * * @v image EFI signature list * @ret rc Return status code */ static int efisig_image_probe ( struct image *image ) { const EFI_SIGNATURE_LIST *lhdr; const EFI_SIGNATURE_DATA *dhdr; size_t offset = 0; unsigned int count = 0; int rc; /* Check file is a well-formed signature list */ while ( 1 ) { /* Find next signature list entry */ if ( ( rc = efisig_find ( image->data, image->len, &offset, &lhdr, &dhdr ) ) != 0 ) { return rc; } /* Skip this entry */ offset += lhdr->SignatureSize; count++; /* Check if we have reached end of the image */ if ( offset == image->len ) { DBGC ( image, "EFISIG %s contains %d signatures\n", image->name, count ); return 0; } } } /** * Extract ASN.1 object from EFI signature list image * * @v image EFI signature list * @v offset Offset within image * @v cursor ASN.1 cursor to fill in * @ret next Offset to next image, or negative error * * The caller is responsible for eventually calling free() on the * allocated ASN.1 cursor. */ static int efisig_image_asn1 ( struct image *image, size_t offset, struct asn1_cursor **cursor ) { int next; int rc; /* Extract ASN.1 object */ if ( ( next = efisig_asn1 ( image->data, image->len, offset, cursor ) ) < 0 ) { rc = next; DBGC ( image, "EFISIG %s could not extract ASN.1: %s\n", image->name, strerror ( rc ) ); return rc; } return next; } /** EFI signature list image type */ struct image_type efisig_image_type __image_type ( PROBE_NORMAL ) = { .name = "EFISIG", .probe = efisig_image_probe, .asn1 = efisig_image_asn1, };