]> granicus.if.org Git - p11-kit/commitdiff
trust: implement the "edk2-cacerts" extractor
authorLaszlo Ersek <lersek@redhat.com>
Tue, 27 Mar 2018 15:28:11 +0000 (17:28 +0200)
committerDaiki Ueno <ueno@gnu.org>
Fri, 30 Mar 2018 06:45:49 +0000 (08:45 +0200)
Extract the DER-encoded X.509 certificates in the EFI_SIGNATURE_LIST
format that is

- defined by the UEFI 2.7 spec (using one inner EFI_SIGNATURE_DATA object
  per EFI_SIGNATURE_LIST, as specified for EFI_CERT_X509_GUID),

- and expected by edk2's HttpDxe when it configures the certificate list
  for HTTPS boot from EFI_TLS_CA_CERTIFICATE_VARIABLE (see the
  TlsConfigCertificate() function in "NetworkPkg/HttpDxe/HttpsSupport.c").

The intended command line is

  p11-kit extract \
    --format=edk2-cacerts \
    --filter=ca-anchors \
    --overwrite \
    --purpose=server-auth \
    $DEST/edk2/cacerts.bin

Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1559580
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
trust/extract-edk2.c

index 5bde0601579d3c286532c24c398c1b39861e18bd..d9893464afc720818203c40fd8d785450b1b8ffd 100644 (file)
 
 #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;
 }