/* * Copyright (c) 2018, Red Hat Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above * copyright notice, this list of conditions and the * following disclaimer. * * 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. * * The names of contributors to this software may not be * used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 * COPYRIGHT OWNER OR CONTRIBUTORS 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. * * Author: Laszlo Ersek */ #include "config.h" #include "buffer.h" /* p11_buffer */ #include "debug.h" /* return_val_if_fail() */ #include "message.h" /* p11_message() */ #include "extract.h" /* p11_extract_edk2_cacerts() */ #include /* UINT32_MAX */ #include /* SSIZE_MAX */ /* types from the UEFI 2.7 spec, section "31.4.1 Signature Database" */ typedef struct { uint32_t data1; uint16_t data2; uint16_t data3; uint8_t data4[8]; } efi_guid; typedef struct { efi_guid signature_type; uint32_t signature_list_size; uint32_t signature_header_size; uint32_t signature_size; } efi_signature_list; typedef struct { efi_guid signature_owner; } efi_signature_data; /* * EFI_CERT_X509_GUID (A5C059A1-94E4-4AA7-87B5-AB155C2BF072) from the UEFI 2.7 * spec, in host byte order */ static const efi_guid efi_cert_x509_guid_host = { 0xa5c059a1, 0x94e4, 0x4aa7, { 0x87, 0xb5, 0xab, 0x15, 0x5c, 0x2b, 0xf0, 0x72 } }; /* * the GUID identifying this extractor as "agent" * (DCDD3B50-F405-43FD-96BE-BD33B1734776, generated with "uuidgen"), in host * byte order */ static const efi_guid agent_guid_host = { 0xdcdd3b50, 0xf405, 0x43fd, { 0x96, 0xbe, 0xbd, 0x33, 0xb1, 0x73, 0x47, 0x76 } }; /* serialization helpers */ static void buffer_add_uint16 (p11_buffer *buffer, uint16_t uint16) { uint8_t uint16_buf[2]; uint16_buf[0] = uint16; uint16_buf[1] = uint16 >> 8; p11_buffer_add (buffer, &uint16_buf, sizeof uint16_buf); } static void buffer_add_uint32 (p11_buffer *buffer, uint32_t uint32) { uint8_t uint32_buf[4]; uint32_buf[0] = uint32; uint32_buf[1] = uint32 >> 8; uint32_buf[2] = uint32 >> 16; uint32_buf[3] = uint32 >> 24; p11_buffer_add (buffer, &uint32_buf, sizeof uint32_buf); } static void buffer_add_efi_guid (p11_buffer *buffer, const efi_guid *guid) { buffer_add_uint32 (buffer, guid->data1); buffer_add_uint16 (buffer, guid->data2); buffer_add_uint16 (buffer, guid->data3); p11_buffer_add (buffer, guid->data4, sizeof guid->data4); } static void buffer_add_efi_signature_list (p11_buffer *buffer, const efi_signature_list *siglist) { buffer_add_efi_guid (buffer, &siglist->signature_type); buffer_add_uint32 (buffer, siglist->signature_list_size); buffer_add_uint32 (buffer, siglist->signature_header_size); buffer_add_uint32 (buffer, siglist->signature_size); } static void buffer_add_efi_signature_data (p11_buffer *buffer, const efi_signature_data *sigdata) { buffer_add_efi_guid (buffer, &sigdata->signature_owner); } /* main routine */ static bool prepare_edk2_buffer (p11_enumerate *ex, p11_buffer *buffer) { efi_signature_list siglist; efi_signature_data sigdata; CK_RV rv; size_t size; /* * set "siglist.signature_type" and "sigdata.signature_owner" for reuse * across all certificates */ siglist.signature_type = efi_cert_x509_guid_host; sigdata.signature_owner = agent_guid_host; /* also reuse a zero "siglist.signature_header_size" */ siglist.signature_header_size = 0; /* for every certificate */ while ((rv = p11_kit_iter_next (ex->iter)) == CKR_OK) { size = sizeof sigdata; /* * set the variable size fields in "siglist" while catching any * (unlikely) integer overflows */ return_val_if_fail (ex->cert_len <= UINT32_MAX - size, false); size += ex->cert_len; siglist.signature_size = size; return_val_if_fail (sizeof siglist <= UINT32_MAX - size, false); size += sizeof siglist; siglist.signature_list_size = size; /* serialize the headers */ buffer_add_efi_signature_list (buffer, &siglist); buffer_add_efi_signature_data (buffer, &sigdata); /* serialize the DER encoding of the certificate */ return_val_if_fail (ex->cert_len <= SSIZE_MAX, false); p11_buffer_add (buffer, ex->cert_der, ex->cert_len); } if (rv != CKR_CANCEL) { p11_message ("failed to find certificate: %s", p11_kit_strerror (rv)); return false; } return_val_if_fail (p11_buffer_ok (buffer), false); return true; } bool p11_extract_edk2_cacerts (p11_enumerate *ex, const char *destination) { p11_buffer buffer; p11_save_file *file; bool ret; p11_buffer_init (&buffer, 1024 * 10); ret = prepare_edk2_buffer (ex, &buffer); if (ret) { file = p11_save_open_file (destination, NULL, ex->flags); ret = p11_save_write_and_finish (file, buffer.data, buffer.len); } p11_buffer_uninit (&buffer); return ret; }