2721 lines
91 KiB
Diff
2721 lines
91 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Nicolas Frayer <nfrayer@redhat.com>
|
|
Date: Wed, 21 May 2025 15:43:44 -0400
|
|
Subject: [PATCH] appended/sig: sync'd with upstream code
|
|
|
|
Signed-off-by: Nicolas Frayer <nfrayer@redhat.com>
|
|
---
|
|
grub-core/commands/appendedsig/appendedsig.c | 479 +++++++++---------
|
|
grub-core/commands/appendedsig/appendedsig.h | 50 +-
|
|
grub-core/commands/appendedsig/asn1util.c | 34 +-
|
|
grub-core/commands/appendedsig/pkcs7.c | 389 +++++++++-----
|
|
grub-core/commands/appendedsig/x509.c | 727 ++++++++++++---------------
|
|
5 files changed, 853 insertions(+), 826 deletions(-)
|
|
|
|
diff --git a/grub-core/commands/appendedsig/appendedsig.c b/grub-core/commands/appendedsig/appendedsig.c
|
|
index bf8b18b..f4eefe5 100644
|
|
--- a/grub-core/commands/appendedsig/appendedsig.c
|
|
+++ b/grub-core/commands/appendedsig/appendedsig.c
|
|
@@ -1,6 +1,7 @@
|
|
/*
|
|
* GRUB -- GRand Unified Bootloader
|
|
- * Copyright (C) 2020-2021 IBM Corporation.
|
|
+ * Copyright (C) 2020, 2021, 2022 Free Software Foundation, Inc.
|
|
+ * Copyright (C) 2020, 2021, 2022 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
|
|
@@ -46,24 +47,21 @@ const char magic[] = "~Module signature appended~\n";
|
|
*/
|
|
struct module_signature
|
|
{
|
|
- grub_uint8_t algo; /* Public-key crypto algorithm [0] */
|
|
- grub_uint8_t hash; /* Digest algorithm [0] */
|
|
- grub_uint8_t id_type; /* Key identifier type [PKEY_ID_PKCS7] */
|
|
- grub_uint8_t signer_len; /* Length of signer's name [0] */
|
|
- grub_uint8_t key_id_len; /* Length of key identifier [0] */
|
|
+ grub_uint8_t algo; /* Public-key crypto algorithm [0] */
|
|
+ grub_uint8_t hash; /* Digest algorithm [0] */
|
|
+ grub_uint8_t id_type; /* Key identifier type [PKEY_ID_PKCS7] */
|
|
+ grub_uint8_t signer_len; /* Length of signer's name [0] */
|
|
+ grub_uint8_t key_id_len; /* Length of key identifier [0] */
|
|
grub_uint8_t __pad[3];
|
|
- grub_uint32_t sig_len; /* Length of signature data */
|
|
+ grub_uint32_t sig_len; /* Length of signature data */
|
|
} GRUB_PACKED;
|
|
|
|
-
|
|
/* This represents an entire, parsed, appended signature */
|
|
struct grub_appended_signature
|
|
{
|
|
- grub_size_t signature_len; /* Length of PKCS#7 data +
|
|
- * metadata + magic */
|
|
-
|
|
- struct module_signature sig_metadata; /* Module signature metadata */
|
|
- struct pkcs7_signedData pkcs7; /* Parsed PKCS#7 data */
|
|
+ grub_size_t signature_len; /* Length of PKCS#7 data + metadata + magic */
|
|
+ struct module_signature sig_metadata; /* Module signature metadata */
|
|
+ struct pkcs7_signedData pkcs7; /* Parsed PKCS#7 data */
|
|
};
|
|
|
|
/* Trusted certificates for verifying appended signatures */
|
|
@@ -90,143 +88,154 @@ struct x509_certificate *grub_trusted_key;
|
|
*/
|
|
extern gcry_pk_spec_t _gcry_pubkey_spec_rsa;
|
|
|
|
-static int check_sigs = 0;
|
|
+static enum
|
|
+{
|
|
+ check_sigs_no = 0,
|
|
+ check_sigs_enforce = 1,
|
|
+ check_sigs_forced = 2
|
|
+} check_sigs = check_sigs_no;
|
|
|
|
static const char *
|
|
grub_env_read_sec (struct grub_env_var *var __attribute__ ((unused)),
|
|
const char *val __attribute__ ((unused)))
|
|
{
|
|
- if (check_sigs == 2)
|
|
+ if (check_sigs == check_sigs_forced)
|
|
return "forced";
|
|
- else if (check_sigs == 1)
|
|
+ else if (check_sigs == check_sigs_enforce)
|
|
return "enforce";
|
|
else
|
|
return "no";
|
|
}
|
|
|
|
static char *
|
|
-grub_env_write_sec (struct grub_env_var *var __attribute__((unused)),
|
|
- const char *val)
|
|
+grub_env_write_sec (struct grub_env_var *var __attribute__ ((unused)), const char *val)
|
|
{
|
|
/* Do not allow the value to be changed if set to forced */
|
|
- if (check_sigs == 2)
|
|
+ if (check_sigs == check_sigs_forced)
|
|
return grub_strdup ("forced");
|
|
|
|
if ((*val == '2') || (*val == 'f'))
|
|
- check_sigs = 2;
|
|
+ check_sigs = check_sigs_forced;
|
|
else if ((*val == '1') || (*val == 'e'))
|
|
- check_sigs = 1;
|
|
+ check_sigs = check_sigs_enforce;
|
|
else if ((*val == '0') || (*val == 'n'))
|
|
- check_sigs = 0;
|
|
+ check_sigs = check_sigs_no;
|
|
|
|
return grub_strdup (grub_env_read_sec (NULL, NULL));
|
|
}
|
|
|
|
+static grub_err_t
|
|
+file_read_all (grub_file_t file, grub_uint8_t **buf, grub_size_t *len)
|
|
+{
|
|
+ grub_off_t full_file_size;
|
|
+ grub_size_t file_size, total_read_size = 0;
|
|
+ grub_ssize_t read_size;
|
|
+
|
|
+ full_file_size = grub_file_size (file);
|
|
+ if (full_file_size == GRUB_FILE_SIZE_UNKNOWN)
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
|
+ N_("Cannot read a file of unknown size into a buffer"));
|
|
+
|
|
+ if (full_file_size > GRUB_SIZE_MAX)
|
|
+ return grub_error (GRUB_ERR_OUT_OF_RANGE,
|
|
+ N_("File is too large to read: %" PRIuGRUB_UINT64_T " bytes"),
|
|
+ full_file_size);
|
|
+
|
|
+ file_size = (grub_size_t) full_file_size;
|
|
+
|
|
+ *buf = grub_malloc (file_size);
|
|
+ if (!*buf)
|
|
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
|
|
+ N_("Could not allocate file data buffer size %" PRIuGRUB_SIZE),
|
|
+ file_size);
|
|
+
|
|
+ while (total_read_size < file_size)
|
|
+ {
|
|
+ read_size = grub_file_read (file, *buf + total_read_size, file_size - total_read_size);
|
|
+
|
|
+ if (read_size < 0)
|
|
+ {
|
|
+ grub_free (*buf);
|
|
+ return grub_errno;
|
|
+ }
|
|
+ else if (read_size == 0)
|
|
+ {
|
|
+ grub_free (*buf);
|
|
+ return grub_error (GRUB_ERR_IO,
|
|
+ N_("Could not read full file size "
|
|
+ "(%" PRIuGRUB_SIZE "), only %" PRIuGRUB_SIZE " bytes read"),
|
|
+ file_size, total_read_size);
|
|
+ }
|
|
+
|
|
+ total_read_size += read_size;
|
|
+ }
|
|
+ *len = file_size;
|
|
+ return GRUB_ERR_NONE;
|
|
+}
|
|
+
|
|
static grub_err_t
|
|
read_cert_from_file (grub_file_t f, struct x509_certificate *certificate)
|
|
{
|
|
grub_err_t err;
|
|
- grub_uint8_t *buf = NULL;
|
|
- grub_ssize_t read_size;
|
|
- grub_off_t total_read_size = 0;
|
|
- grub_off_t file_size = grub_file_size (f);
|
|
+ grub_uint8_t *buf;
|
|
+ grub_size_t file_size;
|
|
|
|
-
|
|
- if (file_size == GRUB_FILE_SIZE_UNKNOWN)
|
|
- return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
|
- N_("Cannot parse a certificate file of unknown size"));
|
|
-
|
|
- buf = grub_zalloc (file_size);
|
|
- if (!buf)
|
|
- return grub_error (GRUB_ERR_OUT_OF_MEMORY,
|
|
- N_("Could not allocate buffer for certificate file contents"));
|
|
-
|
|
- while (total_read_size < file_size)
|
|
- {
|
|
- read_size =
|
|
- grub_file_read (f, &buf[total_read_size],
|
|
- file_size - total_read_size);
|
|
- if (read_size < 0)
|
|
- {
|
|
- err = grub_error (GRUB_ERR_READ_ERROR,
|
|
- N_("Error reading certificate file"));
|
|
- goto cleanup_buf;
|
|
- }
|
|
- total_read_size += read_size;
|
|
- }
|
|
-
|
|
- err = certificate_import (buf, total_read_size, certificate);
|
|
+ err = file_read_all (f, &buf, &file_size);
|
|
if (err != GRUB_ERR_NONE)
|
|
- goto cleanup_buf;
|
|
+ return err;
|
|
|
|
- return GRUB_ERR_NONE;
|
|
-
|
|
-cleanup_buf:
|
|
+ err = parse_x509_certificate (buf, file_size, certificate);
|
|
grub_free (buf);
|
|
+
|
|
return err;
|
|
}
|
|
|
|
static grub_err_t
|
|
-extract_appended_signature (grub_uint8_t * buf, grub_size_t bufsize,
|
|
- struct grub_appended_signature *sig)
|
|
+extract_appended_signature (const grub_uint8_t *buf, grub_size_t bufsize,
|
|
+ struct grub_appended_signature *sig)
|
|
{
|
|
- grub_err_t err;
|
|
grub_size_t pkcs7_size;
|
|
grub_size_t remaining_len;
|
|
- grub_uint8_t *appsigdata = buf + bufsize - grub_strlen (magic);
|
|
+ const grub_uint8_t *appsigdata = buf + bufsize - grub_strlen (magic);
|
|
|
|
if (bufsize < grub_strlen (magic))
|
|
- return grub_error (GRUB_ERR_BAD_SIGNATURE,
|
|
- N_("File too short for signature magic"));
|
|
+ return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("File too short for signature magic"));
|
|
|
|
if (grub_memcmp (appsigdata, (grub_uint8_t *) magic, grub_strlen (magic)))
|
|
- return grub_error (GRUB_ERR_BAD_SIGNATURE,
|
|
- N_("Missing or invalid signature magic"));
|
|
+ return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("Missing or invalid signature magic"));
|
|
|
|
remaining_len = bufsize - grub_strlen (magic);
|
|
|
|
if (remaining_len < sizeof (struct module_signature))
|
|
- return grub_error (GRUB_ERR_BAD_SIGNATURE,
|
|
- N_("File too short for signature metadata"));
|
|
+ return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("File too short for signature metadata"));
|
|
|
|
appsigdata -= sizeof (struct module_signature);
|
|
|
|
/* extract the metadata */
|
|
- grub_memcpy (&(sig->sig_metadata), appsigdata,
|
|
- sizeof (struct module_signature));
|
|
+ grub_memcpy (&(sig->sig_metadata), appsigdata, sizeof (struct module_signature));
|
|
|
|
remaining_len -= sizeof (struct module_signature);
|
|
|
|
if (sig->sig_metadata.id_type != 2)
|
|
return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("Wrong signature type"));
|
|
|
|
-#ifdef GRUB_TARGET_WORDS_BIGENDIAN
|
|
- pkcs7_size = sig->sig_metadata.sig_len;
|
|
-#else
|
|
- pkcs7_size = __builtin_bswap32 (sig->sig_metadata.sig_len);
|
|
-#endif
|
|
+ pkcs7_size = grub_be_to_cpu32 (sig->sig_metadata.sig_len);
|
|
|
|
if (pkcs7_size > remaining_len)
|
|
- return grub_error (GRUB_ERR_BAD_SIGNATURE,
|
|
- N_("File too short for PKCS#7 message"));
|
|
+ return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("File too short for PKCS#7 message"));
|
|
|
|
grub_dprintf ("appendedsig", "sig len %" PRIuGRUB_SIZE "\n", pkcs7_size);
|
|
|
|
- sig->signature_len =
|
|
- grub_strlen (magic) + sizeof (struct module_signature) + pkcs7_size;
|
|
+ sig->signature_len = grub_strlen (magic) + sizeof (struct module_signature) + pkcs7_size;
|
|
|
|
/* rewind pointer and parse pkcs7 data */
|
|
appsigdata -= pkcs7_size;
|
|
|
|
- err = parse_pkcs7_signedData (appsigdata, pkcs7_size, &sig->pkcs7);
|
|
- if (err != GRUB_ERR_NONE)
|
|
- return err;
|
|
-
|
|
- return GRUB_ERR_NONE;
|
|
+ return parse_pkcs7_signedData (appsigdata, pkcs7_size, &sig->pkcs7);
|
|
}
|
|
|
|
static grub_err_t
|
|
-grub_verify_appended_signature (grub_uint8_t * buf, grub_size_t bufsize)
|
|
+grub_verify_appended_signature (const grub_uint8_t *buf, grub_size_t bufsize)
|
|
{
|
|
grub_err_t err = GRUB_ERR_NONE;
|
|
grub_size_t datasize;
|
|
@@ -236,10 +245,11 @@ grub_verify_appended_signature (grub_uint8_t * buf, grub_size_t bufsize)
|
|
gcry_err_code_t rc;
|
|
struct x509_certificate *pk;
|
|
struct grub_appended_signature sig;
|
|
+ struct pkcs7_signerInfo *si;
|
|
+ int i;
|
|
|
|
if (!grub_trusted_key)
|
|
- return grub_error (GRUB_ERR_BAD_SIGNATURE,
|
|
- N_("No trusted keys to verify against"));
|
|
+ return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("No trusted keys to verify against"));
|
|
|
|
err = extract_appended_signature (buf, bufsize, &sig);
|
|
if (err != GRUB_ERR_NONE)
|
|
@@ -247,67 +257,78 @@ grub_verify_appended_signature (grub_uint8_t * buf, grub_size_t bufsize)
|
|
|
|
datasize = bufsize - sig.signature_len;
|
|
|
|
- context = grub_zalloc (sig.pkcs7.hash->contextsize);
|
|
- if (!context)
|
|
- return grub_errno;
|
|
-
|
|
- sig.pkcs7.hash->init (context);
|
|
- sig.pkcs7.hash->write (context, buf, datasize);
|
|
- sig.pkcs7.hash->final (context);
|
|
- hash = sig.pkcs7.hash->read (context);
|
|
- grub_dprintf ("appendedsig",
|
|
- "data size %" PRIxGRUB_SIZE ", hash %02x%02x%02x%02x...\n",
|
|
- datasize, hash[0], hash[1], hash[2], hash[3]);
|
|
-
|
|
- err = GRUB_ERR_BAD_SIGNATURE;
|
|
- for (pk = grub_trusted_key; pk; pk = pk->next)
|
|
+ for (i = 0; i < sig.pkcs7.signerInfo_count; i++)
|
|
{
|
|
- rc = grub_crypto_rsa_pad (&hashmpi, hash, sig.pkcs7.hash, pk->mpis[0]);
|
|
- if (rc)
|
|
- {
|
|
- err = grub_error (GRUB_ERR_BAD_SIGNATURE,
|
|
- N_("Error padding hash for RSA verification: %d"),
|
|
- rc);
|
|
- goto cleanup;
|
|
- }
|
|
-
|
|
- rc = _gcry_pubkey_spec_rsa.verify (0, hashmpi, &sig.pkcs7.sig_mpi,
|
|
- pk->mpis, NULL, NULL);
|
|
- gcry_mpi_release (hashmpi);
|
|
-
|
|
- if (rc == 0)
|
|
- {
|
|
- grub_dprintf ("appendedsig", "verify with key '%s' succeeded\n",
|
|
- pk->subject);
|
|
- err = GRUB_ERR_NONE;
|
|
- break;
|
|
- }
|
|
-
|
|
- grub_dprintf ("appendedsig", "verify with key '%s' failed with %d\n",
|
|
- pk->subject, rc);
|
|
+ /*
|
|
+ * This could be optimised in a couple of ways:
|
|
+ * - we could only compute hashes once per hash type
|
|
+ * - we could track signer information and only verify where IDs match
|
|
+ * For now we do the naive O(trusted keys * pkcs7 signers) approach.
|
|
+ */
|
|
+ si = &sig.pkcs7.signerInfos[i];
|
|
+ context = grub_zalloc (si->hash->contextsize);
|
|
+ if (!context)
|
|
+ return grub_errno;
|
|
+
|
|
+ si->hash->init (context);
|
|
+ si->hash->write (context, buf, datasize);
|
|
+ si->hash->final (context);
|
|
+ hash = si->hash->read (context);
|
|
+
|
|
+ grub_dprintf ("appendedsig", "data size %" PRIxGRUB_SIZE ", signer %d hash %02x%02x%02x%02x...\n",
|
|
+ datasize, i, hash[0], hash[1], hash[2], hash[3]);
|
|
+
|
|
+ err = GRUB_ERR_BAD_SIGNATURE;
|
|
+ for (pk = grub_trusted_key; pk; pk = pk->next)
|
|
+ {
|
|
+ rc = grub_crypto_rsa_pad (&hashmpi, hash, si->hash, pk->mpis[0]);
|
|
+ if (rc)
|
|
+ {
|
|
+ err = grub_error (GRUB_ERR_BAD_SIGNATURE,
|
|
+ N_("Error padding hash for RSA verification: %d"), rc);
|
|
+ grub_free (context);
|
|
+ goto cleanup;
|
|
+ }
|
|
+
|
|
+ rc = _gcry_pubkey_spec_rsa.verify (0, hashmpi, &si->sig_mpi, pk->mpis, NULL, NULL);
|
|
+ gcry_mpi_release (hashmpi);
|
|
+
|
|
+ if (rc == 0)
|
|
+ {
|
|
+ grub_dprintf ("appendedsig", "verify signer %d with key '%s' succeeded\n",
|
|
+ i, pk->subject);
|
|
+ err = GRUB_ERR_NONE;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ grub_dprintf ("appendedsig", "verify signer %d with key '%s' failed with %d\n",
|
|
+ i, pk->subject, rc);
|
|
+ }
|
|
+
|
|
+ grub_free (context);
|
|
+
|
|
+ if (err == GRUB_ERR_NONE)
|
|
+ break;
|
|
}
|
|
|
|
/* If we didn't verify, provide a neat message */
|
|
if (err != GRUB_ERR_NONE)
|
|
- err = grub_error (GRUB_ERR_BAD_SIGNATURE,
|
|
- N_("Failed to verify signature against a trusted key"));
|
|
+ err = grub_error (GRUB_ERR_BAD_SIGNATURE,
|
|
+ N_("Failed to verify signature against a trusted key"));
|
|
|
|
cleanup:
|
|
- grub_free (context);
|
|
pkcs7_signedData_release (&sig.pkcs7);
|
|
|
|
return err;
|
|
}
|
|
|
|
static grub_err_t
|
|
-grub_cmd_verify_signature (grub_command_t cmd __attribute__((unused)),
|
|
- int argc, char **args)
|
|
+grub_cmd_verify_signature (grub_command_t cmd __attribute__ ((unused)), int argc, char **args)
|
|
{
|
|
grub_file_t f;
|
|
grub_err_t err = GRUB_ERR_NONE;
|
|
grub_uint8_t *data;
|
|
- grub_ssize_t read_size;
|
|
- grub_off_t file_size, total_read_size = 0;
|
|
+ grub_size_t file_size;
|
|
|
|
if (argc < 1)
|
|
return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected"));
|
|
@@ -321,35 +342,14 @@ grub_cmd_verify_signature (grub_command_t cmd __attribute__((unused)),
|
|
goto cleanup;
|
|
}
|
|
|
|
- file_size = grub_file_size (f);
|
|
- if (file_size == GRUB_FILE_SIZE_UNKNOWN)
|
|
- return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
|
- N_("Cannot verify the signature of a file of unknown size"));
|
|
-
|
|
- data = grub_malloc (file_size);
|
|
- if (!data)
|
|
- return grub_error (GRUB_ERR_OUT_OF_MEMORY,
|
|
- N_("Could not allocate data buffer size %"
|
|
- PRIuGRUB_UINT64_T " for verification"), file_size);
|
|
-
|
|
- while (total_read_size < file_size)
|
|
- {
|
|
- read_size =
|
|
- grub_file_read (f, &data[total_read_size],
|
|
- file_size - total_read_size);
|
|
- if (read_size < 0)
|
|
- {
|
|
- err = grub_error (GRUB_ERR_READ_ERROR,
|
|
- N_("Error reading file to verify"));
|
|
- goto cleanup_data;
|
|
- }
|
|
- total_read_size += read_size;
|
|
- }
|
|
+ err = file_read_all (f, &data, &file_size);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ goto cleanup;
|
|
|
|
err = grub_verify_appended_signature (data, file_size);
|
|
|
|
-cleanup_data:
|
|
grub_free (data);
|
|
+
|
|
cleanup:
|
|
if (f)
|
|
grub_file_close (f);
|
|
@@ -357,8 +357,7 @@ cleanup:
|
|
}
|
|
|
|
static grub_err_t
|
|
-grub_cmd_distrust (grub_command_t cmd __attribute__((unused)),
|
|
- int argc, char **args)
|
|
+grub_cmd_distrust (grub_command_t cmd __attribute__ ((unused)), int argc, char **args)
|
|
{
|
|
unsigned long cert_num, i;
|
|
struct x509_certificate *cert, *prev;
|
|
@@ -373,7 +372,7 @@ grub_cmd_distrust (grub_command_t cmd __attribute__((unused)),
|
|
|
|
if (cert_num < 1)
|
|
return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
|
- N_("Certificate number too small - numbers start at 1"));
|
|
+ N_("Certificate number too small - numbers start at 1"));
|
|
|
|
if (cert_num == 1)
|
|
{
|
|
@@ -390,25 +389,24 @@ grub_cmd_distrust (grub_command_t cmd __attribute__((unused)),
|
|
while (cert)
|
|
{
|
|
if (i == cert_num)
|
|
- {
|
|
- prev->next = cert->next;
|
|
- certificate_release (cert);
|
|
- grub_free (cert);
|
|
- return GRUB_ERR_NONE;
|
|
- }
|
|
+ {
|
|
+ prev->next = cert->next;
|
|
+ certificate_release (cert);
|
|
+ grub_free (cert);
|
|
+ return GRUB_ERR_NONE;
|
|
+ }
|
|
i++;
|
|
prev = cert;
|
|
cert = cert->next;
|
|
}
|
|
|
|
return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
|
- N_("No certificate number %d found - only %d certificates in the store"),
|
|
- cert_num, i - 1);
|
|
+ N_("No certificate number %lu found - only %lu certificates in the store"),
|
|
+ cert_num, i - 1);
|
|
}
|
|
|
|
static grub_err_t
|
|
-grub_cmd_trust (grub_command_t cmd __attribute__((unused)),
|
|
- int argc, char **args)
|
|
+grub_cmd_trust (grub_command_t cmd __attribute__ ((unused)), int argc, char **args)
|
|
{
|
|
grub_file_t certf;
|
|
struct x509_certificate *cert = NULL;
|
|
@@ -417,17 +415,13 @@ grub_cmd_trust (grub_command_t cmd __attribute__((unused)),
|
|
if (argc != 1)
|
|
return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected"));
|
|
|
|
- certf = grub_file_open (args[0],
|
|
- GRUB_FILE_TYPE_CERTIFICATE_TRUST
|
|
- | GRUB_FILE_TYPE_NO_DECOMPRESS);
|
|
+ certf = grub_file_open (args[0], GRUB_FILE_TYPE_CERTIFICATE_TRUST | GRUB_FILE_TYPE_NO_DECOMPRESS);
|
|
if (!certf)
|
|
return grub_errno;
|
|
|
|
-
|
|
cert = grub_zalloc (sizeof (struct x509_certificate));
|
|
if (!cert)
|
|
- return grub_error (GRUB_ERR_OUT_OF_MEMORY,
|
|
- N_("Could not allocate memory for certificate"));
|
|
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("Could not allocate memory for certificate"));
|
|
|
|
err = read_cert_from_file (certf, cert);
|
|
grub_file_close (certf);
|
|
@@ -436,8 +430,7 @@ grub_cmd_trust (grub_command_t cmd __attribute__((unused)),
|
|
grub_free (cert);
|
|
return err;
|
|
}
|
|
- grub_dprintf ("appendedsig", "Loaded certificate with CN: %s\n",
|
|
- cert->subject);
|
|
+ grub_dprintf ("appendedsig", "Loaded certificate with CN: %s\n", cert->subject);
|
|
|
|
cert->next = grub_trusted_key;
|
|
grub_trusted_key = cert;
|
|
@@ -446,9 +439,8 @@ grub_cmd_trust (grub_command_t cmd __attribute__((unused)),
|
|
}
|
|
|
|
static grub_err_t
|
|
-grub_cmd_list (grub_command_t cmd __attribute__((unused)),
|
|
- int argc __attribute__((unused)),
|
|
- char **args __attribute__((unused)))
|
|
+grub_cmd_list (grub_command_t cmd __attribute__ ((unused)), int argc __attribute__ ((unused)),
|
|
+ char **args __attribute__ ((unused)))
|
|
{
|
|
struct x509_certificate *cert;
|
|
int cert_num = 1;
|
|
@@ -460,26 +452,23 @@ grub_cmd_list (grub_command_t cmd __attribute__((unused)),
|
|
|
|
grub_printf (N_("\tSerial: "));
|
|
for (i = 0; i < cert->serial_len - 1; i++)
|
|
- {
|
|
- grub_printf ("%02x:", cert->serial[i]);
|
|
- }
|
|
+ {
|
|
+ grub_printf ("%02x:", cert->serial[i]);
|
|
+ }
|
|
grub_printf ("%02x\n", cert->serial[cert->serial_len - 1]);
|
|
|
|
grub_printf ("\tCN: %s\n\n", cert->subject);
|
|
cert_num++;
|
|
-
|
|
}
|
|
|
|
return GRUB_ERR_NONE;
|
|
}
|
|
|
|
static grub_err_t
|
|
-appendedsig_init (grub_file_t io __attribute__((unused)),
|
|
- enum grub_file_type type,
|
|
- void **context __attribute__((unused)),
|
|
- enum grub_verify_flags *flags)
|
|
+appendedsig_init (grub_file_t io __attribute__ ((unused)), enum grub_file_type type,
|
|
+ void **context __attribute__ ((unused)), enum grub_verify_flags *flags)
|
|
{
|
|
- if (!check_sigs)
|
|
+ if (check_sigs == check_sigs_no)
|
|
{
|
|
*flags = GRUB_VERIFY_FLAGS_SKIP_VERIFICATION;
|
|
return GRUB_ERR_NONE;
|
|
@@ -487,48 +476,47 @@ appendedsig_init (grub_file_t io __attribute__((unused)),
|
|
|
|
switch (type & GRUB_FILE_TYPE_MASK)
|
|
{
|
|
- case GRUB_FILE_TYPE_CERTIFICATE_TRUST:
|
|
- /*
|
|
- * This is a certificate to add to trusted keychain.
|
|
- *
|
|
- * This needs to be verified or blocked. Ideally we'd write an x509
|
|
- * verifier, but we lack the hubris required to take this on. Instead,
|
|
- * require that it have an appended signature.
|
|
- */
|
|
+ case GRUB_FILE_TYPE_CERTIFICATE_TRUST:
|
|
+ /*
|
|
+ * This is a certificate to add to trusted keychain.
|
|
+ *
|
|
+ * This needs to be verified or blocked. Ideally we'd write an x509
|
|
+ * verifier, but we lack the hubris required to take this on. Instead,
|
|
+ * require that it have an appended signature.
|
|
+ */
|
|
|
|
- /* Fall through */
|
|
+ /* Fall through */
|
|
|
|
- case GRUB_FILE_TYPE_LINUX_KERNEL:
|
|
- case GRUB_FILE_TYPE_GRUB_MODULE:
|
|
- /*
|
|
- * Appended signatures are only defined for ELF binaries.
|
|
- * Out of an abundance of caution, we only verify Linux kernels and
|
|
- * GRUB modules at this point.
|
|
- */
|
|
- *flags = GRUB_VERIFY_FLAGS_SINGLE_CHUNK;
|
|
- return GRUB_ERR_NONE;
|
|
+ case GRUB_FILE_TYPE_LINUX_KERNEL:
|
|
+ case GRUB_FILE_TYPE_GRUB_MODULE:
|
|
+ /*
|
|
+ * Appended signatures are only defined for ELF binaries.
|
|
+ * Out of an abundance of caution, we only verify Linux kernels and
|
|
+ * GRUB modules at this point.
|
|
+ */
|
|
+ *flags = GRUB_VERIFY_FLAGS_SINGLE_CHUNK;
|
|
+ return GRUB_ERR_NONE;
|
|
|
|
- case GRUB_FILE_TYPE_ACPI_TABLE:
|
|
- case GRUB_FILE_TYPE_DEVICE_TREE_IMAGE:
|
|
- /*
|
|
- * It is possible to use appended signature verification without
|
|
- * lockdown - like the PGP verifier. When combined with an embedded
|
|
- * config file in a signed grub binary, this could still be a meaningful
|
|
- * secure-boot chain - so long as it isn't subverted by something like a
|
|
- * rouge ACPI table or DT image. Defer them explicitly.
|
|
- */
|
|
- *flags = GRUB_VERIFY_FLAGS_DEFER_AUTH;
|
|
- return GRUB_ERR_NONE;
|
|
+ case GRUB_FILE_TYPE_ACPI_TABLE:
|
|
+ case GRUB_FILE_TYPE_DEVICE_TREE_IMAGE:
|
|
+ /*
|
|
+ * It is possible to use appended signature verification without
|
|
+ * lockdown - like the PGP verifier. When combined with an embedded
|
|
+ * config file in a signed grub binary, this could still be a meaningful
|
|
+ * secure-boot chain - so long as it isn't subverted by something like a
|
|
+ * rouge ACPI table or DT image. Defer them explicitly.
|
|
+ */
|
|
+ *flags = GRUB_VERIFY_FLAGS_DEFER_AUTH;
|
|
+ return GRUB_ERR_NONE;
|
|
|
|
- default:
|
|
- *flags = GRUB_VERIFY_FLAGS_SKIP_VERIFICATION;
|
|
- return GRUB_ERR_NONE;
|
|
+ default:
|
|
+ *flags = GRUB_VERIFY_FLAGS_SKIP_VERIFICATION;
|
|
+ return GRUB_ERR_NONE;
|
|
}
|
|
}
|
|
|
|
static grub_err_t
|
|
-appendedsig_write (void *ctxt __attribute__((unused)),
|
|
- void *buf, grub_size_t size)
|
|
+appendedsig_write (void *ctxt __attribute__ ((unused)), void *buf, grub_size_t size)
|
|
{
|
|
return grub_verify_appended_signature (buf, size);
|
|
}
|
|
@@ -547,10 +535,7 @@ pseudo_read (struct grub_file *file, char *buf, grub_size_t len)
|
|
}
|
|
|
|
/* Filesystem descriptor. */
|
|
-static struct grub_fs pseudo_fs = {
|
|
- .name = "pseudo",
|
|
- .fs_read = pseudo_read
|
|
-};
|
|
+static struct grub_fs pseudo_fs = { .name = "pseudo", .fs_read = pseudo_read };
|
|
|
|
static grub_command_t cmd_verify, cmd_list, cmd_distrust, cmd_trust;
|
|
|
|
@@ -561,19 +546,15 @@ GRUB_MOD_INIT (appendedsig)
|
|
|
|
/* If in lockdown, immediately enter forced mode */
|
|
if (grub_is_lockdown () == GRUB_LOCKDOWN_ENABLED)
|
|
- check_sigs = 2;
|
|
+ check_sigs = check_sigs_forced;
|
|
|
|
grub_trusted_key = NULL;
|
|
-
|
|
- grub_register_variable_hook ("check_appended_signatures",
|
|
- grub_env_read_sec,
|
|
- grub_env_write_sec);
|
|
+ grub_register_variable_hook ("check_appended_signatures", grub_env_read_sec, grub_env_write_sec);
|
|
grub_env_export ("check_appended_signatures");
|
|
|
|
rc = asn1_init ();
|
|
if (rc)
|
|
- grub_fatal ("Error initing ASN.1 data structures: %d: %s\n", rc,
|
|
- asn1_strerror (rc));
|
|
+ grub_fatal ("Error initing ASN.1 data structures: %d: %s\n", rc, asn1_strerror (rc));
|
|
|
|
FOR_MODULES (header)
|
|
{
|
|
@@ -581,7 +562,7 @@ GRUB_MOD_INIT (appendedsig)
|
|
struct x509_certificate *pk = NULL;
|
|
grub_err_t err;
|
|
|
|
- /* Not an ELF module, skip. */
|
|
+ /* Not an X.509 certificate, skip. */
|
|
if (header->type != OBJ_TYPE_X509_PUBKEY)
|
|
continue;
|
|
|
|
@@ -590,15 +571,12 @@ GRUB_MOD_INIT (appendedsig)
|
|
pseudo_file.size = header->size - sizeof (struct grub_module_header);
|
|
pseudo_file.data = (char *) header + sizeof (struct grub_module_header);
|
|
|
|
- grub_dprintf ("appendedsig",
|
|
- "Found an x509 key, size=%" PRIuGRUB_UINT64_T "\n",
|
|
- pseudo_file.size);
|
|
+ grub_dprintf ("appendedsig", "Found an x509 key, size=%" PRIuGRUB_UINT64_T "\n",
|
|
+ pseudo_file.size);
|
|
|
|
pk = grub_zalloc (sizeof (struct x509_certificate));
|
|
if (!pk)
|
|
- {
|
|
- grub_fatal ("Out of memory loading initial certificates");
|
|
- }
|
|
+ grub_fatal ("Out of memory loading initial certificates");
|
|
|
|
err = read_cert_from_file (&pseudo_file, pk);
|
|
if (err != GRUB_ERR_NONE)
|
|
@@ -610,21 +588,16 @@ GRUB_MOD_INIT (appendedsig)
|
|
grub_trusted_key = pk;
|
|
}
|
|
|
|
- cmd_trust =
|
|
- grub_register_command ("trust_certificate", grub_cmd_trust,
|
|
- N_("X509_CERTIFICATE"),
|
|
- N_("Add X509_CERTIFICATE to trusted certificates."));
|
|
- cmd_list =
|
|
- grub_register_command ("list_certificates", grub_cmd_list, 0,
|
|
- N_("Show the list of trusted x509 certificates."));
|
|
- cmd_verify =
|
|
- grub_register_command ("verify_appended", grub_cmd_verify_signature,
|
|
- N_("FILE"),
|
|
- N_("Verify FILE against the trusted x509 certificates."));
|
|
- cmd_distrust =
|
|
- grub_register_command ("distrust_certificate", grub_cmd_distrust,
|
|
- N_("CERT_NUMBER"),
|
|
- N_("Remove CERT_NUMBER (as listed by list_certificates) from trusted certificates."));
|
|
+ cmd_trust = grub_register_command ("trust_certificate", grub_cmd_trust, N_("X509_CERTIFICATE"),
|
|
+ N_("Add X509_CERTIFICATE to trusted certificates."));
|
|
+ cmd_list = grub_register_command ("list_certificates", grub_cmd_list, 0,
|
|
+ N_("Show the list of trusted x509 certificates."));
|
|
+ cmd_verify = grub_register_command ("verify_appended", grub_cmd_verify_signature, N_("FILE"),
|
|
+ N_("Verify FILE against the trusted x509 certificates."));
|
|
+ cmd_distrust = grub_register_command ("distrust_certificate", grub_cmd_distrust,
|
|
+ N_("CERT_NUMBER"),
|
|
+ N_("Remove CERT_NUMBER (as listed by list_certificates)"
|
|
+ " from trusted certificates."));
|
|
|
|
grub_verifier_register (&grub_appendedsig_verifier);
|
|
grub_dl_set_persistent (mod);
|
|
diff --git a/grub-core/commands/appendedsig/appendedsig.h b/grub-core/commands/appendedsig/appendedsig.h
|
|
index 9792ef3..3f4d700 100644
|
|
--- a/grub-core/commands/appendedsig/appendedsig.h
|
|
+++ b/grub-core/commands/appendedsig/appendedsig.h
|
|
@@ -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 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
|
|
@@ -26,85 +27,84 @@ extern asn1_node _gnutls_pkix_asn;
|
|
|
|
/*
|
|
* One or more x509 certificates.
|
|
- *
|
|
* We do limited parsing: extracting only the serial, CN and RSA public key.
|
|
*/
|
|
struct x509_certificate
|
|
{
|
|
struct x509_certificate *next;
|
|
-
|
|
grub_uint8_t *serial;
|
|
grub_size_t serial_len;
|
|
-
|
|
char *subject;
|
|
grub_size_t subject_len;
|
|
-
|
|
/* We only support RSA public keys. This encodes [modulus, publicExponent] */
|
|
gcry_mpi_t mpis[2];
|
|
};
|
|
|
|
+/*
|
|
+ * A PKCS#7 signedData signerInfo.
|
|
+ */
|
|
+struct pkcs7_signerInfo
|
|
+{
|
|
+ const gcry_md_spec_t *hash;
|
|
+ gcry_mpi_t sig_mpi;
|
|
+};
|
|
+
|
|
/*
|
|
* A PKCS#7 signedData message.
|
|
- *
|
|
* We make no attempt to match intelligently, so we don't save any info about
|
|
- * the signer. We also support only 1 signerInfo, so we only store a single
|
|
- * MPI for the signature.
|
|
+ * the signer.
|
|
*/
|
|
struct pkcs7_signedData
|
|
{
|
|
- const gcry_md_spec_t *hash;
|
|
- gcry_mpi_t sig_mpi;
|
|
+ int signerInfo_count;
|
|
+ struct pkcs7_signerInfo *signerInfos;
|
|
};
|
|
|
|
-
|
|
/* Do libtasn1 init */
|
|
-int asn1_init (void);
|
|
+int
|
|
+asn1_init (void);
|
|
|
|
/*
|
|
* Import a DER-encoded certificate at 'data', of size 'size'.
|
|
- *
|
|
* Place the results into 'results', which must be already allocated.
|
|
*/
|
|
grub_err_t
|
|
-certificate_import (void *data, grub_size_t size,
|
|
- struct x509_certificate *results);
|
|
+parse_x509_certificate (const void *data, grub_size_t size, struct x509_certificate *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.
|
|
*/
|
|
-void certificate_release (struct x509_certificate *cert);
|
|
+void
|
|
+certificate_release (struct x509_certificate *cert);
|
|
|
|
/*
|
|
* Parse a PKCS#7 message, which must be a signedData message.
|
|
- *
|
|
* The message must be in 'sigbuf' and of size 'data_size'. The result is
|
|
* placed in 'msg', which must already be allocated.
|
|
*/
|
|
grub_err_t
|
|
-parse_pkcs7_signedData (void *sigbuf, grub_size_t data_size,
|
|
- struct pkcs7_signedData *msg);
|
|
+parse_pkcs7_signedData (const void *sigbuf, grub_size_t data_size, struct pkcs7_signedData *msg);
|
|
|
|
/*
|
|
* Release all the storage associated with the PKCS#7 message.
|
|
* If the caller dynamically allocated the message, it must free it.
|
|
*/
|
|
-void pkcs7_signedData_release (struct pkcs7_signedData *msg);
|
|
+void
|
|
+pkcs7_signedData_release (struct pkcs7_signedData *msg);
|
|
|
|
/*
|
|
* Read a value from an ASN1 node, allocating memory to store it.
|
|
- *
|
|
* It will work for anything where the size libtasn1 returns is right:
|
|
* - Integers
|
|
* - Octet strings
|
|
* - DER encoding of other structures
|
|
* It will _not_ work for things where libtasn1 size requires adjustment:
|
|
- * - Strings that require an extra NULL byte at the end
|
|
+ * - Strings that require an extra null byte at the end
|
|
* - Bit strings because libtasn1 returns the length in bits, not bytes.
|
|
*
|
|
* If the function returns a non-NULL value, the caller must free it.
|
|
*/
|
|
-void *grub_asn1_allocate_and_read (asn1_node node, const char *name,
|
|
- const char *friendly_name,
|
|
- int *content_size);
|
|
+void *
|
|
+grub_asn1_allocate_and_read (asn1_node node, const char *name, const char *friendly_name, int *content_size);
|
|
diff --git a/grub-core/commands/appendedsig/asn1util.c b/grub-core/commands/appendedsig/asn1util.c
|
|
index eff095a..06c3b61 100644
|
|
--- a/grub-core/commands/appendedsig/asn1util.c
|
|
+++ b/grub-core/commands/appendedsig/asn1util.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 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
|
|
@@ -21,19 +22,19 @@
|
|
#include <grub/err.h>
|
|
#include <grub/mm.h>
|
|
#include <grub/crypto.h>
|
|
+#include <grub/misc.h>
|
|
#include <grub/gcrypt/gcrypt.h>
|
|
|
|
#include "appendedsig.h"
|
|
|
|
-asn1_node _gnutls_gnutls_asn = ASN1_TYPE_EMPTY;
|
|
-asn1_node _gnutls_pkix_asn = ASN1_TYPE_EMPTY;
|
|
+asn1_node _gnutls_gnutls_asn = NULL;
|
|
+asn1_node _gnutls_pkix_asn = NULL;
|
|
|
|
-extern const ASN1_ARRAY_TYPE gnutls_asn1_tab[];
|
|
-extern const ASN1_ARRAY_TYPE pkix_asn1_tab[];
|
|
+extern const asn1_static_node gnutls_asn1_tab[];
|
|
+extern const asn1_static_node pkix_asn1_tab[];
|
|
|
|
/*
|
|
* Read a value from an ASN1 node, allocating memory to store it.
|
|
- *
|
|
* It will work for anything where the size libtasn1 returns is right:
|
|
* - Integers
|
|
* - Octet strings
|
|
@@ -41,12 +42,10 @@ extern const ASN1_ARRAY_TYPE pkix_asn1_tab[];
|
|
* It will _not_ work for things where libtasn1 size requires adjustment:
|
|
* - Strings that require an extra NULL byte at the end
|
|
* - Bit strings because libtasn1 returns the length in bits, not bytes.
|
|
- *
|
|
* If the function returns a non-NULL value, the caller must free it.
|
|
*/
|
|
void *
|
|
-grub_asn1_allocate_and_read (asn1_node node, const char *name,
|
|
- const char *friendly_name, int *content_size)
|
|
+grub_asn1_allocate_and_read (asn1_node node, const char *name, const char *friendly_name, int *content_size)
|
|
{
|
|
int result;
|
|
grub_uint8_t *tmpstr = NULL;
|
|
@@ -56,9 +55,8 @@ grub_asn1_allocate_and_read (asn1_node node, const char *name,
|
|
if (result != ASN1_MEM_ERROR)
|
|
{
|
|
grub_snprintf (grub_errmsg, sizeof (grub_errmsg),
|
|
- _
|
|
- ("Reading size of %s did not return expected status: %s"),
|
|
- friendly_name, asn1_strerror (result));
|
|
+ _("Reading size of %s did not return expected status: %s"),
|
|
+ friendly_name, asn1_strerror (result));
|
|
grub_errno = GRUB_ERR_BAD_FILE_TYPE;
|
|
return NULL;
|
|
}
|
|
@@ -67,7 +65,7 @@ grub_asn1_allocate_and_read (asn1_node node, const char *name,
|
|
if (tmpstr == NULL)
|
|
{
|
|
grub_snprintf (grub_errmsg, sizeof (grub_errmsg),
|
|
- "Could not allocate memory to store %s", friendly_name);
|
|
+ "Could not allocate memory to store %s", friendly_name);
|
|
grub_errno = GRUB_ERR_OUT_OF_MEMORY;
|
|
return NULL;
|
|
}
|
|
@@ -76,9 +74,8 @@ grub_asn1_allocate_and_read (asn1_node node, const char *name,
|
|
if (result != ASN1_SUCCESS)
|
|
{
|
|
grub_free (tmpstr);
|
|
- grub_snprintf (grub_errmsg, sizeof (grub_errmsg),
|
|
- "Error reading %s: %s",
|
|
- friendly_name, asn1_strerror (result));
|
|
+ grub_snprintf (grub_errmsg, sizeof (grub_errmsg), "Error reading %s: %s",
|
|
+ friendly_name, asn1_strerror (result));
|
|
grub_errno = GRUB_ERR_BAD_FILE_TYPE;
|
|
return NULL;
|
|
}
|
|
@@ -94,9 +91,8 @@ asn1_init (void)
|
|
int res;
|
|
res = asn1_array2tree (gnutls_asn1_tab, &_gnutls_gnutls_asn, NULL);
|
|
if (res != ASN1_SUCCESS)
|
|
- {
|
|
- return res;
|
|
- }
|
|
+ return res;
|
|
+
|
|
res = asn1_array2tree (pkix_asn1_tab, &_gnutls_pkix_asn, NULL);
|
|
return res;
|
|
}
|
|
diff --git a/grub-core/commands/appendedsig/pkcs7.c b/grub-core/commands/appendedsig/pkcs7.c
|
|
index dc6afe2..31a5eab 100644
|
|
--- a/grub-core/commands/appendedsig/pkcs7.c
|
|
+++ b/grub-core/commands/appendedsig/pkcs7.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 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
|
|
@@ -20,24 +21,23 @@
|
|
#include <grub/misc.h>
|
|
#include <grub/crypto.h>
|
|
#include <grub/gcrypt/gcrypt.h>
|
|
-
|
|
+#include <sys/types.h>
|
|
|
|
static char asn1_error[ASN1_MAX_ERROR_DESCRIPTION_SIZE];
|
|
|
|
/*
|
|
* RFC 5652 s 5.1
|
|
*/
|
|
-const char *signedData_oid = "1.2.840.113549.1.7.2";
|
|
+static const char *signedData_oid = "1.2.840.113549.1.7.2";
|
|
|
|
/*
|
|
* RFC 4055 s 2.1
|
|
*/
|
|
-const char *sha256_oid = "2.16.840.1.101.3.4.2.1";
|
|
-const char *sha512_oid = "2.16.840.1.101.3.4.2.3";
|
|
+static const char *sha256_oid = "2.16.840.1.101.3.4.2.1";
|
|
+static const char *sha512_oid = "2.16.840.1.101.3.4.2.3";
|
|
|
|
static grub_err_t
|
|
-process_content (grub_uint8_t * content, int size,
|
|
- struct pkcs7_signedData *msg)
|
|
+process_content (grub_uint8_t *content, int size, struct pkcs7_signedData *msg)
|
|
{
|
|
int res;
|
|
asn1_node signed_part;
|
|
@@ -45,32 +45,35 @@ process_content (grub_uint8_t * content, int size,
|
|
char algo_oid[MAX_OID_LEN];
|
|
int algo_oid_size = sizeof (algo_oid);
|
|
int algo_count;
|
|
+ int signer_count;
|
|
+ int i;
|
|
char version;
|
|
int version_size = sizeof (version);
|
|
grub_uint8_t *result_buf;
|
|
int result_size = 0;
|
|
int crls_size = 0;
|
|
gcry_error_t gcry_err;
|
|
+ bool sha256_in_da, sha256_in_si, sha512_in_da, sha512_in_si;
|
|
+ char *da_path;
|
|
+ char *si_sig_path;
|
|
+ char *si_da_path;
|
|
|
|
- res = asn1_create_element (_gnutls_pkix_asn, "PKIX1.pkcs-7-SignedData",
|
|
- &signed_part);
|
|
+ res = asn1_create_element (_gnutls_pkix_asn, "PKIX1.pkcs-7-SignedData", &signed_part);
|
|
if (res != ASN1_SUCCESS)
|
|
- {
|
|
- return grub_error (GRUB_ERR_OUT_OF_MEMORY,
|
|
- "Could not create ASN.1 structure for PKCS#7 signed part.");
|
|
- }
|
|
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
|
|
+ "Could not create ASN.1 structure for PKCS#7 signed part.");
|
|
|
|
res = asn1_der_decoding2 (&signed_part, content, &size,
|
|
- ASN1_DECODE_FLAG_STRICT_DER, asn1_error);
|
|
+ ASN1_DECODE_FLAG_STRICT_DER, asn1_error);
|
|
if (res != ASN1_SUCCESS)
|
|
{
|
|
- err =
|
|
- grub_error (GRUB_ERR_BAD_SIGNATURE,
|
|
- "Error reading PKCS#7 signed data: %s", asn1_error);
|
|
+ err = grub_error (GRUB_ERR_BAD_SIGNATURE,
|
|
+ "Error reading PKCS#7 signed data: %s", asn1_error);
|
|
goto cleanup_signed_part;
|
|
}
|
|
|
|
- /* SignedData ::= SEQUENCE {
|
|
+ /*
|
|
+ * SignedData ::= SEQUENCE {
|
|
* version CMSVersion,
|
|
* digestAlgorithms DigestAlgorithmIdentifiers,
|
|
* encapContentInfo EncapsulatedContentInfo,
|
|
@@ -83,19 +86,15 @@ process_content (grub_uint8_t * content, int size,
|
|
res = asn1_read_value (signed_part, "version", &version, &version_size);
|
|
if (res != ASN1_SUCCESS)
|
|
{
|
|
- err =
|
|
- grub_error (GRUB_ERR_BAD_SIGNATURE,
|
|
- "Error reading signedData version: %s",
|
|
- asn1_strerror (res));
|
|
+ err = grub_error (GRUB_ERR_BAD_SIGNATURE, "Error reading signedData version: %s",
|
|
+ asn1_strerror (res));
|
|
goto cleanup_signed_part;
|
|
}
|
|
|
|
if (version != 1)
|
|
{
|
|
- err =
|
|
- grub_error (GRUB_ERR_BAD_SIGNATURE,
|
|
- "Unexpected signature version v%d, only v1 supported",
|
|
- version);
|
|
+ err = grub_error (GRUB_ERR_BAD_SIGNATURE,
|
|
+ "Unexpected signature version v%d, only v1 supported", version);
|
|
goto cleanup_signed_part;
|
|
}
|
|
|
|
@@ -104,69 +103,96 @@ process_content (grub_uint8_t * content, int size,
|
|
*
|
|
* DigestAlgorithmIdentifiers ::= SET OF DigestAlgorithmIdentifier
|
|
* DigestAlgorithmIdentifer is an X.509 AlgorithmIdentifier (10.1.1)
|
|
- *
|
|
+ *
|
|
* RFC 4055 s 2.1:
|
|
* sha256Identifier AlgorithmIdentifier ::= { id-sha256, NULL }
|
|
* sha512Identifier AlgorithmIdentifier ::= { id-sha512, NULL }
|
|
*
|
|
* We only support 1 element in the set, and we do not check parameters atm.
|
|
*/
|
|
- res =
|
|
- asn1_number_of_elements (signed_part, "digestAlgorithms", &algo_count);
|
|
+ res = asn1_number_of_elements (signed_part, "digestAlgorithms", &algo_count);
|
|
if (res != ASN1_SUCCESS)
|
|
{
|
|
- err =
|
|
- grub_error (GRUB_ERR_BAD_SIGNATURE,
|
|
- "Error counting number of digest algorithms: %s",
|
|
- asn1_strerror (res));
|
|
+ err = grub_error (GRUB_ERR_BAD_SIGNATURE, "Error counting number of digest algorithms: %s",
|
|
+ asn1_strerror (res));
|
|
goto cleanup_signed_part;
|
|
}
|
|
|
|
- if (algo_count != 1)
|
|
+ if (algo_count <= 0)
|
|
{
|
|
- err =
|
|
- grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
|
|
- "Only 1 digest algorithm is supported");
|
|
+ err = grub_error (GRUB_ERR_BAD_SIGNATURE,
|
|
+ "A minimum of 1 digest algorithm is required");
|
|
goto cleanup_signed_part;
|
|
}
|
|
|
|
- res =
|
|
- asn1_read_value (signed_part, "digestAlgorithms.?1.algorithm", algo_oid,
|
|
- &algo_oid_size);
|
|
- if (res != ASN1_SUCCESS)
|
|
+ if (algo_count > 2)
|
|
{
|
|
- err =
|
|
- grub_error (GRUB_ERR_BAD_SIGNATURE,
|
|
- "Error reading digest algorithm: %s",
|
|
- asn1_strerror (res));
|
|
+ err = grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
|
|
+ "A maximum of 2 digest algorithms is supported");
|
|
goto cleanup_signed_part;
|
|
}
|
|
|
|
- if (grub_strncmp (sha512_oid, algo_oid, algo_oid_size) == 0)
|
|
- {
|
|
- msg->hash = grub_crypto_lookup_md_by_name ("sha512");
|
|
- }
|
|
- else if (grub_strncmp (sha256_oid, algo_oid, algo_oid_size) == 0)
|
|
- {
|
|
- msg->hash = grub_crypto_lookup_md_by_name ("sha256");
|
|
- }
|
|
- else
|
|
- {
|
|
- err =
|
|
- grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
|
|
- "Only SHA-256 and SHA-512 hashes are supported, found OID %s",
|
|
- algo_oid);
|
|
- goto cleanup_signed_part;
|
|
- }
|
|
+ sha256_in_da = false;
|
|
+ sha512_in_da = false;
|
|
|
|
- if (!msg->hash)
|
|
+ for (i = 0; i < algo_count; i++)
|
|
{
|
|
- err =
|
|
- grub_error (GRUB_ERR_BAD_SIGNATURE,
|
|
- "Hash algorithm for OID %s not loaded", algo_oid);
|
|
- goto cleanup_signed_part;
|
|
+ da_path = grub_xasprintf ("digestAlgorithms.?%d.algorithm", i + 1);
|
|
+ if (!da_path)
|
|
+ {
|
|
+ err = grub_error (GRUB_ERR_OUT_OF_MEMORY,
|
|
+ "Could not allocate path for digest algorithm "
|
|
+ "parsing path");
|
|
+ goto cleanup_signed_part;
|
|
+ }
|
|
+
|
|
+ algo_oid_size = sizeof (algo_oid);
|
|
+ res = asn1_read_value (signed_part, da_path, algo_oid, &algo_oid_size);
|
|
+ if (res != ASN1_SUCCESS)
|
|
+ {
|
|
+ err = grub_error (GRUB_ERR_BAD_SIGNATURE, "Error reading digest algorithm: %s",
|
|
+ asn1_strerror (res));
|
|
+ grub_free (da_path);
|
|
+ goto cleanup_signed_part;
|
|
+ }
|
|
+
|
|
+ if (grub_strncmp (sha512_oid, algo_oid, algo_oid_size) == 0)
|
|
+ {
|
|
+ if (!sha512_in_da)
|
|
+ sha512_in_da = true;
|
|
+ else
|
|
+ {
|
|
+ err = grub_error (GRUB_ERR_BAD_SIGNATURE,
|
|
+ "SHA-512 specified twice in digest algorithm list");
|
|
+ grub_free (da_path);
|
|
+ goto cleanup_signed_part;
|
|
+ }
|
|
+ }
|
|
+ else if (grub_strncmp (sha256_oid, algo_oid, algo_oid_size) == 0)
|
|
+ {
|
|
+ if (!sha256_in_da)
|
|
+ sha256_in_da = true;
|
|
+ else
|
|
+ {
|
|
+ err = grub_error (GRUB_ERR_BAD_SIGNATURE,
|
|
+ "SHA-256 specified twice in digest algorithm list");
|
|
+ grub_free (da_path);
|
|
+ goto cleanup_signed_part;
|
|
+ }
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ err = grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "Only SHA-256 and SHA-512 hashes are supported, found OID %s",
|
|
+ algo_oid);
|
|
+ grub_free (da_path);
|
|
+ goto cleanup_signed_part;
|
|
+ }
|
|
+
|
|
+ grub_free (da_path);
|
|
}
|
|
|
|
+ /* at this point, at least one of sha{256,512}_in_da must be true */
|
|
+
|
|
/*
|
|
* We ignore the certificates, but we don't permit CRLs.
|
|
* A CRL entry might be revoking the certificate we're using, and we have
|
|
@@ -175,45 +201,179 @@ process_content (grub_uint8_t * content, int size,
|
|
res = asn1_read_value (signed_part, "crls", NULL, &crls_size);
|
|
if (res != ASN1_ELEMENT_NOT_FOUND)
|
|
{
|
|
- err =
|
|
- grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
|
|
- "PKCS#7 messages with embedded CRLs are not supported");
|
|
+ err = grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
|
|
+ "PKCS#7 messages with embedded CRLs are not supported");
|
|
goto cleanup_signed_part;
|
|
}
|
|
|
|
- /* read the signature */
|
|
- result_buf =
|
|
- grub_asn1_allocate_and_read (signed_part, "signerInfos.?1.signature",
|
|
- "signature data", &result_size);
|
|
- if (!result_buf)
|
|
+ /* read the signatures */
|
|
+
|
|
+ res = asn1_number_of_elements (signed_part, "signerInfos", &signer_count);
|
|
+ if (res != ASN1_SUCCESS)
|
|
{
|
|
- err = grub_errno;
|
|
+ err = grub_error (GRUB_ERR_BAD_SIGNATURE, "Error counting number of signers: %s",
|
|
+ asn1_strerror (res));
|
|
goto cleanup_signed_part;
|
|
}
|
|
|
|
- gcry_err =
|
|
- gcry_mpi_scan (&(msg->sig_mpi), GCRYMPI_FMT_USG, result_buf, result_size,
|
|
- NULL);
|
|
- if (gcry_err != GPG_ERR_NO_ERROR)
|
|
+ if (signer_count <= 0)
|
|
{
|
|
- err =
|
|
- grub_error (GRUB_ERR_BAD_SIGNATURE,
|
|
- "Error loading signature into MPI structure: %d",
|
|
- gcry_err);
|
|
- goto cleanup_result;
|
|
+ err = grub_error (GRUB_ERR_BAD_SIGNATURE, "A minimum of 1 signer is required");
|
|
+ goto cleanup_signed_part;
|
|
}
|
|
|
|
-cleanup_result:
|
|
- grub_free (result_buf);
|
|
+ msg->signerInfos = grub_calloc (signer_count, sizeof (struct pkcs7_signerInfo));
|
|
+ if (!msg->signerInfos)
|
|
+ {
|
|
+ err = grub_error (GRUB_ERR_OUT_OF_MEMORY,
|
|
+ "Could not allocate space for %d signers", signer_count);
|
|
+ goto cleanup_signed_part;
|
|
+ }
|
|
+
|
|
+ msg->signerInfo_count = 0;
|
|
+ for (i = 0; i < signer_count; i++)
|
|
+ {
|
|
+ si_da_path = grub_xasprintf ("signerInfos.?%d.digestAlgorithm.algorithm", i + 1);
|
|
+ if (!si_da_path)
|
|
+ {
|
|
+ err = grub_error (GRUB_ERR_OUT_OF_MEMORY, "Could not allocate path for signer %d's digest algorithm parsing path",
|
|
+ i);
|
|
+ goto cleanup_signerInfos;
|
|
+ }
|
|
+
|
|
+ algo_oid_size = sizeof (algo_oid);
|
|
+ res = asn1_read_value (signed_part, si_da_path, algo_oid, &algo_oid_size);
|
|
+ if (res != ASN1_SUCCESS)
|
|
+ {
|
|
+ err = grub_error (GRUB_ERR_BAD_SIGNATURE,
|
|
+ "Error reading signer %d's digest algorithm: %s", i,
|
|
+ asn1_strerror (res));
|
|
+ grub_free (si_da_path);
|
|
+ goto cleanup_signerInfos;
|
|
+ }
|
|
+
|
|
+ grub_free (si_da_path);
|
|
+
|
|
+ if (grub_strncmp (sha512_oid, algo_oid, algo_oid_size) == 0)
|
|
+ {
|
|
+ if (!sha512_in_da)
|
|
+ {
|
|
+ err = grub_error (GRUB_ERR_BAD_SIGNATURE,
|
|
+ "Signer %d claims a SHA-512 signature which was not specified in the outer DigestAlgorithms",
|
|
+ i);
|
|
+ goto cleanup_signerInfos;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ sha512_in_si = true;
|
|
+ msg->signerInfos[i].hash = grub_crypto_lookup_md_by_name ("sha512");
|
|
+ }
|
|
+ }
|
|
+ else if (grub_strncmp (sha256_oid, algo_oid, algo_oid_size) == 0)
|
|
+ {
|
|
+ if (!sha256_in_da)
|
|
+ {
|
|
+ err = grub_error (GRUB_ERR_BAD_SIGNATURE,
|
|
+ "Signer %d claims a SHA-256 signature which was not specified in the outer DigestAlgorithms",
|
|
+ i);
|
|
+ goto cleanup_signerInfos;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ sha256_in_si = true;
|
|
+ msg->signerInfos[i].hash = grub_crypto_lookup_md_by_name ("sha256");
|
|
+ }
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ err = grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
|
|
+ "Only SHA-256 and SHA-512 hashes are supported, found OID %s",
|
|
+ algo_oid);
|
|
+ goto cleanup_signerInfos;
|
|
+ }
|
|
+
|
|
+ if (!msg->signerInfos[i].hash)
|
|
+ {
|
|
+ err = grub_error (GRUB_ERR_BAD_SIGNATURE,
|
|
+ "Hash algorithm for signer %d (OID %s) not loaded", i, algo_oid);
|
|
+ goto cleanup_signerInfos;
|
|
+ }
|
|
+
|
|
+ si_sig_path = grub_xasprintf ("signerInfos.?%d.signature", i + 1);
|
|
+ if (!si_sig_path)
|
|
+ {
|
|
+ err = grub_error (GRUB_ERR_OUT_OF_MEMORY,
|
|
+ "Could not allocate path for signer %d's signature parsing path", i);
|
|
+ goto cleanup_signerInfos;
|
|
+ }
|
|
+
|
|
+ result_buf = grub_asn1_allocate_and_read (signed_part, si_sig_path,
|
|
+ "signature data", &result_size);
|
|
+ grub_free (si_sig_path);
|
|
+
|
|
+ if (!result_buf)
|
|
+ {
|
|
+ err = grub_errno;
|
|
+ goto cleanup_signerInfos;
|
|
+ }
|
|
+
|
|
+ gcry_err = gcry_mpi_scan (&(msg->signerInfos[i].sig_mpi), GCRYMPI_FMT_USG,
|
|
+ result_buf, result_size, NULL);
|
|
+
|
|
+ grub_free (result_buf);
|
|
+
|
|
+ if (gcry_err != GPG_ERR_NO_ERROR)
|
|
+ {
|
|
+ err = grub_error (GRUB_ERR_BAD_SIGNATURE,
|
|
+ "Error loading signature %d into MPI structure: %d",
|
|
+ i, gcry_err);
|
|
+ goto cleanup_signerInfos;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * use msg->signerInfo_count to track fully populated signerInfos so we
|
|
+ * know how many we need to clean up
|
|
+ */
|
|
+ msg->signerInfo_count++;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Final consistency check of signerInfo.*.digestAlgorithm vs
|
|
+ * digestAlgorithms.*.algorithm. An algorithm must be present in both
|
|
+ * digestAlgorithms and signerInfo or in neither. We have already checked
|
|
+ * for an algorithm in signerInfo that is not in digestAlgorithms, here we
|
|
+ * check for algorithms in digestAlgorithms but not in signerInfos.
|
|
+ */
|
|
+ if (sha512_in_da && !sha512_in_si)
|
|
+ {
|
|
+ err = grub_error (GRUB_ERR_BAD_SIGNATURE,
|
|
+ "SHA-512 specified in DigestAlgorithms but did not "
|
|
+ "appear in SignerInfos");
|
|
+ goto cleanup_signerInfos;
|
|
+ }
|
|
+
|
|
+ if (sha256_in_da && !sha256_in_si)
|
|
+ {
|
|
+ err = grub_error (GRUB_ERR_BAD_SIGNATURE,
|
|
+ "SHA-256 specified in DigestAlgorithms but did not "
|
|
+ "appear in SignerInfos");
|
|
+ goto cleanup_signerInfos;
|
|
+ }
|
|
+
|
|
+ asn1_delete_structure (&signed_part);
|
|
+ return GRUB_ERR_NONE;
|
|
+
|
|
+cleanup_signerInfos:
|
|
+ for (i = 0; i < msg->signerInfo_count; i++)
|
|
+ gcry_mpi_release (msg->signerInfos[i].sig_mpi);
|
|
+ grub_free (msg->signerInfos);
|
|
cleanup_signed_part:
|
|
asn1_delete_structure (&signed_part);
|
|
-
|
|
return err;
|
|
}
|
|
|
|
grub_err_t
|
|
-parse_pkcs7_signedData (void *sigbuf, grub_size_t data_size,
|
|
- struct pkcs7_signedData *msg)
|
|
+parse_pkcs7_signedData (const void *sigbuf, grub_size_t data_size, struct pkcs7_signedData *msg)
|
|
{
|
|
int res;
|
|
asn1_node content_info;
|
|
@@ -225,26 +385,23 @@ parse_pkcs7_signedData (void *sigbuf, grub_size_t data_size,
|
|
int size;
|
|
|
|
if (data_size > GRUB_INT_MAX)
|
|
- return grub_error (GRUB_ERR_OUT_OF_RANGE,
|
|
- "Cannot parse a PKCS#7 message where data size > INT_MAX");
|
|
+ return grub_error (GRUB_ERR_OUT_OF_RANGE, "Cannot parse a PKCS#7 message "
|
|
+ "where data size > INT_MAX");
|
|
size = (int) data_size;
|
|
|
|
- res = asn1_create_element (_gnutls_pkix_asn,
|
|
- "PKIX1.pkcs-7-ContentInfo", &content_info);
|
|
+ res = asn1_create_element (_gnutls_pkix_asn, "PKIX1.pkcs-7-ContentInfo", &content_info);
|
|
if (res != ASN1_SUCCESS)
|
|
- {
|
|
- return grub_error (GRUB_ERR_OUT_OF_MEMORY,
|
|
- "Could not create ASN.1 structure for PKCS#7 data: %s",
|
|
- asn1_strerror (res));
|
|
- }
|
|
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY,
|
|
+ "Could not create ASN.1 structure for PKCS#7 data: %s",
|
|
+ asn1_strerror (res));
|
|
|
|
res = asn1_der_decoding2 (&content_info, sigbuf, &size,
|
|
- ASN1_DECODE_FLAG_STRICT_DER, asn1_error);
|
|
+ ASN1_DECODE_FLAG_STRICT_DER | ASN1_DECODE_FLAG_ALLOW_PADDING,
|
|
+ asn1_error);
|
|
if (res != ASN1_SUCCESS)
|
|
{
|
|
- err =
|
|
- grub_error (GRUB_ERR_BAD_SIGNATURE,
|
|
- "Error decoding PKCS#7 message DER: %s", asn1_error);
|
|
+ err = grub_error (GRUB_ERR_BAD_SIGNATURE,
|
|
+ "Error decoding PKCS#7 message DER: %s", asn1_error);
|
|
goto cleanup;
|
|
}
|
|
|
|
@@ -255,31 +412,24 @@ parse_pkcs7_signedData (void *sigbuf, grub_size_t data_size,
|
|
*
|
|
* ContentType ::= OBJECT IDENTIFIER
|
|
*/
|
|
- res =
|
|
- asn1_read_value (content_info, "contentType", content_oid,
|
|
- &content_oid_size);
|
|
+ res = asn1_read_value (content_info, "contentType", content_oid, &content_oid_size);
|
|
if (res != ASN1_SUCCESS)
|
|
{
|
|
- err =
|
|
- grub_error (GRUB_ERR_BAD_SIGNATURE,
|
|
- "Error reading PKCS#7 content type: %s",
|
|
- asn1_strerror (res));
|
|
+ err = grub_error (GRUB_ERR_BAD_SIGNATURE, "Error reading PKCS#7 content type: %s",
|
|
+ asn1_strerror (res));
|
|
goto cleanup;
|
|
}
|
|
|
|
/* OID for SignedData defined in 5.1 */
|
|
if (grub_strncmp (signedData_oid, content_oid, content_oid_size) != 0)
|
|
{
|
|
- err =
|
|
- grub_error (GRUB_ERR_BAD_SIGNATURE,
|
|
- "Unexpected content type in PKCS#7 message: OID %s",
|
|
- content_oid);
|
|
+ err = grub_error (GRUB_ERR_BAD_SIGNATURE,
|
|
+ "Unexpected content type in PKCS#7 message: OID %s", content_oid);
|
|
goto cleanup;
|
|
}
|
|
|
|
- content =
|
|
- grub_asn1_allocate_and_read (content_info, "content",
|
|
- "PKCS#7 message content", &content_size);
|
|
+ content = grub_asn1_allocate_and_read (content_info, "content",
|
|
+ "PKCS#7 message content", &content_size);
|
|
if (!content)
|
|
{
|
|
err = grub_errno;
|
|
@@ -301,5 +451,10 @@ cleanup:
|
|
void
|
|
pkcs7_signedData_release (struct pkcs7_signedData *msg)
|
|
{
|
|
- gcry_mpi_release (msg->sig_mpi);
|
|
+ grub_ssize_t i;
|
|
+
|
|
+ for (i = 0; i < msg->signerInfo_count; i++)
|
|
+ gcry_mpi_release (msg->signerInfos[i].sig_mpi);
|
|
+
|
|
+ grub_free (msg->signerInfos);
|
|
}
|
|
diff --git a/grub-core/commands/appendedsig/x509.c b/grub-core/commands/appendedsig/x509.c
|
|
index 42ec65c..eb87025 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 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
|
|
@@ -21,6 +22,7 @@
|
|
#include <grub/err.h>
|
|
#include <grub/mm.h>
|
|
#include <grub/crypto.h>
|
|
+#include <grub/misc.h>
|
|
#include <grub/gcrypt/gcrypt.h>
|
|
|
|
#include "appendedsig.h"
|
|
@@ -30,28 +32,30 @@ 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";
|
|
+static const char *rsaEncryption_oid = "1.2.840.113549.1.1.1";
|
|
|
|
/*
|
|
* RFC 5280 Appendix A
|
|
*/
|
|
-const char *commonName_oid = "2.5.4.3";
|
|
+static const char *commonName_oid = "2.5.4.3";
|
|
|
|
/*
|
|
* RFC 5280 4.2.1.3 Key Usage
|
|
*/
|
|
-const char *keyUsage_oid = "2.5.29.15";
|
|
+static const char *keyUsage_oid = "2.5.29.15";
|
|
+
|
|
+static const grub_uint8_t digitalSignatureUsage = 0x80;
|
|
|
|
/*
|
|
* RFC 5280 4.2.1.9 Basic Constraints
|
|
*/
|
|
-const char *basicConstraints_oid = "2.5.29.19";
|
|
+static 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";
|
|
+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
|
|
@@ -66,46 +70,37 @@ const char *codeSigningUsage_oid = "1.3.6.1.5.5.7.3.3";
|
|
* 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, int dersize, struct x509_certificate *certificate)
|
|
{
|
|
int result;
|
|
- asn1_node spk = ASN1_TYPE_EMPTY;
|
|
+ asn1_node spk = NULL;
|
|
grub_uint8_t *m_data, *e_data;
|
|
int 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 (_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);
|
|
+ m_data = grub_asn1_allocate_and_read (spk, "modulus", "RSA modulus", &m_size);
|
|
if (!m_data)
|
|
{
|
|
err = grub_errno;
|
|
goto cleanup;
|
|
}
|
|
|
|
- e_data =
|
|
- grub_asn1_allocate_and_read (spk, "publicExponent", "RSA public exponent",
|
|
- &e_size);
|
|
+ e_data = grub_asn1_allocate_and_read (spk, "publicExponent",
|
|
+ "RSA public exponent", &e_size);
|
|
if (!e_data)
|
|
{
|
|
err = grub_errno;
|
|
@@ -115,30 +110,22 @@ grub_parse_rsa_pubkey (grub_uint8_t * der, int dersize,
|
|
/*
|
|
* convert m, e to mpi
|
|
*
|
|
- * nscanned is not set for FMT_USG, it's only set for FMT_PGP,
|
|
+ * 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;
|
|
}
|
|
|
|
@@ -158,7 +145,6 @@ cleanup:
|
|
return err;
|
|
}
|
|
|
|
-
|
|
/*
|
|
* RFC 5280:
|
|
* SubjectPublicKeyInfo ::= SEQUENCE {
|
|
@@ -170,17 +156,15 @@ cleanup:
|
|
*/
|
|
|
|
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, struct x509_certificate *results)
|
|
{
|
|
int result;
|
|
grub_err_t err;
|
|
const char *algo_name =
|
|
- "tbsCertificate.subjectPublicKeyInfo.algorithm.algorithm";
|
|
+ "tbsCertificate.subjectPublicKeyInfo.algorithm.algorithm";
|
|
const char *params_name =
|
|
- "tbsCertificate.subjectPublicKeyInfo.algorithm.parameters";
|
|
- const char *pk_name =
|
|
- "tbsCertificate.subjectPublicKeyInfo.subjectPublicKey";
|
|
+ "tbsCertificate.subjectPublicKeyInfo.algorithm.parameters";
|
|
+ const char *pk_name = "tbsCertificate.subjectPublicKeyInfo.subjectPublicKey";
|
|
char algo_oid[MAX_OID_LEN];
|
|
int algo_size = sizeof (algo_oid);
|
|
char params_value[2];
|
|
@@ -192,21 +176,14 @@ grub_x509_read_subject_public_key (asn1_node asn,
|
|
/* 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
|
|
@@ -214,17 +191,12 @@ grub_x509_read_subject_public_key (asn1_node asn,
|
|
*/
|
|
result = asn1_read_value (asn, params_name, params_value, ¶ms_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,34 +204,26 @@ 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 */
|
|
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");
|
|
- }
|
|
+ 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");
|
|
+ "Error reading public key data");
|
|
}
|
|
key_size = (key_size + 7) / 8;
|
|
|
|
@@ -271,8 +235,7 @@ grub_x509_read_subject_public_key (asn1_node asn,
|
|
|
|
/* 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, int der_size, char **string, grub_size_t *string_size)
|
|
{
|
|
asn1_node strasn;
|
|
int result;
|
|
@@ -281,51 +244,51 @@ decode_string (char *der, int der_size, char **string,
|
|
int tmp_size = 0;
|
|
grub_err_t err = GRUB_ERR_NONE;
|
|
|
|
- result =
|
|
- asn1_create_element (_gnutls_pkix_asn, "PKIX1.DirectoryString", &strasn);
|
|
+ result = asn1_create_element (_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);
|
|
+ choice = grub_asn1_allocate_and_read (strasn, "", "DirectoryString choice", &choice_size);
|
|
if (!choice)
|
|
{
|
|
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)
|
|
{
|
|
- err =
|
|
- grub_error (GRUB_ERR_BAD_FILE_TYPE,
|
|
- "Error reading size of UTF-8 string: %s",
|
|
- asn1_strerror (result));
|
|
+ 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 UTF-8 string: %s",
|
|
+ asn1_strerror (result));
|
|
+ goto cleanup_choice;
|
|
+ }
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ err = grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "Only UTF-8 and printable DirectoryStrings are supported, got %s",
|
|
+ choice);
|
|
goto cleanup_choice;
|
|
}
|
|
|
|
@@ -335,20 +298,18 @@ decode_string (char *der, int der_size, char **string,
|
|
*string = grub_malloc (tmp_size);
|
|
if (!*string)
|
|
{
|
|
- 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;
|
|
@@ -365,7 +326,7 @@ cleanup:
|
|
* TBSCertificate ::= SEQUENCE {
|
|
* version [0] EXPLICIT Version DEFAULT v1,
|
|
* ...
|
|
- *
|
|
+ *
|
|
* Version ::= INTEGER { v1(0), v2(1), v3(2) }
|
|
*/
|
|
static grub_err_t
|
|
@@ -374,19 +335,18 @@ check_version (asn1_node certificate)
|
|
int rc;
|
|
const char *name = "tbsCertificate.version";
|
|
grub_uint8_t version;
|
|
- int len = 1;
|
|
+ int len = sizeof (version);
|
|
|
|
rc = asn1_read_value (certificate, name, &version, &len);
|
|
|
|
/* require version 3 */
|
|
if (rc != ASN1_SUCCESS || len != 1)
|
|
return grub_error (GRUB_ERR_BAD_FILE_TYPE,
|
|
- "Error reading certificate version");
|
|
+ "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);
|
|
+ return grub_error (GRUB_ERR_BAD_FILE_TYPE, "Invalid x509 certificate version, expected v3 (0x02), got 0x%02x",
|
|
+ version);
|
|
|
|
return GRUB_ERR_NONE;
|
|
}
|
|
@@ -397,8 +357,7 @@ check_version (asn1_node certificate)
|
|
* For simplicity, we extract only the CN.
|
|
*/
|
|
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;
|
|
@@ -415,16 +374,13 @@ read_name (asn1_node asn, const char *name_path, char **name,
|
|
top_path = grub_xasprintf ("%s.rdnSequence", name_path);
|
|
if (!top_path)
|
|
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;
|
|
}
|
|
|
|
@@ -432,85 +388,75 @@ read_name (asn1_node asn, const char *name_path, char **name,
|
|
{
|
|
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;
|
|
- }
|
|
+ {
|
|
+ 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)
|
|
+ {
|
|
+ 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)
|
|
+ {
|
|
+ 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)
|
|
+ {
|
|
+ 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:
|
|
@@ -527,51 +473,44 @@ cleanup:
|
|
}
|
|
|
|
/*
|
|
- * 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, int value_size)
|
|
{
|
|
asn1_node usageasn;
|
|
int result;
|
|
grub_err_t err = GRUB_ERR_NONE;
|
|
grub_uint8_t usage = 0xff;
|
|
- int usage_size = 1;
|
|
+ int usage_size = sizeof (usage_size);
|
|
|
|
- result =
|
|
- asn1_create_element (_gnutls_pkix_asn, "PKIX1.KeyUsage", &usageasn);
|
|
+ result = asn1_create_element (_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;
|
|
}
|
|
|
|
@@ -586,31 +525,25 @@ 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, int value_size)
|
|
{
|
|
asn1_node basicasn;
|
|
int result;
|
|
grub_err_t err = GRUB_ERR_NONE;
|
|
- char cA[6]; /* FALSE or TRUE */
|
|
+ char cA[6]; /* FALSE or TRUE */
|
|
int cA_size = sizeof (cA);
|
|
|
|
- result =
|
|
- asn1_create_element (_gnutls_pkix_asn, "PKIX1.BasicConstraints",
|
|
- &basicasn);
|
|
+ result = asn1_create_element (_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;
|
|
}
|
|
|
|
@@ -623,18 +556,15 @@ verify_basic_constraints (grub_uint8_t * value, int value_size)
|
|
}
|
|
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 */
|
|
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;
|
|
}
|
|
|
|
@@ -644,72 +574,64 @@ cleanup:
|
|
}
|
|
|
|
/*
|
|
+ * 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, int value_size)
|
|
{
|
|
asn1_node extendedasn;
|
|
- int result, count;
|
|
+ int result, count, i = 0;
|
|
grub_err_t err = GRUB_ERR_NONE;
|
|
- char usage[MAX_OID_LEN];
|
|
+ char usage[MAX_OID_LEN], name[3];
|
|
int usage_size = sizeof (usage);
|
|
|
|
- result =
|
|
- asn1_create_element (_gnutls_pkix_asn, "PKIX1.ExtKeyUsageSyntax",
|
|
- &extendedasn);
|
|
+ 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");
|
|
- }
|
|
+ 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)
|
|
- {
|
|
- 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)
|
|
+ for (i = 1; i < count + 1; i++)
|
|
{
|
|
- err =
|
|
- grub_error (GRUB_ERR_BAD_FILE_TYPE,
|
|
- "Unexpected Extended Key Usage OID, got: %s",
|
|
- usage);
|
|
- 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)
|
|
+ goto cleanup;
|
|
}
|
|
|
|
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE,
|
|
+ "Extended Key Usage missing Code Signing usage");
|
|
cleanup:
|
|
asn1_delete_structure (&extendedasn);
|
|
return err;
|
|
@@ -727,10 +649,11 @@ 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)
|
|
@@ -742,116 +665,110 @@ verify_extensions (asn1_node cert)
|
|
char extnID[MAX_OID_LEN];
|
|
int extnID_size;
|
|
grub_err_t err;
|
|
- char critical[6]; /* we get either "TRUE" or "FALSE" */
|
|
+ char critical[6]; /* we get either "TRUE" or "FALSE" */
|
|
int critical_size;
|
|
grub_uint8_t *value;
|
|
int 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)
|
|
+ {
|
|
+ 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)
|
|
+ {
|
|
+ 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);
|
|
+ value_path = grub_xasprintf ("tbsCertificate.extensions.?%d.extnValue", ext);
|
|
+ if (!value_path)
|
|
+ {
|
|
+ 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)
|
|
- {
|
|
- err = grub_errno;
|
|
- goto cleanup_value_path;
|
|
- }
|
|
+ {
|
|
+ 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.
|
|
*/
|
|
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,23 +777,17 @@ 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:
|
|
@@ -887,6 +798,7 @@ cleanup_critical_path:
|
|
grub_free (critical_path);
|
|
cleanup_oid_path:
|
|
grub_free (oid_path);
|
|
+
|
|
return err;
|
|
}
|
|
|
|
@@ -895,8 +807,7 @@ cleanup_oid_path:
|
|
* 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)
|
|
+parse_x509_certificate (const void *data, grub_size_t data_size, struct x509_certificate *results)
|
|
{
|
|
int result = 0;
|
|
asn1_node cert;
|
|
@@ -906,45 +817,38 @@ certificate_import (void *data, grub_size_t data_size,
|
|
|
|
if (data_size > GRUB_INT_MAX)
|
|
return grub_error (GRUB_ERR_OUT_OF_RANGE,
|
|
- "Cannot parse a certificate where data size > INT_MAX");
|
|
+ "Cannot parse a certificate where data size > INT_MAX");
|
|
size = (int) data_size;
|
|
|
|
result = asn1_create_element (_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);
|
|
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);
|
|
+ results->serial = grub_asn1_allocate_and_read (cert, "tbsCertificate.serialNumber",
|
|
+ "certificate serial number", &tmp_size);
|
|
if (!results->serial)
|
|
{
|
|
err = grub_errno;
|
|
@@ -956,7 +860,7 @@ certificate_import (void *data, grub_size_t data_size,
|
|
*/
|
|
results->serial_len = tmp_size;
|
|
|
|
- /*
|
|
+ /*
|
|
* signature AlgorithmIdentifier,
|
|
*
|
|
* We don't load the signature or issuer at the moment,
|
|
@@ -984,12 +888,10 @@ certificate_import (void *data, grub_size_t data_size,
|
|
|
|
/*
|
|
* 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;
|
|
|
|
@@ -1013,8 +915,7 @@ certificate_import (void *data, grub_size_t data_size,
|
|
|
|
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:
|
|
@@ -1025,7 +926,9 @@ certificate_import (void *data, grub_size_t data_size,
|
|
asn1_delete_structure (&cert);
|
|
return GRUB_ERR_NONE;
|
|
|
|
-
|
|
+cleanup_mpis:
|
|
+ gcry_mpi_release (results->mpis[0]);
|
|
+ gcry_mpi_release (results->mpis[1]);
|
|
cleanup_name:
|
|
grub_free (results->subject);
|
|
cleanup_serial:
|