From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Javier Martinez Canillas <javierm@redhat.com>
Date: Sat, 8 May 2021 02:27:58 +0200
Subject: [PATCH] appendedsig/x509: Also handle the Extended Key Usage
 extension

Red Hat certificates have both Key Usage and Extended Key Usage extensions
present, but the appended signatures x509 parser doesn't handle the latter
and so buils due finding an unrecognised critical extension:

Error loading initial key:
../../grub-core/commands/appendedsig/x509.c:780:Unhandled critical x509 extension with OID 2.5.29.37

Fix this by also parsing the Extended Key Usage extension and handle it by
verifying that the certificate has a single purpose, that is code signing.

Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
Signed-off-by: Daniel Axtens <dja@axtens.net>
---
 grub-core/commands/appendedsig/x509.c     | 94 ++++++++++++++++++++++++++++++-
 grub-core/tests/appended_signature_test.c | 29 +++++++++-
 grub-core/tests/appended_signatures.h     | 81 ++++++++++++++++++++++++++
 3 files changed, 201 insertions(+), 3 deletions(-)

diff --git a/grub-core/commands/appendedsig/x509.c b/grub-core/commands/appendedsig/x509.c
index 2b38b3670a2..42ec65c54aa 100644
--- a/grub-core/commands/appendedsig/x509.c
+++ b/grub-core/commands/appendedsig/x509.c
@@ -47,6 +47,12 @@ const char *keyUsage_oid = "2.5.29.15";
  */
 const char *basicConstraints_oid = "2.5.29.19";
 
+/*
+ * RFC 5280 4.2.1.12 Extended Key Usage
+ */
+const char *extendedKeyUsage_oid = "2.5.29.37";
+const char *codeSigningUsage_oid = "1.3.6.1.5.5.7.3.3";
+
 /*
  * RFC 3279 2.3.1
  *
@@ -637,6 +643,77 @@ cleanup:
   return err;
 }
 
+/*
+ * ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId
+ *
+ * KeyPurposeId ::= OBJECT IDENTIFIER
+ */
+static grub_err_t
+verify_extended_key_usage (grub_uint8_t * value, int value_size)
+{
+  asn1_node extendedasn;
+  int result, count;
+  grub_err_t err = GRUB_ERR_NONE;
+  char usage[MAX_OID_LEN];
+  int usage_size = sizeof (usage);
+
+  result =
+    asn1_create_element (_gnutls_pkix_asn, "PKIX1.ExtKeyUsageSyntax",
+			 &extendedasn);
+  if (result != ASN1_SUCCESS)
+    {
+      return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+			 "Could not create ASN.1 structure for Extended Key Usage");
+    }
+
+  result = asn1_der_decoding2 (&extendedasn, value, &value_size,
+			       ASN1_DECODE_FLAG_STRICT_DER, asn1_error);
+  if (result != ASN1_SUCCESS)
+    {
+      err =
+	grub_error (GRUB_ERR_BAD_FILE_TYPE,
+		    "Error parsing DER for Extended Key Usage: %s",
+		    asn1_error);
+      goto cleanup;
+    }
+
+  /*
+   * If EKUs are present, there must be exactly 1 and it must be a
+   * codeSigning usage.
+   */
+  result = asn1_number_of_elements(extendedasn, "", &count);
+  if (result != ASN1_SUCCESS)
+    {
+      err =
+	grub_error (GRUB_ERR_BAD_FILE_TYPE,
+		    "Error counting number of Extended Key Usages: %s",
+		    asn1_strerror (result));
+      goto cleanup;
+    }
+
+  result = asn1_read_value (extendedasn, "?1", usage, &usage_size);
+  if (result != ASN1_SUCCESS)
+    {
+      err =
+	grub_error (GRUB_ERR_BAD_FILE_TYPE,
+		    "Error reading Extended Key Usage: %s",
+		    asn1_strerror (result));
+      goto cleanup;
+    }
+
+  if (grub_strncmp (codeSigningUsage_oid, usage, usage_size) != 0)
+    {
+      err =
+	grub_error (GRUB_ERR_BAD_FILE_TYPE,
+		    "Unexpected Extended Key Usage OID, got: %s",
+		    usage);
+      goto cleanup;
+    }
+
+cleanup:
+  asn1_delete_structure (&extendedasn);
+  return err;
+}
 
 /*
  * Extensions  ::=  SEQUENCE SIZE (1..MAX) OF Extension
@@ -660,7 +737,7 @@ verify_extensions (asn1_node cert)
 {
   int result;
   int ext, num_extensions = 0;
-  int usage_present = 0, constraints_present = 0;
+  int usage_present = 0, constraints_present = 0, extended_usage_present = 0;
   char *oid_path, *critical_path, *value_path;
   char extnID[MAX_OID_LEN];
   int extnID_size;
@@ -754,6 +831,15 @@ verify_extensions (asn1_node cert)
 	    }
 	  constraints_present++;
 	}
+      else if (grub_strncmp (extendedKeyUsage_oid, extnID, extnID_size) == 0)
+	{
+	  err = verify_extended_key_usage (value, value_size);
+	  if (err != GRUB_ERR_NONE)
+	    {
+	      goto cleanup_value;
+	    }
+	  extended_usage_present++;
+	}
       else if (grub_strncmp ("TRUE", critical, critical_size) == 0)
 	{
 	  /*
@@ -785,6 +871,12 @@ verify_extensions (asn1_node cert)
 			 "Unexpected number of basic constraints extensions - expected 1, got %d",
 			 constraints_present);
     }
+  if (extended_usage_present > 1)
+    {
+      return grub_error (GRUB_ERR_BAD_FILE_TYPE,
+			 "Unexpected number of Extended Key Usage extensions - expected 0 or 1, got %d",
+			 extended_usage_present);
+    }
   return GRUB_ERR_NONE;
 
 cleanup_value:
diff --git a/grub-core/tests/appended_signature_test.c b/grub-core/tests/appended_signature_test.c
index 88a485200d8..dbba0616621 100644
--- a/grub-core/tests/appended_signature_test.c
+++ b/grub-core/tests/appended_signature_test.c
@@ -111,6 +111,22 @@ static struct grub_procfs_entry certificate_printable_der_entry = {
   .get_contents = get_certificate_printable_der
 };
 
+static char *
+get_certificate_eku_der (grub_size_t * sz)
+{
+  char *ret;
+  *sz = certificate_eku_der_len;
+  ret = grub_malloc (*sz);
+  if (ret)
+    grub_memcpy (ret, certificate_eku_der, *sz);
+  return ret;
+}
+
+static struct grub_procfs_entry certificate_eku_der_entry = {
+  .name = "certificate_eku.der",
+  .get_contents = get_certificate_eku_der
+};
+
 
 static void
 do_verify (const char *f, int is_valid)
@@ -149,6 +165,7 @@ appended_signature_test (void)
   char *trust_args2[] = { (char *) "(proc)/certificate2.der", NULL };
   char *trust_args_printable[] = { (char *) "(proc)/certificate_printable.der",
 				   NULL };
+  char *trust_args_eku[] = { (char *) "(proc)/certificate_eku.der", NULL };
   char *distrust_args[] = { (char *) "1", NULL };
   char *distrust2_args[] = { (char *) "2", NULL };
   grub_err_t err;
@@ -157,6 +174,7 @@ appended_signature_test (void)
   grub_procfs_register ("certificate2.der", &certificate2_der_entry);
   grub_procfs_register ("certificate_printable.der",
 			&certificate_printable_der_entry);
+  grub_procfs_register ("certificate_eku.der", &certificate_eku_der_entry);
 
   cmd_trust = grub_command_find ("trust_certificate");
   if (!cmd_trust)
@@ -266,16 +284,23 @@ appended_signature_test (void)
 
   /*
    * Lastly, check a certificate that uses printableString rather than
-   * utf8String loads properly.
+   * utf8String loads properly, and that a certificate with an appropriate
+   * extended key usage loads.
    */
   err = (cmd_trust->func) (cmd_trust, 1, trust_args_printable);
   grub_test_assert (err == GRUB_ERR_NONE,
-		    "distrusting printable certificate failed: %d: %s",
+		    "trusting printable certificate failed: %d: %s",
+		    grub_errno, grub_errmsg);
+
+  err = (cmd_trust->func) (cmd_trust, 1, trust_args_eku);
+  grub_test_assert (err == GRUB_ERR_NONE,
+		    "trusting certificate with extended key usage failed: %d: %s",
 		    grub_errno, grub_errmsg);
 
   grub_procfs_unregister (&certificate_der_entry);
   grub_procfs_unregister (&certificate2_der_entry);
   grub_procfs_unregister (&certificate_printable_der_entry);
+  grub_procfs_unregister (&certificate_eku_der_entry);
 }
 
 GRUB_FUNCTIONAL_TEST (appended_signature_test, appended_signature_test);
diff --git a/grub-core/tests/appended_signatures.h b/grub-core/tests/appended_signatures.h
index aa3dc6278e3..2e5ebd7d8bd 100644
--- a/grub-core/tests/appended_signatures.h
+++ b/grub-core/tests/appended_signatures.h
@@ -555,3 +555,84 @@ unsigned char certificate_printable_der[] = {
   0xd2
 };
 unsigned int certificate_printable_der_len = 829;
+
+unsigned char certificate_eku_der[] = {
+  0x30, 0x82, 0x03, 0x90, 0x30, 0x82, 0x02, 0x78, 0xa0, 0x03, 0x02, 0x01,
+  0x02, 0x02, 0x09, 0x00, 0xd3, 0x9c, 0x41, 0x33, 0xdd, 0x6b, 0x5f, 0x45,
+  0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+  0x0b, 0x05, 0x00, 0x30, 0x47, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55,
+  0x04, 0x03, 0x0c, 0x18, 0x52, 0x65, 0x64, 0x20, 0x48, 0x61, 0x74, 0x20,
+  0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x42, 0x6f, 0x6f, 0x74, 0x20,
+  0x43, 0x41, 0x20, 0x36, 0x31, 0x22, 0x30, 0x20, 0x06, 0x09, 0x2a, 0x86,
+  0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x13, 0x73, 0x65, 0x63,
+  0x61, 0x6c, 0x65, 0x72, 0x74, 0x40, 0x72, 0x65, 0x64, 0x68, 0x61, 0x74,
+  0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x1e, 0x17, 0x0d, 0x32, 0x31, 0x30, 0x32,
+  0x31, 0x35, 0x31, 0x34, 0x30, 0x30, 0x34, 0x34, 0x5a, 0x17, 0x0d, 0x33,
+  0x38, 0x30, 0x31, 0x31, 0x37, 0x31, 0x34, 0x30, 0x30, 0x34, 0x34, 0x5a,
+  0x30, 0x4e, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c,
+  0x1f, 0x52, 0x65, 0x64, 0x20, 0x48, 0x61, 0x74, 0x20, 0x53, 0x65, 0x63,
+  0x75, 0x72, 0x65, 0x20, 0x42, 0x6f, 0x6f, 0x74, 0x20, 0x53, 0x69, 0x67,
+  0x6e, 0x69, 0x6e, 0x67, 0x20, 0x36, 0x30, 0x32, 0x31, 0x22, 0x30, 0x20,
+  0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16,
+  0x13, 0x73, 0x65, 0x63, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x40, 0x72, 0x65,
+  0x64, 0x68, 0x61, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x82, 0x01, 0x22,
+  0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+  0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a,
+  0x02, 0x82, 0x01, 0x01, 0x00, 0xaa, 0x6f, 0xbb, 0x92, 0x77, 0xd7, 0x15,
+  0xef, 0x88, 0x80, 0x88, 0xc0, 0xe7, 0x89, 0xeb, 0x35, 0x76, 0xf4, 0x85,
+  0x05, 0x0f, 0x19, 0xe4, 0x5f, 0x25, 0xdd, 0xc1, 0xa2, 0xe5, 0x5c, 0x06,
+  0xfb, 0xf1, 0x06, 0xb5, 0x65, 0x45, 0xcb, 0xbd, 0x19, 0x33, 0x54, 0xb5,
+  0x1a, 0xcd, 0xe4, 0xa8, 0x35, 0x2a, 0xfe, 0x9c, 0x53, 0xf4, 0xc6, 0x76,
+  0xdb, 0x1f, 0x8a, 0xd4, 0x7b, 0x18, 0x11, 0xaf, 0xa3, 0x90, 0xd4, 0xdd,
+  0x4d, 0xd5, 0x42, 0xcc, 0x14, 0x9a, 0x64, 0x6b, 0xc0, 0x7f, 0xaa, 0x1c,
+  0x94, 0x47, 0x4d, 0x79, 0xbd, 0x57, 0x9a, 0xbf, 0x99, 0x4e, 0x96, 0xa9,
+  0x31, 0x2c, 0xa9, 0xe7, 0x14, 0x65, 0x86, 0xc8, 0xac, 0x79, 0x5e, 0x78,
+  0xa4, 0x3c, 0x00, 0x24, 0xd3, 0xf7, 0xe1, 0xf5, 0x12, 0xad, 0xa0, 0x29,
+  0xe5, 0xfe, 0x80, 0xae, 0xf8, 0xaa, 0x60, 0x36, 0xe7, 0xe8, 0x94, 0xcb,
+  0xe9, 0xd1, 0xcc, 0x0b, 0x4d, 0xf7, 0xde, 0xeb, 0x52, 0xd2, 0x73, 0x09,
+  0x28, 0xdf, 0x48, 0x99, 0x53, 0x9f, 0xc5, 0x9a, 0xd4, 0x36, 0xa3, 0xc6,
+  0x5e, 0x8d, 0xbe, 0xd5, 0xdc, 0x76, 0xb4, 0x74, 0xb8, 0x26, 0x18, 0x27,
+  0xfb, 0xf2, 0xfb, 0xd0, 0x9b, 0x3d, 0x7f, 0x10, 0xe2, 0xab, 0x44, 0xc7,
+  0x88, 0x7f, 0xb4, 0x3d, 0x3e, 0xa3, 0xff, 0x6d, 0x06, 0x4b, 0x3e, 0x55,
+  0xb2, 0x84, 0xf4, 0xad, 0x54, 0x88, 0x81, 0xc3, 0x9c, 0xf8, 0xb6, 0x68,
+  0x96, 0x38, 0x8b, 0xcd, 0x90, 0x6d, 0x25, 0x4b, 0xbf, 0x0c, 0x44, 0x90,
+  0xa5, 0x5b, 0x98, 0xd0, 0x40, 0x2f, 0xbb, 0x0d, 0xa8, 0x4b, 0x8a, 0x62,
+  0x82, 0x46, 0x46, 0x18, 0x38, 0xae, 0x82, 0x07, 0xd0, 0xb4, 0x2f, 0x16,
+  0x79, 0x55, 0x9f, 0x1b, 0xc5, 0x08, 0x6d, 0x85, 0xdf, 0x3f, 0xa9, 0x9b,
+  0x4b, 0xc6, 0x28, 0xd3, 0x58, 0x72, 0x3d, 0x37, 0x11, 0x02, 0x03, 0x01,
+  0x00, 0x01, 0xa3, 0x78, 0x30, 0x76, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d,
+  0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00, 0x30, 0x0e, 0x06, 0x03,
+  0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x07, 0x80,
+  0x30, 0x16, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x01, 0x01, 0xff, 0x04, 0x0c,
+  0x30, 0x0a, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x03,
+  0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x6c,
+  0xe4, 0x6c, 0x27, 0xaa, 0xcd, 0x0d, 0x4b, 0x74, 0x21, 0xa4, 0xf6, 0x5f,
+  0x87, 0xb5, 0x31, 0xfe, 0x10, 0xbb, 0xa7, 0x30, 0x1f, 0x06, 0x03, 0x55,
+  0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xe8, 0x6a, 0x1c, 0xab,
+  0x2c, 0x48, 0xf9, 0x60, 0x36, 0xa2, 0xf0, 0x7b, 0x8e, 0xd2, 0x9d, 0xb4,
+  0x2a, 0x28, 0x98, 0xc8, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+  0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00,
+  0x55, 0x34, 0xe2, 0xfa, 0xf6, 0x89, 0x86, 0xad, 0x92, 0x21, 0xec, 0xb9,
+  0x54, 0x0e, 0x18, 0x47, 0x0d, 0x1b, 0xa7, 0x58, 0xad, 0x69, 0xe4, 0xef,
+  0x3b, 0xe6, 0x8d, 0xdd, 0xda, 0x0c, 0x45, 0xf6, 0xe8, 0x96, 0xa4, 0x29,
+  0x0f, 0xbb, 0xcf, 0x16, 0xae, 0x93, 0xd0, 0xcb, 0x2a, 0x26, 0x1a, 0x7b,
+  0xfc, 0x51, 0x22, 0x76, 0x98, 0x31, 0xa7, 0x0f, 0x29, 0x35, 0x79, 0xbf,
+  0xe2, 0x4f, 0x0f, 0x14, 0xf5, 0x1f, 0xcb, 0xbf, 0x87, 0x65, 0x13, 0x32,
+  0xa3, 0x19, 0x4a, 0xd1, 0x3f, 0x45, 0xd4, 0x4b, 0xe2, 0x00, 0x26, 0xa9,
+  0x3e, 0xd7, 0xa5, 0x37, 0x9f, 0xf5, 0xad, 0x61, 0xe2, 0x40, 0xa9, 0x74,
+  0x24, 0x53, 0xf2, 0x78, 0xeb, 0x10, 0x9b, 0x2c, 0x27, 0x88, 0x46, 0xcb,
+  0xe4, 0x60, 0xca, 0xf5, 0x06, 0x24, 0x40, 0x2a, 0x97, 0x3a, 0xcc, 0xd0,
+  0x81, 0xb1, 0x15, 0xa3, 0x4f, 0xd0, 0x2b, 0x4f, 0xca, 0x6e, 0xaa, 0x24,
+  0x31, 0xb3, 0xac, 0xa6, 0x75, 0x05, 0xfe, 0x8a, 0xf4, 0x41, 0xc4, 0x06,
+  0x8a, 0xc7, 0x0a, 0x83, 0x4e, 0x49, 0xd4, 0x3f, 0x83, 0x50, 0xec, 0x57,
+  0x04, 0x97, 0x14, 0x49, 0xf5, 0xe1, 0xb1, 0x7a, 0x9c, 0x09, 0x4f, 0x61,
+  0x87, 0xc3, 0x97, 0x22, 0x17, 0xc2, 0xeb, 0xcc, 0x32, 0x81, 0x31, 0x21,
+  0x3f, 0x10, 0x57, 0x5b, 0x43, 0xbe, 0xcd, 0x68, 0x82, 0xbe, 0xe5, 0xc1,
+  0x65, 0x94, 0x7e, 0xc2, 0x34, 0x76, 0x2b, 0xcf, 0x89, 0x3c, 0x2b, 0x81,
+  0x23, 0x72, 0x95, 0xcf, 0xc9, 0x67, 0x19, 0x2a, 0xd5, 0x5c, 0xca, 0xa3,
+  0x46, 0xbd, 0x48, 0x06, 0x0b, 0xa6, 0xa3, 0x96, 0x50, 0x28, 0xc7, 0x7e,
+  0xcf, 0x62, 0xf2, 0xfa, 0xc4, 0xf2, 0x53, 0xe3, 0xc9, 0xe8, 0x2e, 0xdd,
+  0x29, 0x37, 0x07, 0x47, 0xff, 0xff, 0x8a, 0x32, 0xbd, 0xa2, 0xb7, 0x21,
+  0x89, 0xa0, 0x55, 0xf7
+};
+unsigned int certificate_eku_der_len = 916;
