251 lines
9.5 KiB
Diff
251 lines
9.5 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Sudhakar Kuppusamy <sudhakar@linux.ibm.com>
|
|
Date: Thu, 27 Mar 2025 01:02:38 +0530
|
|
Subject: [PATCH] appendedsig: While verifying the kernel, use trusted and
|
|
distrusted lists
|
|
|
|
To verify the kernel's signature: verify the kernel binary against lists of binary hashes
|
|
that are either distrusted or trusted. If it is not list in either trusted or distrusted hashes list
|
|
then the trusted keys from the trusted key list are used to verify the signature.
|
|
|
|
Signed-off-by: Sudhakar Kuppusamy <sudhakar@linux.ibm.com>
|
|
Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>
|
|
Reviewed-by: Avnish Chouhan <avnish@linux.ibm.com>
|
|
---
|
|
grub-core/commands/appendedsig/appendedsig.c | 193 +++++++++++++++++++--------
|
|
1 file changed, 139 insertions(+), 54 deletions(-)
|
|
|
|
diff --git a/grub-core/commands/appendedsig/appendedsig.c b/grub-core/commands/appendedsig/appendedsig.c
|
|
index 994852f..07d6b39 100644
|
|
--- a/grub-core/commands/appendedsig/appendedsig.c
|
|
+++ b/grub-core/commands/appendedsig/appendedsig.c
|
|
@@ -454,6 +454,83 @@ extract_appended_signature (const grub_uint8_t *buf, grub_size_t bufsize,
|
|
return parse_pkcs7_signedData (appsigdata, pkcs7_size, &sig->pkcs7);
|
|
}
|
|
|
|
+static grub_err_t
|
|
+get_binary_hash (const grub_size_t binary_hash_size, const grub_uint8_t *data,
|
|
+ const grub_size_t data_size, grub_uint8_t *hash, grub_size_t *hash_size)
|
|
+{
|
|
+ grub_uuid_t guid = { 0 };
|
|
+
|
|
+ /* support SHA256, SHA384 and SHA512 for binary hash */
|
|
+ if (binary_hash_size == 32)
|
|
+ grub_memcpy (&guid, &GRUB_PKS_CERT_SHA256_GUID, GRUB_UUID_SIZE);
|
|
+ else if (binary_hash_size == 48)
|
|
+ grub_memcpy (&guid, &GRUB_PKS_CERT_SHA384_GUID, GRUB_UUID_SIZE);
|
|
+ else if (binary_hash_size == 64)
|
|
+ grub_memcpy (&guid, &GRUB_PKS_CERT_SHA512_GUID, GRUB_UUID_SIZE);
|
|
+ else
|
|
+ {
|
|
+ grub_dprintf ("appendedsig", "unsupported hash type (%" PRIuGRUB_SIZE ") and skipping binary hash\n",
|
|
+ binary_hash_size);
|
|
+ return GRUB_ERR_UNKNOWN_COMMAND;
|
|
+ }
|
|
+
|
|
+ return get_hash (&guid, data, data_size, hash, hash_size);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Verify binary hash against the list of binary hashes that are distrusted
|
|
+ * and trusted.
|
|
+ * The following errors can occur:
|
|
+ * - GRUB_ERR_BAD_SIGNATURE: indicates that the hash is distrusted.
|
|
+ * - GRUB_ERR_NONE: the hash is trusted, since it was found in the trusted hashes list
|
|
+ * - GRUB_ERR_EOF: the hash could not be found in the hashes list
|
|
+ */
|
|
+static grub_err_t
|
|
+verify_binary_hash (const grub_uint8_t *data, const grub_size_t data_size)
|
|
+{
|
|
+ grub_err_t rc = GRUB_ERR_NONE;
|
|
+ grub_size_t i = 0, hash_size = 0;
|
|
+ grub_uint8_t hash[GRUB_MAX_HASH_SIZE] = { 0 };
|
|
+
|
|
+ for (i = 0; i < dbx.signature_entries; i++)
|
|
+ {
|
|
+ rc = get_binary_hash (dbx.signature_size[i], data, data_size, hash, &hash_size);
|
|
+ if (rc != GRUB_ERR_NONE)
|
|
+ continue;
|
|
+
|
|
+ if (hash_size == dbx.signature_size[i] &&
|
|
+ grub_memcmp (dbx.signatures[i], hash, hash_size) == 0)
|
|
+ {
|
|
+ grub_dprintf ("appendedsig", "the binary hash (%02x%02x%02x%02x) was listed as distrusted\n",
|
|
+ hash[0], hash[1], hash[2], hash[3]);
|
|
+ return GRUB_ERR_BAD_SIGNATURE;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < db.signature_entries; i++)
|
|
+ {
|
|
+ rc = get_binary_hash (db.signature_size[i], data, data_size, hash, &hash_size);
|
|
+ if (rc != GRUB_ERR_NONE)
|
|
+ continue;
|
|
+
|
|
+ if (hash_size == db.signature_size[i] &&
|
|
+ grub_memcmp (db.signatures[i], hash, hash_size) == 0)
|
|
+ {
|
|
+ grub_dprintf ("appendedsig", "verified with a trusted binary hash (%02x%02x%02x%02x)\n",
|
|
+ hash[0], hash[1], hash[2], hash[3]);
|
|
+ return GRUB_ERR_NONE;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return GRUB_ERR_EOF;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+ * Verify the kernel's integrity, the trusted key will be used from
|
|
+ * the trusted key list. If it fails, verify it against the list of binary hashes
|
|
+ * that are distrusted and trusted.
|
|
+ */
|
|
static grub_err_t
|
|
grub_verify_appended_signature (const grub_uint8_t *buf, grub_size_t bufsize)
|
|
{
|
|
@@ -463,12 +540,12 @@ grub_verify_appended_signature (const grub_uint8_t *buf, grub_size_t bufsize)
|
|
unsigned char *hash;
|
|
gcry_mpi_t hashmpi;
|
|
gcry_err_code_t rc;
|
|
- struct x509_certificate *pk;
|
|
+ struct x509_certificate *cert;
|
|
struct grub_appended_signature sig;
|
|
struct pkcs7_signerInfo *si;
|
|
int i;
|
|
|
|
- if (!db.key_entries)
|
|
+ if (!db.key_entries && !db.signature_entries)
|
|
return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("No trusted keys to verify against"));
|
|
|
|
err = extract_appended_signature (buf, bufsize, &sig);
|
|
@@ -476,69 +553,77 @@ grub_verify_appended_signature (const grub_uint8_t *buf, grub_size_t bufsize)
|
|
return err;
|
|
|
|
datasize = bufsize - sig.signature_len;
|
|
-
|
|
- for (i = 0; i < sig.pkcs7.signerInfo_count; i++)
|
|
+ err = verify_binary_hash (buf, datasize);
|
|
+ if (err != GRUB_ERR_EOF && err != GRUB_ERR_NONE)
|
|
{
|
|
- /*
|
|
- * 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 = db.keys; pk; pk = pk->next)
|
|
+ err = grub_error (err, N_("failed to verify binary-hash/signature with any trusted binary-hash/key\n"));
|
|
+ pkcs7_signedData_release (&sig.pkcs7);
|
|
+ return err;
|
|
+ }
|
|
+ else if (err == GRUB_ERR_EOF)
|
|
+ {
|
|
+ /* Binary hash was not found in trusted and distrusted list: check signature now */
|
|
+ for (i = 0; i < sig.pkcs7.signerInfo_count; i++)
|
|
{
|
|
- 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;
|
|
- }
|
|
+ /*
|
|
+ * 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(db.keys * pkcs7 signers) approach.
|
|
+ */
|
|
+ si = &sig.pkcs7.signerInfos[i];
|
|
+ context = grub_zalloc (si->hash->contextsize);
|
|
+ if (context == NULL)
|
|
+ return grub_errno;
|
|
|
|
- rc = _gcry_pubkey_spec_rsa.verify (0, hashmpi, &si->sig_mpi, pk->mpis, NULL, NULL);
|
|
- gcry_mpi_release (hashmpi);
|
|
+ si->hash->init (context);
|
|
+ si->hash->write (context, buf, datasize);
|
|
+ si->hash->final (context);
|
|
+ hash = si->hash->read (context);
|
|
|
|
- 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",
|
|
+ "data size %" PRIxGRUB_SIZE ", signer %d hash %02x%02x%02x%02x...\n",
|
|
+ datasize, i, hash[0], hash[1], hash[2], hash[3]);
|
|
|
|
- grub_dprintf ("appendedsig", "verify signer %d with key '%s' failed with %d\n",
|
|
- i, pk->subject, rc);
|
|
- }
|
|
+ err = GRUB_ERR_BAD_SIGNATURE;
|
|
+ for (cert = db.keys; cert; cert = cert->next)
|
|
+ {
|
|
+ rc = grub_crypto_rsa_pad (&hashmpi, hash, si->hash, cert->mpis[0]);
|
|
+ if (rc != 0)
|
|
+ {
|
|
+ err = grub_error (GRUB_ERR_BAD_SIGNATURE,
|
|
+ N_("Error padding hash for RSA verification: %d"), rc);
|
|
+ grub_free (context);
|
|
+ pkcs7_signedData_release (&sig.pkcs7);
|
|
+ return err;
|
|
+ }
|
|
|
|
- grub_free (context);
|
|
+ rc = _gcry_pubkey_spec_rsa.verify (0, hashmpi, &si->sig_mpi, cert->mpis, NULL, NULL);
|
|
+ gcry_mpi_release (hashmpi);
|
|
+ if (rc == 0)
|
|
+ {
|
|
+ grub_dprintf ("appendedsig", "verify signer %d with key '%s' succeeded\n",
|
|
+ i, cert->subject);
|
|
+ err = GRUB_ERR_NONE;
|
|
+ break;
|
|
+ }
|
|
|
|
- if (err == GRUB_ERR_NONE)
|
|
- break;
|
|
+ grub_dprintf ("appendedsig", "verify signer %d with key '%s' failed with %d\n",
|
|
+ i, cert->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"));
|
|
-
|
|
-cleanup:
|
|
pkcs7_signedData_release (&sig.pkcs7);
|
|
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ err = grub_error (err, N_("failed to verify signature with any trusted key\n"));
|
|
+ else
|
|
+ grub_dprintf ("appendedsig", "successfully verified the signature with a trusted key\n");
|
|
+
|
|
return err;
|
|
}
|
|
|