diff options
-rw-r--r-- | trust/extract-edk2.c | 169 |
1 files changed, 168 insertions, 1 deletions
diff --git a/trust/extract-edk2.c b/trust/extract-edk2.c index 5bde060..d989346 100644 --- a/trust/extract-edk2.c +++ b/trust/extract-edk2.c @@ -34,11 +34,178 @@ #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 <stdint.h> /* UINT32_MAX */ +#include <limits.h> /* 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) { - return false; + 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; } |