grub2/0394-appended-signatures-Parse-X.509-certificates.patch
Nicolas Frayer 17ffd9b3e0 powerpc: Add appended signature feature
Resolves: #RHEL-24510
Signed-off-by: Nicolas Frayer <nfrayer@redhat.com>
2025-11-27 14:40:11 +01:00

1591 lines
51 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Sudhakar Kuppusamy <sudhakar@linux.ibm.com>
Date: Tue, 18 Nov 2025 16:25:34 +0100
Subject: [PATCH] appended signatures: Parse X.509 certificates
This code allows us to parse:
- X.509 certificates: at least enough to verify the signatures on the PKCS#7
messages. We expect that the certificates embedded in GRUB will be leaf
certificates, not CA certificates. The parser enforces this.
- X.509 certificates support the Extended Key Usage extension and handle it by
verifying that the certificate has a Code Signing usage.
Signed-off-by: Javier Martinez Canillas <javierm@redhat.com> # EKU support
Reported-by: Michal Suchanek <msuchanek@suse.com> # key usage issue
Signed-off-by: Daniel Axtens <dja@axtens.net>
Signed-off-by: Sudhakar Kuppusamy <sudhakar@linux.ibm.com>
Reviewed-by: Avnish Chouhan <avnish@linux.ibm.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
grub-core/commands/appendedsig/appendedsig.h | 52 ++
grub-core/commands/appendedsig/x509.c | 1012 ++++++++++++--------------
include/grub/crypto.h | 1 +
3 files changed, 519 insertions(+), 546 deletions(-)
diff --git a/grub-core/commands/appendedsig/appendedsig.h b/grub-core/commands/appendedsig/appendedsig.h
index b0beb89..c874654 100644
--- a/grub-core/commands/appendedsig/appendedsig.h
+++ b/grub-core/commands/appendedsig/appendedsig.h
@@ -25,6 +25,43 @@ extern asn1_node grub_gnutls_pkix_asn;
#define GRUB_MAX_OID_LEN 32
+/* RSA public key. */
+#define GRUB_MAX_MPI 2
+#define GRUB_RSA_PK_MODULUS 0
+#define GRUB_RSA_PK_EXPONENT 1
+
+/* Certificate fingerprint. */
+#define GRUB_MAX_FINGERPRINT 3
+#define GRUB_FINGERPRINT_SHA256 0
+#define GRUB_FINGERPRINT_SHA384 1
+#define GRUB_FINGERPRINT_SHA512 2
+
+/* Max size of hash data. */
+#define GRUB_MAX_HASH_LEN 64
+
+/*
+ * One or more x509 certificates. We do limited parsing:
+ * extracting only the version, serial, issuer, subject, RSA public key
+ * and key size.
+ * Also, hold the sha256, sha384, and sha512 fingerprint of the certificate.
+ */
+struct x509_certificate
+{
+ struct x509_certificate *next;
+ grub_uint8_t version;
+ grub_uint8_t *serial;
+ grub_size_t serial_len;
+ char *issuer;
+ grub_size_t issuer_len;
+ char *subject;
+ grub_size_t subject_len;
+ /* We only support RSA public keys. This encodes [modulus, publicExponent]. */
+ gcry_mpi_t mpis[GRUB_MAX_MPI];
+ grub_int32_t modulus_size;
+ grub_uint8_t fingerprint[GRUB_MAX_FINGERPRINT][GRUB_MAX_HASH_LEN];
+};
+typedef struct x509_certificate grub_x509_cert_t;
+
/* A PKCS#7 signed data signer info. */
struct pkcs7_signer
{
@@ -44,6 +81,21 @@ struct pkcs7_data
};
typedef struct pkcs7_data grub_pkcs7_data_t;
+/*
+ * Import a DER-encoded certificate at 'data', of size 'size'. Place the results
+ * into 'results', which must be already allocated.
+ */
+extern grub_err_t
+grub_x509_cert_parse (const void *data, grub_size_t size, grub_x509_cert_t *results);
+
+/*
+ * Release all the storage associated with the x509 certificate. If the caller
+ * dynamically allocated the certificate, it must free it. The caller is also
+ * responsible for maintenance of the linked list.
+ */
+extern void
+grub_x509_cert_release (grub_x509_cert_t *cert);
+
/*
* Parse a PKCS#7 message, which must be a signed data message. The message must
* be in 'sigbuf' and of size 'data_size'. The result is placed in 'msg', which
diff --git a/grub-core/commands/appendedsig/x509.c b/grub-core/commands/appendedsig/x509.c
index 42ec65c..bc15266 100644
--- a/grub-core/commands/appendedsig/x509.c
+++ b/grub-core/commands/appendedsig/x509.c
@@ -1,6 +1,7 @@
/*
* GRUB -- GRand Unified Bootloader
- * Copyright (C) 2020 IBM Corporation.
+ * Copyright (C) 2020, 2022 Free Software Foundation, Inc.
+ * Copyright (C) 2020, 2022, 2025 IBM Corporation
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -16,42 +17,36 @@
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <grub/libtasn1.h>
+#include <libtasn1.h>
#include <grub/types.h>
#include <grub/err.h>
#include <grub/mm.h>
#include <grub/crypto.h>
+#include <grub/misc.h>
#include <grub/gcrypt/gcrypt.h>
#include "appendedsig.h"
static char asn1_error[ASN1_MAX_ERROR_DESCRIPTION_SIZE];
-/*
- * RFC 3279 2.3.1 RSA Keys
- */
-const char *rsaEncryption_oid = "1.2.840.113549.1.1.1";
-/*
- * RFC 5280 Appendix A
- */
-const char *commonName_oid = "2.5.4.3";
+/* RFC 3279 2.3.1 RSA Keys. */
+static const char *rsaEncryption_oid = "1.2.840.113549.1.1.1";
-/*
- * RFC 5280 4.2.1.3 Key Usage
- */
-const char *keyUsage_oid = "2.5.29.15";
+/* RFC 5280 Appendix A. */
+static const char *commonName_oid = "2.5.4.3";
-/*
- * RFC 5280 4.2.1.9 Basic Constraints
- */
-const char *basicConstraints_oid = "2.5.29.19";
+/* RFC 5280 4.2.1.3 Key Usage. */
+static const char *keyUsage_oid = "2.5.29.15";
-/*
- * 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";
+static const grub_uint8_t digitalSignatureUsage = 0x80;
+
+/* RFC 5280 4.2.1.9 Basic Constraints. */
+static const char *basicConstraints_oid = "2.5.29.19";
+
+/* RFC 5280 4.2.1.12 Extended Key Usage. */
+static const char *extendedKeyUsage_oid = "2.5.29.37";
+static const char *codeSigningUsage_oid = "1.3.6.1.5.5.7.3.3";
/*
* RFC 3279 2.3.1
@@ -62,103 +57,88 @@ const char *codeSigningUsage_oid = "1.3.6.1.5.5.7.3.3";
* modulus INTEGER, -- n
* publicExponent INTEGER } -- e
*
- * where modulus is the modulus n, and publicExponent is the public
- * exponent e.
+ * where modulus is the modulus n, and publicExponent is the public exponent e.
*/
static grub_err_t
-grub_parse_rsa_pubkey (grub_uint8_t * der, int dersize,
- struct x509_certificate *certificate)
+grub_parse_rsa_pubkey (grub_uint8_t *der, grub_int32_t dersize, grub_x509_cert_t *certificate)
{
- int result;
- asn1_node spk = ASN1_TYPE_EMPTY;
+ grub_int32_t result;
+ asn1_node spk = NULL;
grub_uint8_t *m_data, *e_data;
- int m_size, e_size;
+ grub_int32_t m_size, e_size;
grub_err_t err = GRUB_ERR_NONE;
gcry_error_t gcry_err;
- result =
- asn1_create_element (_gnutls_gnutls_asn, "GNUTLS.RSAPublicKey", &spk);
+ result = asn1_create_element (grub_gnutls_gnutls_asn, "GNUTLS.RSAPublicKey", &spk);
if (result != ASN1_SUCCESS)
- {
- return grub_error (GRUB_ERR_OUT_OF_MEMORY,
- "Cannot create storage for public key ASN.1 data");
- }
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "cannot create storage for public key ASN.1 data");
- result = asn1_der_decoding2 (&spk, der, &dersize,
- ASN1_DECODE_FLAG_STRICT_DER, asn1_error);
+ result = asn1_der_decoding2 (&spk, der, &dersize, ASN1_DECODE_FLAG_STRICT_DER, asn1_error);
if (result != ASN1_SUCCESS)
{
- err =
- grub_error (GRUB_ERR_BAD_FILE_TYPE,
- "Cannot decode certificate public key DER: %s",
- asn1_error);
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "cannot decode certificate public key DER: %s", asn1_error);
goto cleanup;
}
- m_data =
- grub_asn1_allocate_and_read (spk, "modulus", "RSA modulus", &m_size);
- if (!m_data)
+ m_data = grub_asn1_allocate_and_read (spk, "modulus", "RSA modulus", &m_size);
+ if (m_data == NULL)
{
err = grub_errno;
goto cleanup;
}
- e_data =
- grub_asn1_allocate_and_read (spk, "publicExponent", "RSA public exponent",
- &e_size);
- if (!e_data)
+ e_data = grub_asn1_allocate_and_read (spk, "publicExponent", "RSA public exponent", &e_size);
+ if (e_data == NULL)
{
err = grub_errno;
goto cleanup_m_data;
}
/*
- * convert m, e to mpi
+ * Convert m, e to mpi
*
- * nscanned is not set for FMT_USG, it's only set for FMT_PGP,
- * so we can't verify it
+ * nscanned is not set for FMT_USG, it's only set for FMT_PGP, so we can't
+ * verify it.
*/
- gcry_err =
- gcry_mpi_scan (&certificate->mpis[0], GCRYMPI_FMT_USG, m_data, m_size,
- NULL);
+ gcry_err = _gcry_mpi_scan (&certificate->mpis[0], GCRYMPI_FMT_USG, m_data, m_size, NULL);
if (gcry_err != GPG_ERR_NO_ERROR)
{
- err =
- grub_error (GRUB_ERR_BAD_FILE_TYPE,
- "Error loading RSA modulus into MPI structure: %d",
- gcry_err);
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "error loading RSA modulus into MPI structure: %d", gcry_err);
goto cleanup_e_data;
}
- gcry_err =
- gcry_mpi_scan (&certificate->mpis[1], GCRYMPI_FMT_USG, e_data, e_size,
- NULL);
+ gcry_err = _gcry_mpi_scan (&certificate->mpis[1], GCRYMPI_FMT_USG, e_data, e_size, NULL);
if (gcry_err != GPG_ERR_NO_ERROR)
{
- err =
- grub_error (GRUB_ERR_BAD_FILE_TYPE,
- "Error loading RSA exponent into MPI structure: %d",
- gcry_err);
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "error loading RSA exponent into MPI structure: %d", gcry_err);
goto cleanup_m_mpi;
}
+ /* RSA key size in bits. */
+ certificate->modulus_size = (m_size * 8) - 8;
+
grub_free (e_data);
grub_free (m_data);
asn1_delete_structure (&spk);
+
return GRUB_ERR_NONE;
-cleanup_m_mpi:
- gcry_mpi_release (certificate->mpis[0]);
-cleanup_e_data:
+ cleanup_m_mpi:
+ _gcry_mpi_release (certificate->mpis[0]);
+ cleanup_e_data:
grub_free (e_data);
-cleanup_m_data:
+ cleanup_m_data:
grub_free (m_data);
-cleanup:
+ cleanup:
asn1_delete_structure (&spk);
+
return err;
}
-
/*
* RFC 5280:
* SubjectPublicKeyInfo ::= SEQUENCE {
@@ -168,63 +148,45 @@ cleanup:
* AlgorithmIdentifiers come from RFC 3279, we are not strictly compilant as we
* only support RSA Encryption.
*/
-
static grub_err_t
-grub_x509_read_subject_public_key (asn1_node asn,
- struct x509_certificate *results)
+grub_x509_read_subject_public_key (asn1_node asn, grub_x509_cert_t *results)
{
- int result;
+ grub_int32_t result;
grub_err_t err;
- const char *algo_name =
- "tbsCertificate.subjectPublicKeyInfo.algorithm.algorithm";
- const char *params_name =
- "tbsCertificate.subjectPublicKeyInfo.algorithm.parameters";
- const char *pk_name =
- "tbsCertificate.subjectPublicKeyInfo.subjectPublicKey";
- char algo_oid[MAX_OID_LEN];
- int algo_size = sizeof (algo_oid);
+ const char *algo_name = "tbsCertificate.subjectPublicKeyInfo.algorithm.algorithm";
+ const char *params_name = "tbsCertificate.subjectPublicKeyInfo.algorithm.parameters";
+ const char *pk_name = "tbsCertificate.subjectPublicKeyInfo.subjectPublicKey";
+ char algo_oid[GRUB_MAX_OID_LEN];
+ grub_int32_t algo_size = sizeof (algo_oid);
char params_value[2];
- int params_size = sizeof (params_value);
+ grub_int32_t params_size = sizeof (params_value);
grub_uint8_t *key_data = NULL;
- int key_size = 0;
- unsigned int key_type;
+ grub_int32_t key_size = 0;
+ grub_uint32_t key_type;
- /* algorithm: see notes for rsaEncryption_oid */
+ /* Algorithm: see notes for rsaEncryption_oid. */
result = asn1_read_value (asn, algo_name, algo_oid, &algo_size);
if (result != ASN1_SUCCESS)
- {
- return grub_error (GRUB_ERR_BAD_FILE_TYPE,
- "Error reading x509 public key algorithm: %s",
- asn1_strerror (result));
- }
+ return grub_error (GRUB_ERR_BAD_FILE_TYPE, "error reading x509 public key algorithm: %s",
+ asn1_strerror (result));
- if (grub_strncmp (algo_oid, rsaEncryption_oid, sizeof (rsaEncryption_oid))
- != 0)
- {
- return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
- "Unsupported x509 public key algorithm: %s",
- algo_oid);
- }
+ if (grub_strncmp (algo_oid, rsaEncryption_oid, sizeof (rsaEncryption_oid)) != 0)
+ return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
+ "unsupported x509 public key algorithm: %s", algo_oid);
- /*
- * RFC 3279 2.3.1
- * The rsaEncryption OID is intended to be used in the algorithm field
- * of a value of type AlgorithmIdentifier. The parameters field MUST
- * have ASN.1 type NULL for this algorithm identifier.
+ /*
+ * RFC 3279 2.3.1 : The rsaEncryption OID is intended to be used in the
+ * algorithm field of a value of type AlgorithmIdentifier. The parameters
+ * field MUST have ASN.1 type NULL for this algorithm identifier.
*/
result = asn1_read_value (asn, params_name, params_value, &params_size);
if (result != ASN1_SUCCESS)
- {
- return grub_error (GRUB_ERR_BAD_FILE_TYPE,
- "Error reading x509 public key parameters: %s",
- asn1_strerror (result));
- }
+ return grub_error (GRUB_ERR_BAD_FILE_TYPE, "error reading x509 public key parameters: %s",
+ asn1_strerror (result));
if (params_value[0] != ASN1_TAG_NULL)
- {
- return grub_error (GRUB_ERR_BAD_FILE_TYPE,
- "Invalid x509 public key parameters: expected NULL");
- }
+ return grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "invalid x509 public key parameters: expected NULL");
/*
* RFC 3279 2.3.1: The DER encoded RSAPublicKey is the value of the BIT
@@ -232,132 +194,122 @@ grub_x509_read_subject_public_key (asn1_node asn,
*/
result = asn1_read_value_type (asn, pk_name, NULL, &key_size, &key_type);
if (result != ASN1_MEM_ERROR)
- {
- return grub_error (GRUB_ERR_BAD_FILE_TYPE,
- "Error reading size of x509 public key: %s",
- asn1_strerror (result));
- }
+ return grub_error (GRUB_ERR_BAD_FILE_TYPE, "error reading size of x509 public key: %s",
+ asn1_strerror (result));
if (key_type != ASN1_ETYPE_BIT_STRING)
- {
- return grub_error (GRUB_ERR_BAD_FILE_TYPE,
- "Unexpected ASN.1 type when reading x509 public key: %x",
- key_type);
- }
+ return grub_error (GRUB_ERR_BAD_FILE_TYPE, "unexpected ASN.1 type when reading x509 public key: %x",
+ key_type);
- /* length is in bits */
+ /* Length is in bits. */
key_size = (key_size + 7) / 8;
key_data = grub_malloc (key_size);
- if (!key_data)
- {
- return grub_error (GRUB_ERR_OUT_OF_MEMORY,
- "Out of memory for x509 public key");
- }
+ if (key_data == NULL)
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory for x509 public key");
result = asn1_read_value (asn, pk_name, key_data, &key_size);
if (result != ASN1_SUCCESS)
{
grub_free (key_data);
- return grub_error (GRUB_ERR_BAD_FILE_TYPE,
- "Error reading public key data");
+ return grub_error (GRUB_ERR_BAD_FILE_TYPE, "error reading public key data");
}
- key_size = (key_size + 7) / 8;
+ key_size = (key_size + 7) / 8;
err = grub_parse_rsa_pubkey (key_data, key_size, results);
grub_free (key_data);
return err;
}
-/* Decode a string as defined in Appendix A */
+/* Decode a string as defined in Appendix A. */
static grub_err_t
-decode_string (char *der, int der_size, char **string,
- grub_size_t * string_size)
+decode_string (char *der, grub_int32_t der_size, char **string, grub_size_t *string_size)
{
asn1_node strasn;
- int result;
+ grub_int32_t result;
char *choice;
- int choice_size = 0;
- int tmp_size = 0;
+ grub_int32_t choice_size = 0;
+ grub_int32_t tmp_size = 0;
grub_err_t err = GRUB_ERR_NONE;
- result =
- asn1_create_element (_gnutls_pkix_asn, "PKIX1.DirectoryString", &strasn);
+ result = asn1_create_element (grub_gnutls_pkix_asn, "PKIX1.DirectoryString", &strasn);
if (result != ASN1_SUCCESS)
- {
- return grub_error (GRUB_ERR_OUT_OF_MEMORY,
- "Could not create ASN.1 structure for certificate: %s",
- asn1_strerror (result));
- }
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "could not create ASN.1 structure for certificate: %s",
+ asn1_strerror (result));
- result = asn1_der_decoding2 (&strasn, der, &der_size,
- ASN1_DECODE_FLAG_STRICT_DER, asn1_error);
+ result = asn1_der_decoding2 (&strasn, der, &der_size, ASN1_DECODE_FLAG_STRICT_DER, asn1_error);
if (result != ASN1_SUCCESS)
{
- err =
- grub_error (GRUB_ERR_BAD_FILE_TYPE,
- "Could not parse DER for DirectoryString: %s",
- asn1_error);
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "could not parse DER for DirectoryString: %s", asn1_error);
goto cleanup;
}
- choice =
- grub_asn1_allocate_and_read (strasn, "", "DirectoryString choice",
- &choice_size);
- if (!choice)
+ choice = grub_asn1_allocate_and_read (strasn, "", "DirectoryString choice", &choice_size);
+ if (choice == NULL)
{
err = grub_errno;
goto cleanup;
}
- if (grub_strncmp ("utf8String", choice, choice_size))
+ if (grub_strncmp ("utf8String", choice, choice_size) == 0)
{
- err =
- grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
- "Only UTF-8 DirectoryStrings are supported, got %s",
- choice);
- goto cleanup_choice;
+ result = asn1_read_value (strasn, "utf8String", NULL, &tmp_size);
+ if (result != ASN1_MEM_ERROR)
+ {
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "error reading size of UTF-8 string: %s",
+ asn1_strerror (result));
+ goto cleanup_choice;
+ }
}
-
- result = asn1_read_value (strasn, "utf8String", NULL, &tmp_size);
- if (result != ASN1_MEM_ERROR)
+ else if (grub_strncmp ("printableString", choice, choice_size) == 0)
+ {
+ result = asn1_read_value (strasn, "printableString", NULL, &tmp_size);
+ if (result != ASN1_MEM_ERROR)
+ {
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "error reading size of printableString: %s",
+ asn1_strerror (result));
+ goto cleanup_choice;
+ }
+ }
+ else
{
- err =
- grub_error (GRUB_ERR_BAD_FILE_TYPE,
- "Error reading size of UTF-8 string: %s",
- asn1_strerror (result));
+ err = grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
+ "only UTF-8 and printable DirectoryStrings are supported, got %s",
+ choice);
goto cleanup_choice;
}
- /* read size does not include trailing null */
+ /* Read size does not include trailing NUL. */
tmp_size++;
*string = grub_malloc (tmp_size);
- if (!*string)
+ if (*string == NULL)
{
- err =
- grub_error (GRUB_ERR_OUT_OF_MEMORY,
- "Cannot allocate memory for DirectoryString contents");
+ err = grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "cannot allocate memory for DirectoryString contents");
goto cleanup_choice;
}
- result = asn1_read_value (strasn, "utf8String", *string, &tmp_size);
+ result = asn1_read_value (strasn, choice, *string, &tmp_size);
if (result != ASN1_SUCCESS)
{
- err =
- grub_error (GRUB_ERR_BAD_FILE_TYPE,
- "Error reading out UTF-8 string in DirectoryString: %s",
- asn1_strerror (result));
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "error reading out %s in DirectoryString: %s",
+ choice, asn1_strerror (result));
grub_free (*string);
+ *string = NULL;
goto cleanup_choice;
}
+
*string_size = tmp_size + 1;
(*string)[tmp_size] = '\0';
-cleanup_choice:
+ cleanup_choice:
grub_free (choice);
-cleanup:
+ cleanup:
asn1_delete_structure (&strasn);
+
return err;
}
@@ -365,218 +317,197 @@ cleanup:
* TBSCertificate ::= SEQUENCE {
* version [0] EXPLICIT Version DEFAULT v1,
* ...
- *
+ *
* Version ::= INTEGER { v1(0), v2(1), v3(2) }
*/
static grub_err_t
-check_version (asn1_node certificate)
+check_version (asn1_node certificate, grub_x509_cert_t *results)
{
- int rc;
+ grub_int32_t rc;
const char *name = "tbsCertificate.version";
grub_uint8_t version;
- int len = 1;
+ grub_int32_t len = sizeof (version);
rc = asn1_read_value (certificate, name, &version, &len);
- /* require version 3 */
+ /* Require version 3. */
if (rc != ASN1_SUCCESS || len != 1)
- return grub_error (GRUB_ERR_BAD_FILE_TYPE,
- "Error reading certificate version");
+ return grub_error (GRUB_ERR_BAD_FILE_TYPE, "error reading certificate version");
if (version != 0x02)
return grub_error (GRUB_ERR_BAD_FILE_TYPE,
- "Invalid x509 certificate version, expected v3 (0x02), got 0x%02x",
- version);
+ "invalid x509 certificate version, expected v3 (0x02), got 0x%02x.",
+ version);
+
+ results->version = version;
return GRUB_ERR_NONE;
}
-/*
- * This is an X.501 Name, which is complex.
- *
- * For simplicity, we extract only the CN.
- */
+/* we extract only the CN and issuer. */
static grub_err_t
-read_name (asn1_node asn, const char *name_path, char **name,
- grub_size_t * name_size)
+read_name (asn1_node asn, const char *name_path, char **name, grub_size_t *name_size)
{
- int seq_components, set_components;
- int result;
- int i, j;
+ grub_int32_t seq_components, set_components;
+ grub_int32_t result;
+ grub_int32_t i, j;
char *top_path, *set_path, *type_path, *val_path;
- char type[MAX_OID_LEN];
- int type_len = sizeof (type);
- int string_size = 0;
+ char type[GRUB_MAX_OID_LEN];
+ grub_int32_t type_len = sizeof (type);
+ grub_int32_t string_size = 0;
char *string_der;
grub_err_t err;
*name = NULL;
top_path = grub_xasprintf ("%s.rdnSequence", name_path);
- if (!top_path)
+ if (top_path == NULL)
return grub_error (GRUB_ERR_OUT_OF_MEMORY,
- "Could not allocate memory for %s name parsing path",
- name_path);
+ "could not allocate memory for %s name parsing path", name_path);
result = asn1_number_of_elements (asn, top_path, &seq_components);
if (result != ASN1_SUCCESS)
{
- err =
- grub_error (GRUB_ERR_BAD_FILE_TYPE,
- "Error counting name components: %s",
- asn1_strerror (result));
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "error counting name components: %s",
+ asn1_strerror (result));
goto cleanup;
}
for (i = 1; i <= seq_components; i++)
{
set_path = grub_xasprintf ("%s.?%d", top_path, i);
- if (!set_path)
- {
- err =
- grub_error (GRUB_ERR_OUT_OF_MEMORY,
- "Could not allocate memory for %s name set parsing path",
- name_path);
- goto cleanup_set;
- }
- /* this brings us, hopefully, to a set */
+ if (set_path == NULL)
+ {
+ err = grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "could not allocate memory for %s name set parsing path",
+ name_path);
+ goto cleanup_set;
+ }
+ /* This brings us, hopefully, to a set. */
result = asn1_number_of_elements (asn, set_path, &set_components);
if (result != ASN1_SUCCESS)
- {
- err =
- grub_error (GRUB_ERR_BAD_FILE_TYPE,
- "Error counting name sub-components components (element %d): %s",
- i, asn1_strerror (result));
- goto cleanup_set;
- }
+ {
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "error counting name sub-components components (element %d): %s",
+ i, asn1_strerror (result));
+ goto cleanup_set;
+ }
for (j = 1; j <= set_components; j++)
- {
- type_path = grub_xasprintf ("%s.?%d.?%d.type", top_path, i, j);
- if (!type_path)
- {
- err =
- grub_error (GRUB_ERR_OUT_OF_MEMORY,
- "Could not allocate memory for %s name component type path",
- name_path);
- goto cleanup_set;
- }
- type_len = sizeof (type);
- result = asn1_read_value (asn, type_path, type, &type_len);
- if (result != ASN1_SUCCESS)
- {
- err =
- grub_error (GRUB_ERR_BAD_FILE_TYPE,
- "Error reading %s name component type: %s",
- name_path, asn1_strerror (result));
- goto cleanup_type;
- }
+ {
+ type_path = grub_xasprintf ("%s.?%d.?%d.type", top_path, i, j);
+ if (type_path == NULL)
+ {
+ err = grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "could not allocate memory for %s name component type path",
+ name_path);
+ goto cleanup_set;
+ }
+ type_len = sizeof (type);
+ result = asn1_read_value (asn, type_path, type, &type_len);
+ if (result != ASN1_SUCCESS)
+ {
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "error reading %s name component type: %s",
+ name_path, asn1_strerror (result));
+ goto cleanup_type;
+ }
- if (grub_strncmp (type, commonName_oid, type_len) != 0)
- {
- grub_free (type_path);
- continue;
- }
+ if (grub_strncmp (type, commonName_oid, type_len) != 0)
+ {
+ grub_free (type_path);
+ continue;
+ }
- val_path = grub_xasprintf ("%s.?%d.?%d.value", top_path, i, j);
- if (!val_path)
- {
- err =
- grub_error (GRUB_ERR_OUT_OF_MEMORY,
- "Could not allocate memory for %s name component value path",
- name_path);
- goto cleanup_set;
- }
+ val_path = grub_xasprintf ("%s.?%d.?%d.value", top_path, i, j);
+ if (val_path == NULL)
+ {
+ err = grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "could not allocate memory for %s name component value path",
+ name_path);
+ goto cleanup_type;
+ }
- string_der =
- grub_asn1_allocate_and_read (asn, val_path, name_path,
- &string_size);
- if (!string_der)
- {
- err = grub_errno;
- goto cleanup_val_path;
- }
+ string_der = grub_asn1_allocate_and_read (asn, val_path, name_path, &string_size);
+ if (string_der == NULL)
+ {
+ err = grub_errno;
+ goto cleanup_val_path;
+ }
- err = decode_string (string_der, string_size, name, name_size);
- if (err)
- goto cleanup_string;
+ err = decode_string (string_der, string_size, name, name_size);
+ if (err)
+ goto cleanup_string;
+
+ grub_free (string_der);
+ grub_free (type_path);
+ grub_free (val_path);
+ break;
+ }
- grub_free (string_der);
- grub_free (type_path);
- grub_free (val_path);
- break;
- }
grub_free (set_path);
-
if (*name)
- break;
+ break;
}
+ grub_free (top_path);
+
return GRUB_ERR_NONE;
-cleanup_string:
+ cleanup_string:
grub_free (string_der);
-cleanup_val_path:
+ cleanup_val_path:
grub_free (val_path);
-cleanup_type:
+ cleanup_type:
grub_free (type_path);
-cleanup_set:
+ cleanup_set:
grub_free (set_path);
-cleanup:
+ cleanup:
grub_free (top_path);
+
return err;
}
-/*
- * details here
- */
+/* Verify the Key Usage extension. We require the Digital Signature usage. */
static grub_err_t
-verify_key_usage (grub_uint8_t * value, int value_size)
+verify_key_usage (grub_uint8_t *value, grub_int32_t value_size)
{
asn1_node usageasn;
- int result;
+ grub_int32_t result;
grub_err_t err = GRUB_ERR_NONE;
grub_uint8_t usage = 0xff;
- int usage_size = 1;
+ grub_int32_t usage_size = sizeof (usage_size);
- result =
- asn1_create_element (_gnutls_pkix_asn, "PKIX1.KeyUsage", &usageasn);
+ result = asn1_create_element (grub_gnutls_pkix_asn, "PKIX1.KeyUsage", &usageasn);
if (result != ASN1_SUCCESS)
- {
- return grub_error (GRUB_ERR_OUT_OF_MEMORY,
- "Could not create ASN.1 structure for key usage");
- }
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "could not create ASN.1 structure for key usage");
result = asn1_der_decoding2 (&usageasn, value, &value_size,
- ASN1_DECODE_FLAG_STRICT_DER, asn1_error);
+ ASN1_DECODE_FLAG_STRICT_DER, asn1_error);
if (result != ASN1_SUCCESS)
{
- err =
- grub_error (GRUB_ERR_BAD_FILE_TYPE,
- "Error parsing DER for Key Usage: %s", asn1_error);
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "error parsing DER for Key Usage: %s", asn1_error);
goto cleanup;
}
result = asn1_read_value (usageasn, "", &usage, &usage_size);
if (result != ASN1_SUCCESS)
{
- err =
- grub_error (GRUB_ERR_BAD_FILE_TYPE,
- "Error reading Key Usage value: %s",
- asn1_strerror (result));
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "error reading Key Usage value: %s",
+ asn1_strerror (result));
goto cleanup;
}
- /* Only the first bit is permitted to be set */
- if (usage != 0x80)
+ if (!(usage & digitalSignatureUsage))
{
- err =
- grub_error (GRUB_ERR_BAD_FILE_TYPE, "Unexpected Key Usage value: %x",
- usage);
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "key usage (0x%x) missing Digital Signature usage", usage);
goto cleanup;
}
-cleanup:
+ cleanup:
asn1_delete_structure (&usageasn);
+
return err;
}
@@ -586,132 +517,115 @@ cleanup:
* pathLenConstraint INTEGER (0..MAX) OPTIONAL }
*/
static grub_err_t
-verify_basic_constraints (grub_uint8_t * value, int value_size)
+verify_basic_constraints (grub_uint8_t *value, grub_int32_t value_size)
{
asn1_node basicasn;
- int result;
+ grub_int32_t result;
grub_err_t err = GRUB_ERR_NONE;
- char cA[6]; /* FALSE or TRUE */
- int cA_size = sizeof (cA);
+ char cA[6]; /* FALSE or TRUE. */
+ grub_int32_t cA_size = sizeof (cA);
- result =
- asn1_create_element (_gnutls_pkix_asn, "PKIX1.BasicConstraints",
- &basicasn);
+ result = asn1_create_element (grub_gnutls_pkix_asn, "PKIX1.BasicConstraints", &basicasn);
if (result != ASN1_SUCCESS)
- {
- return grub_error (GRUB_ERR_OUT_OF_MEMORY,
- "Could not create ASN.1 structure for Basic Constraints");
- }
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "could not create ASN.1 structure for Basic Constraints");
result = asn1_der_decoding2 (&basicasn, value, &value_size,
- ASN1_DECODE_FLAG_STRICT_DER, asn1_error);
+ ASN1_DECODE_FLAG_STRICT_DER, asn1_error);
if (result != ASN1_SUCCESS)
{
- err =
- grub_error (GRUB_ERR_BAD_FILE_TYPE,
- "Error parsing DER for Basic Constraints: %s",
- asn1_error);
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "error parsing DER for Basic Constraints: %s", asn1_error);
goto cleanup;
}
result = asn1_read_value (basicasn, "cA", cA, &cA_size);
if (result == ASN1_ELEMENT_NOT_FOUND)
{
- /* Not present, default is False, so this is OK */
+ /* Not present, default is False, so this is OK. */
err = GRUB_ERR_NONE;
goto cleanup;
}
else if (result != ASN1_SUCCESS)
{
- err =
- grub_error (GRUB_ERR_BAD_FILE_TYPE,
- "Error reading Basic Constraints cA value: %s",
- asn1_strerror (result));
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "error reading Basic Constraints cA value: %s",
+ asn1_strerror (result));
goto cleanup;
}
- /* The certificate must not be a CA certificate */
+ /* The certificate must not be a CA certificate. */
if (grub_strncmp ("FALSE", cA, cA_size) != 0)
{
- err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "Unexpected CA value: %s",
- cA);
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "unexpected CA value: %s", cA);
goto cleanup;
}
-cleanup:
+ cleanup:
asn1_delete_structure (&basicasn);
+
return err;
}
/*
+ * Verify the Extended Key Usage extension. We require the Code Signing usage.
+ *
* 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)
+verify_extended_key_usage (grub_uint8_t *value, grub_int32_t value_size)
{
asn1_node extendedasn;
- int result, count;
+ grub_int32_t result, count, i = 0;
grub_err_t err = GRUB_ERR_NONE;
- char usage[MAX_OID_LEN];
- int usage_size = sizeof (usage);
+ char usage[GRUB_MAX_OID_LEN], name[3];
+ grub_int32_t usage_size = sizeof (usage);
- result =
- asn1_create_element (_gnutls_pkix_asn, "PKIX1.ExtKeyUsageSyntax",
- &extendedasn);
+ result = asn1_create_element (grub_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");
- }
+ 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);
+ 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);
+ 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 EKUs are present, it checks the presents of Code Signing 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));
+ 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)
+ for (i = 1; i < count + 1; i++)
{
- err =
- grub_error (GRUB_ERR_BAD_FILE_TYPE,
- "Error reading Extended Key Usage: %s",
- asn1_strerror (result));
- goto cleanup;
- }
+ grub_memset (name, 0, sizeof (name));
+ grub_snprintf (name, sizeof (name), "?%d", i);
+ result = asn1_read_value (extendedasn, name, 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;
+ if (grub_strncmp (codeSigningUsage_oid, usage, usage_size) == 0)
+ goto cleanup;
}
-cleanup:
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "extended key usage missing Code Signing usage");
+
+ cleanup:
asn1_delete_structure (&extendedasn);
+
return err;
}
@@ -727,131 +641,126 @@ cleanup:
* -- by extnID
* }
*
- * We require that a certificate:
- * - contain the Digital Signature usage only
+ * A certificate must:
+ * - contain the Digital Signature usage
* - not be a CA
- * - MUST not contain any other critical extensions (RFC 5280 s 4.2)
+ * - contain no extended usages, or contain the Code Signing extended usage
+ * - not contain any other critical extensions (RFC 5280 s 4.2)
*/
static grub_err_t
verify_extensions (asn1_node cert)
{
- int result;
- int ext, num_extensions = 0;
- int usage_present = 0, constraints_present = 0, extended_usage_present = 0;
+ grub_int32_t result;
+ grub_int32_t ext, num_extensions = 0;
+ grub_int32_t 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;
+ char extnID[GRUB_MAX_OID_LEN];
+ grub_int32_t extnID_size;
grub_err_t err;
- char critical[6]; /* we get either "TRUE" or "FALSE" */
- int critical_size;
+ char critical[6]; /* We get either "TRUE" or "FALSE". */
+ grub_int32_t critical_size;
grub_uint8_t *value;
- int value_size;
+ grub_int32_t value_size;
- result =
- asn1_number_of_elements (cert, "tbsCertificate.extensions",
- &num_extensions);
+ result = asn1_number_of_elements (cert, "tbsCertificate.extensions", &num_extensions);
if (result != ASN1_SUCCESS)
- {
- return grub_error (GRUB_ERR_BAD_FILE_TYPE,
- "Error counting number of extensions: %s",
- asn1_strerror (result));
- }
+ return grub_error (GRUB_ERR_BAD_FILE_TYPE, "error counting number of extensions: %s",
+ asn1_strerror (result));
if (num_extensions < 2)
- {
- return grub_error (GRUB_ERR_BAD_FILE_TYPE,
- "Insufficient number of extensions for certificate, need at least 2, got %d",
- num_extensions);
- }
+ return grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "insufficient number of extensions for certificate, need at least 2, got %d",
+ num_extensions);
for (ext = 1; ext <= num_extensions; ext++)
{
oid_path = grub_xasprintf ("tbsCertificate.extensions.?%d.extnID", ext);
+ if (oid_path == NULL)
+ {
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "error extension OID path is empty");
+ return err;
+ }
extnID_size = sizeof (extnID);
result = asn1_read_value (cert, oid_path, extnID, &extnID_size);
- if (result != GRUB_ERR_NONE)
- {
- err =
- grub_error (GRUB_ERR_BAD_FILE_TYPE,
- "Error reading extension OID: %s",
- asn1_strerror (result));
- goto cleanup_oid_path;
- }
+ if (result != ASN1_SUCCESS)
+ {
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "error reading extension OID: %s",
+ asn1_strerror (result));
+ goto cleanup_oid_path;
+ }
+
+ critical_path = grub_xasprintf ("tbsCertificate.extensions.?%d.critical", ext);
+ if (critical_path == NULL)
+ {
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "error critical path is empty");
+ goto cleanup_oid_path;
+ }
- critical_path =
- grub_xasprintf ("tbsCertificate.extensions.?%d.critical", ext);
critical_size = sizeof (critical);
- result =
- asn1_read_value (cert, critical_path, critical, &critical_size);
+ result = asn1_read_value (cert, critical_path, critical, &critical_size);
if (result == ASN1_ELEMENT_NOT_FOUND)
- {
- critical[0] = '\0';
- }
+ critical[0] = '\0';
else if (result != ASN1_SUCCESS)
- {
- err =
- grub_error (GRUB_ERR_BAD_FILE_TYPE,
- "Error reading extension criticality: %s",
- asn1_strerror (result));
- goto cleanup_critical_path;
- }
+ {
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "error reading extension criticality: %s",
+ asn1_strerror (result));
+ goto cleanup_critical_path;
+ }
- value_path =
- grub_xasprintf ("tbsCertificate.extensions.?%d.extnValue", ext);
- value =
- grub_asn1_allocate_and_read (cert, value_path,
- "certificate extension value",
- &value_size);
- if (!value)
- {
- err = grub_errno;
- goto cleanup_value_path;
- }
+ value_path = grub_xasprintf ("tbsCertificate.extensions.?%d.extnValue", ext);
+ if (value_path == NULL)
+ {
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "error extnValue path is empty");
+ goto cleanup_critical_path;
+ }
+
+ value = grub_asn1_allocate_and_read (cert, value_path,
+ "certificate extension value", &value_size);
+ if (value == NULL)
+ {
+ err = grub_errno;
+ goto cleanup_value_path;
+ }
/*
- * Now we must see if we recognise the OID.
- * If we have an unrecognised critical extension we MUST bail.
+ * Now we must see if we recognise the OID. If we have an unrecognised
+ * critical extension we MUST bail.
*/
if (grub_strncmp (keyUsage_oid, extnID, extnID_size) == 0)
- {
- err = verify_key_usage (value, value_size);
- if (err != GRUB_ERR_NONE)
- {
- goto cleanup_value;
- }
- usage_present++;
- }
+ {
+ err = verify_key_usage (value, value_size);
+ if (err != GRUB_ERR_NONE)
+ goto cleanup_value;
+
+ usage_present++;
+ }
else if (grub_strncmp (basicConstraints_oid, extnID, extnID_size) == 0)
- {
- err = verify_basic_constraints (value, value_size);
- if (err != GRUB_ERR_NONE)
- {
- goto cleanup_value;
- }
- constraints_present++;
- }
+ {
+ err = verify_basic_constraints (value, value_size);
+ if (err != GRUB_ERR_NONE)
+ goto cleanup_value;
+
+ 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++;
- }
+ {
+ 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)
- {
- /*
- * per the RFC, we must not process a certificate with
- * a critical extension we do not understand.
- */
- err =
- grub_error (GRUB_ERR_BAD_FILE_TYPE,
- "Unhandled critical x509 extension with OID %s",
- extnID);
- goto cleanup_value;
- }
+ {
+ /*
+ * Per the RFC, we must not process a certificate with a critical
+ * extension we do not understand.
+ */
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "unhandled critical x509 extension with OID %s", extnID);
+ goto cleanup_value;
+ }
grub_free (value);
grub_free (value_path);
@@ -860,117 +769,114 @@ verify_extensions (asn1_node cert)
}
if (usage_present != 1)
- {
- return grub_error (GRUB_ERR_BAD_FILE_TYPE,
- "Unexpected number of Key Usage extensions - expected 1, got %d",
- usage_present);
- }
+ return grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "unexpected number of Key Usage extensions - expected 1, got %d",
+ usage_present);
+
if (constraints_present != 1)
- {
- return grub_error (GRUB_ERR_BAD_FILE_TYPE,
- "Unexpected number of basic constraints extensions - expected 1, got %d",
- constraints_present);
- }
+ return grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "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_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:
+ cleanup_value:
grub_free (value);
-cleanup_value_path:
+ cleanup_value_path:
grub_free (value_path);
-cleanup_critical_path:
+ cleanup_critical_path:
grub_free (critical_path);
-cleanup_oid_path:
+ cleanup_oid_path:
grub_free (oid_path);
+
return err;
}
+static void
+add_cert_fingerprint (const void *data, const grub_size_t data_size,
+ grub_x509_cert_t *const cert)
+{
+ /* Add SHA256 hash of certificate. */
+ grub_crypto_hash ((gcry_md_spec_t *) &_gcry_digest_spec_sha256,
+ &cert->fingerprint[GRUB_FINGERPRINT_SHA256], data, data_size);
+ /* Add SHA384 hash of certificate. */
+ grub_crypto_hash ((gcry_md_spec_t *) &_gcry_digest_spec_sha384,
+ &cert->fingerprint[GRUB_FINGERPRINT_SHA384], data, data_size);
+ /* Add SHA512 hash of certificate. */
+ grub_crypto_hash ((gcry_md_spec_t *) &_gcry_digest_spec_sha512,
+ &cert->fingerprint[GRUB_FINGERPRINT_SHA512], data, data_size);
+}
+
/*
* Parse a certificate whose DER-encoded form is in @data, of size @data_size.
- * Return the results in @results, which must point to an allocated x509 certificate.
+ * Return the results in @results, which must point to an allocated x509
+ * certificate.
*/
grub_err_t
-certificate_import (void *data, grub_size_t data_size,
- struct x509_certificate *results)
+grub_x509_cert_parse (const void *data, grub_size_t data_size, grub_x509_cert_t *results)
{
- int result = 0;
+ grub_int32_t result = 0;
asn1_node cert;
grub_err_t err;
- int size;
- int tmp_size;
+ grub_int32_t tmp_size;
+ grub_int32_t size = (grub_int32_t) data_size;
- if (data_size > GRUB_INT_MAX)
+ if (data_size > GRUB_UINT_MAX)
return grub_error (GRUB_ERR_OUT_OF_RANGE,
- "Cannot parse a certificate where data size > INT_MAX");
- size = (int) data_size;
+ "cannot parse a certificate where data size > GRUB_UINT_MAX");
- result = asn1_create_element (_gnutls_pkix_asn, "PKIX1.Certificate", &cert);
+ result = asn1_create_element (grub_gnutls_pkix_asn, "PKIX1.Certificate", &cert);
if (result != ASN1_SUCCESS)
- {
- return grub_error (GRUB_ERR_OUT_OF_MEMORY,
- "Could not create ASN.1 structure for certificate: %s",
- asn1_strerror (result));
- }
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+ "could not create ASN.1 structure for certificate: %s",
+ asn1_strerror (result));
- result = asn1_der_decoding2 (&cert, data, &size,
- ASN1_DECODE_FLAG_STRICT_DER, asn1_error);
+ result = asn1_der_decoding2 (&cert, data, &size, ASN1_DECODE_FLAG_STRICT_DER, asn1_error);
if (result != ASN1_SUCCESS)
{
- err =
- grub_error (GRUB_ERR_BAD_FILE_TYPE,
- "Could not parse DER for certificate: %s", asn1_error);
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE,
+ "could not parse DER for certificate: %s", asn1_error);
goto cleanup;
}
- /*
+ /*
* TBSCertificate ::= SEQUENCE {
* version [0] EXPLICIT Version DEFAULT v1
*/
- err = check_version (cert);
+ err = check_version (cert, results);
if (err != GRUB_ERR_NONE)
- {
- goto cleanup;
- }
+ goto cleanup;
/*
* serialNumber CertificateSerialNumber,
*
* CertificateSerialNumber ::= INTEGER
*/
- results->serial =
- grub_asn1_allocate_and_read (cert, "tbsCertificate.serialNumber",
- "certificate serial number", &tmp_size);
- if (!results->serial)
+ results->serial = grub_asn1_allocate_and_read (cert, "tbsCertificate.serialNumber",
+ "certificate serial number", &tmp_size);
+ if (results->serial == NULL)
{
err = grub_errno;
goto cleanup;
}
/*
* It's safe to cast the signed int to an unsigned here, we know
- * length is non-negative
+ * length is non-negative.
*/
results->serial_len = tmp_size;
- /*
+ /*
* signature AlgorithmIdentifier,
*
* We don't load the signature or issuer at the moment,
* as we don't attempt x509 verification.
*/
- /*
- * issuer Name,
- *
- * The RFC only requires the serial number to be unique within
- * issuers, so to avoid ambiguity we _technically_ ought to make
- * this available.
- */
-
/*
* validity Validity,
*
@@ -982,16 +888,23 @@ certificate_import (void *data, grub_size_t data_size,
* platforms. For now we do not parse them.
*/
+ /*
+ * issuer Name,
+ *
+ * This is an X501 name, we parse out just the issuer.
+ */
+ err = read_name (cert, "tbsCertificate.issuer", &results->issuer, &results->issuer_len);
+ if (err != GRUB_ERR_NONE)
+ goto cleanup_serial;
+
/*
* subject Name,
- *
+ *
* This is an X501 name, we parse out just the CN.
*/
- err =
- read_name (cert, "tbsCertificate.subject", &results->subject,
- &results->subject_len);
+ err = read_name (cert, "tbsCertificate.subject", &results->subject, &results->subject_len);
if (err != GRUB_ERR_NONE)
- goto cleanup_serial;
+ goto cleanup_issuer;
/*
* TBSCertificate ::= SEQUENCE {
@@ -1010,41 +923,48 @@ certificate_import (void *data, grub_size_t data_size,
* -- If present, version MUST be v3
* }
*/
-
err = verify_extensions (cert);
if (err != GRUB_ERR_NONE)
- goto cleanup_name;
-
+ goto cleanup_mpis;
/*
* We do not read or check the signature on the certificate:
* as discussed we do not try to validate the certificate but trust
* it implictly.
*/
-
asn1_delete_structure (&cert);
+
+ /* Add the fingerprint of the certificate. */
+ add_cert_fingerprint (data, data_size, results);
+
return GRUB_ERR_NONE;
-
-cleanup_name:
+ cleanup_mpis:
+ _gcry_mpi_release (results->mpis[GRUB_RSA_PK_MODULUS]);
+ _gcry_mpi_release (results->mpis[GRUB_RSA_PK_EXPONENT]);
+ cleanup_name:
grub_free (results->subject);
-cleanup_serial:
+ cleanup_issuer:
+ grub_free (results->issuer);
+ cleanup_serial:
grub_free (results->serial);
-cleanup:
+ cleanup:
asn1_delete_structure (&cert);
+
return err;
}
/*
- * Release all the storage associated with the x509 certificate.
- * If the caller dynamically allocated the certificate, it must free it.
- * The caller is also responsible for maintenance of the linked list.
+ * Release all the storage associated with the x509 certificate. If the caller
+ * dynamically allocated the certificate, it must free it. The caller is also
+ * responsible for maintenance of the linked list.
*/
void
-certificate_release (struct x509_certificate *cert)
+grub_x509_cert_release (grub_x509_cert_t *cert)
{
+ grub_free (cert->issuer);
grub_free (cert->subject);
grub_free (cert->serial);
- gcry_mpi_release (cert->mpis[0]);
- gcry_mpi_release (cert->mpis[1]);
+ _gcry_mpi_release (cert->mpis[GRUB_RSA_PK_MODULUS]);
+ _gcry_mpi_release (cert->mpis[GRUB_RSA_PK_EXPONENT]);
}
diff --git a/include/grub/crypto.h b/include/grub/crypto.h
index b0d7add..00d074d 100644
--- a/include/grub/crypto.h
+++ b/include/grub/crypto.h
@@ -510,6 +510,7 @@ grub_crypto_hmac_buffer (const struct gcry_md_spec *md,
extern gcry_md_spec_t _gcry_digest_spec_md5;
extern gcry_md_spec_t _gcry_digest_spec_sha1;
extern gcry_md_spec_t _gcry_digest_spec_sha256;
+extern gcry_md_spec_t _gcry_digest_spec_sha384;
extern gcry_md_spec_t _gcry_digest_spec_sha512;
extern gcry_md_spec_t _gcry_digest_spec_crc32;
extern gcry_cipher_spec_t _gcry_cipher_spec_aes;