diff --git a/0467-appended-sig-sync-d-with-upstream-code.patch b/0467-appended-sig-sync-d-with-upstream-code.patch new file mode 100644 index 0000000..e27e6c6 --- /dev/null +++ b/0467-appended-sig-sync-d-with-upstream-code.patch @@ -0,0 +1,2720 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Nicolas Frayer +Date: Wed, 21 May 2025 15:43:44 -0400 +Subject: [PATCH] appended/sig: sync'd with upstream code + +Signed-off-by: Nicolas Frayer +--- + 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 + #include + #include ++#include + #include + + #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 + #include + #include +- ++#include + + 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 + #include + #include ++#include + #include + + #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: diff --git a/0468-ieee1275-Platform-Keystore-PKS-Support.patch b/0468-ieee1275-Platform-Keystore-PKS-Support.patch new file mode 100644 index 0000000..feb14aa --- /dev/null +++ b/0468-ieee1275-Platform-Keystore-PKS-Support.patch @@ -0,0 +1,200 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Sudhakar Kuppusamy +Date: Thu, 27 Mar 2025 01:02:35 +0530 +Subject: [PATCH] ieee1275: Platform Keystore (PKS) Support + +enhancing the infrastructure to enable the Platform Keystore (PKS) feature, +which provides access to the SB VERSION, DB, and DBX secure boot variables +from PKS. + +Signed-off-by: Sudhakar Kuppusamy +Reviewed-by: Stefan Berger +Reviewed-by: Avnish Chouhan +--- + grub-core/Makefile.core.def | 1 + + grub-core/kern/powerpc/ieee1275/ieee1275.c | 140 +++++++++++++++++++++++++++++ + include/grub/powerpc/ieee1275/ieee1275.h | 14 +++ + 3 files changed, 155 insertions(+) + create mode 100644 grub-core/kern/powerpc/ieee1275/ieee1275.c + +diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def +index 3740330..05b9c8d 100644 +--- a/grub-core/Makefile.core.def ++++ b/grub-core/Makefile.core.def +@@ -320,6 +320,7 @@ kernel = { + extra_dist = video/sis315_init.c; + mips_loongson = commands/keylayouts.c; + ++ powerpc_ieee1275 = kern/powerpc/ieee1275/ieee1275.c; + powerpc_ieee1275 = kern/powerpc/cache.S; + powerpc_ieee1275 = kern/powerpc/dl.c; + powerpc_ieee1275 = kern/powerpc/compiler-rt.S; +diff --git a/grub-core/kern/powerpc/ieee1275/ieee1275.c b/grub-core/kern/powerpc/ieee1275/ieee1275.c +new file mode 100644 +index 0000000..f685afc +--- /dev/null ++++ b/grub-core/kern/powerpc/ieee1275/ieee1275.c +@@ -0,0 +1,140 @@ ++/* of.c - Access the Open Firmware client interface. */ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2003,2004,2005,2007,2008,2009 Free Software Foundation, Inc. ++ * ++ * GRUB is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GRUB is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with GRUB. If not, see . ++ */ ++#include ++#include ++#include ++ ++#define IEEE1275_CELL_INVALID ((grub_ieee1275_cell_t) - 1) ++ ++int ++grub_ieee1275_test (const char *name, grub_ieee1275_cell_t *missing) ++{ ++ struct test_args ++ { ++ struct grub_ieee1275_common_hdr common; ++ grub_ieee1275_cell_t name; ++ grub_ieee1275_cell_t missing; ++ } args; ++ ++ INIT_IEEE1275_COMMON (&args.common, "test", 1, 1); ++ args.name = (grub_ieee1275_cell_t) name; ++ ++ if (IEEE1275_CALL_ENTRY_FN (&args) == -1) ++ return -1; ++ ++ if (args.missing == IEEE1275_CELL_INVALID) ++ return -1; ++ ++ *missing = args.missing; ++ ++ return 0; ++} ++ ++int ++grub_ieee1275_pks_max_object_size (grub_size_t *result) ++{ ++ struct mos_args ++ { ++ struct grub_ieee1275_common_hdr common; ++ grub_ieee1275_cell_t size; ++ } args; ++ ++ INIT_IEEE1275_COMMON (&args.common, "pks-max-object-size", 0, 1); ++ ++ if (IEEE1275_CALL_ENTRY_FN (&args) == -1) ++ return -1; ++ ++ if (args.size == IEEE1275_CELL_INVALID) ++ return -1; ++ ++ *result = args.size; ++ ++ return 0; ++} ++ ++int ++grub_ieee1275_pks_read_object (grub_uint8_t consumer, grub_uint8_t *label, ++ grub_size_t label_len, grub_uint8_t *buffer, ++ grub_size_t buffer_len, grub_size_t *data_len, ++ grub_uint32_t *policies) ++{ ++ struct pks_read_args ++ { ++ struct grub_ieee1275_common_hdr common; ++ grub_ieee1275_cell_t consumer; ++ grub_ieee1275_cell_t label; ++ grub_ieee1275_cell_t label_len; ++ grub_ieee1275_cell_t buffer; ++ grub_ieee1275_cell_t buffer_len; ++ grub_ieee1275_cell_t data_len; ++ grub_ieee1275_cell_t policies; ++ grub_ieee1275_cell_t rc; ++ } args; ++ ++ INIT_IEEE1275_COMMON (&args.common, "pks-read-object", 5, 3); ++ args.consumer = (grub_ieee1275_cell_t) consumer; ++ args.label = (grub_ieee1275_cell_t) label; ++ args.label_len = (grub_ieee1275_cell_t) label_len; ++ args.buffer = (grub_ieee1275_cell_t) buffer; ++ args.buffer_len = (grub_ieee1275_cell_t) buffer_len; ++ ++ if (IEEE1275_CALL_ENTRY_FN (&args) == -1) ++ return -1; ++ ++ if (args.data_len == IEEE1275_CELL_INVALID) ++ return -1; ++ ++ *data_len = args.data_len; ++ *policies = args.policies; ++ ++ return (int) args.rc; ++} ++ ++int ++grub_ieee1275_pks_read_sbvar (grub_uint8_t sbvarflags, grub_uint8_t sbvartype, ++ grub_uint8_t *buffer, grub_size_t buffer_len, ++ grub_size_t *data_len) ++{ ++ struct pks_read_sbvar_args ++ { ++ struct grub_ieee1275_common_hdr common; ++ grub_ieee1275_cell_t sbvarflags; ++ grub_ieee1275_cell_t sbvartype; ++ grub_ieee1275_cell_t buffer; ++ grub_ieee1275_cell_t buffer_len; ++ grub_ieee1275_cell_t data_len; ++ grub_ieee1275_cell_t rc; ++ } args; ++ ++ INIT_IEEE1275_COMMON (&args.common, "pks-read-sbvar", 4, 2); ++ args.sbvarflags = (grub_ieee1275_cell_t) sbvarflags; ++ args.sbvartype = (grub_ieee1275_cell_t) sbvartype; ++ args.buffer = (grub_ieee1275_cell_t) buffer; ++ args.buffer_len = (grub_ieee1275_cell_t) buffer_len; ++ ++ if (IEEE1275_CALL_ENTRY_FN (&args) == -1) ++ return -1; ++ ++ if (args.data_len == IEEE1275_CELL_INVALID) ++ return -1; ++ ++ *data_len = args.data_len; ++ ++ return (int) args.rc; ++} +diff --git a/include/grub/powerpc/ieee1275/ieee1275.h b/include/grub/powerpc/ieee1275/ieee1275.h +index 3c7683f..45c7b16 100644 +--- a/include/grub/powerpc/ieee1275/ieee1275.h ++++ b/include/grub/powerpc/ieee1275/ieee1275.h +@@ -25,4 +25,18 @@ + #define GRUB_IEEE1275_CELL_SIZEOF 4 + typedef grub_uint32_t grub_ieee1275_cell_t; + ++int EXPORT_FUNC (grub_ieee1275_test) (const char *name, ++ grub_ieee1275_cell_t *missing); ++ ++int grub_ieee1275_pks_max_object_size (grub_size_t *result); ++ ++int grub_ieee1275_pks_read_object (grub_uint8_t consumer, grub_uint8_t *label, ++ grub_size_t label_len, grub_uint8_t *buffer, ++ grub_size_t buffer_len, grub_size_t *data_len, ++ grub_uint32_t *policies); ++ ++int grub_ieee1275_pks_read_sbvar (grub_uint8_t sbvarflags, grub_uint8_t sbvartype, ++ grub_uint8_t *buffer, grub_size_t buffer_len, ++ grub_size_t *data_len); ++ + #endif /* ! GRUB_IEEE1275_MACHINE_HEADER */ diff --git a/0469-ieee1275-Read-the-DB-and-DBX-secure-boot-variables.patch b/0469-ieee1275-Read-the-DB-and-DBX-secure-boot-variables.patch new file mode 100644 index 0000000..c2e62d9 --- /dev/null +++ b/0469-ieee1275-Read-the-DB-and-DBX-secure-boot-variables.patch @@ -0,0 +1,686 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Sudhakar Kuppusamy +Date: Thu, 27 Mar 2025 01:02:36 +0530 +Subject: [PATCH] ieee1275: Read the DB and DBX secure boot variables + +If secure boot is enabled with PKS, it will read secure boot variables +such as db and dbx from PKS and extract ESL's from it. +The ESL's would be saved in the platform keystore buffer, and +the appendedsig (module) would read it later to extract +the certificate's details from ESL. + +In the following scenarios, static key mode will be activated: + 1. When Secure Boot is enabled with static keys + 2. When SB Version is unavailable but Secure Boot is enabled + 3. When PKS support is unavailable but Secure Boot is enabled + +Note:- + +SB Version - Secure Boot mode +1 - PKS +0 - static key (embeded key) + +Signed-off-by: Sudhakar Kuppusamy +Reviewed-by: Stefan Berger +Reviewed-by: Avnish Chouhan +--- + grub-core/Makefile.am | 1 + + grub-core/Makefile.core.def | 1 + + grub-core/kern/ieee1275/init.c | 15 +- + .../kern/powerpc/ieee1275/platform_keystore.c | 332 +++++++++++++++++++++ + include/grub/powerpc/ieee1275/platform_keystore.h | 225 ++++++++++++++ + include/grub/types.h | 8 + + 6 files changed, 580 insertions(+), 2 deletions(-) + create mode 100644 grub-core/kern/powerpc/ieee1275/platform_keystore.c + create mode 100644 include/grub/powerpc/ieee1275/platform_keystore.h + +diff --git a/grub-core/Makefile.am b/grub-core/Makefile.am +index dd49939..a398a04 100644 +--- a/grub-core/Makefile.am ++++ b/grub-core/Makefile.am +@@ -245,6 +245,7 @@ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/ieee1275/ieee1275.h + KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/terminfo.h + KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/extcmd.h + KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/lib/arg.h ++KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/powerpc/ieee1275/platform_keystore.h + endif + + if COND_sparc64_ieee1275 +diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def +index 05b9c8d..218068b 100644 +--- a/grub-core/Makefile.core.def ++++ b/grub-core/Makefile.core.def +@@ -325,6 +325,7 @@ kernel = { + powerpc_ieee1275 = kern/powerpc/dl.c; + powerpc_ieee1275 = kern/powerpc/compiler-rt.S; + powerpc_ieee1275 = kern/lockdown.c; ++ powerpc_ieee1275 = kern/powerpc/ieee1275/platform_keystore.c; + + sparc64_ieee1275 = kern/sparc64/cache.S; + sparc64_ieee1275 = kern/sparc64/dl.c; +diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c +index bb791f8..b853f04 100644 +--- a/grub-core/kern/ieee1275/init.c ++++ b/grub-core/kern/ieee1275/init.c +@@ -47,6 +47,8 @@ + #include + #endif + #include ++#include ++#include + + /* The maximum heap size we're going to claim at boot. Not used by sparc. */ + #ifdef __i386__ +@@ -956,7 +958,7 @@ grub_get_ieee1275_secure_boot (void) + { + grub_ieee1275_phandle_t root; + int rc; +- grub_uint32_t is_sb; ++ grub_uint32_t is_sb = 0; + + grub_ieee1275_finddevice ("/", &root); + +@@ -972,7 +974,16 @@ grub_get_ieee1275_secure_boot (void) + * We only support enforce. + */ + if (rc >= 0 && is_sb >= 2) +- grub_lockdown (); ++ { ++ grub_dprintf ("ieee1275", "Secure Boot Enabled\n"); ++ rc = grub_pks_keystore_init (); ++ if (rc != GRUB_ERR_NONE) ++ grub_error (rc, "Initialization of the Platform Keystore failed!\n"); ++ ++ grub_lockdown (); ++ } ++ else ++ grub_dprintf ("ieee1275", "Secure Boot Disabled\n"); + } + + grub_addr_t grub_modbase; +diff --git a/grub-core/kern/powerpc/ieee1275/platform_keystore.c b/grub-core/kern/powerpc/ieee1275/platform_keystore.c +new file mode 100644 +index 0000000..cf1d055 +--- /dev/null ++++ b/grub-core/kern/powerpc/ieee1275/platform_keystore.c +@@ -0,0 +1,332 @@ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2024 Free Software Foundation, Inc. ++ * Copyright (C) 2024 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 ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GRUB is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with GRUB. If not, see . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define PKS_CONSUMER_FW 1 ++#define SB_VERSION_KEY_NAME ((grub_uint8_t *) "SB_VERSION") ++#define SB_VERSION_KEY_LEN 10 ++#define DB 1 ++#define DBX 2 ++#define PKS_OBJECT_NOT_FOUND ((grub_err_t) - 7) ++ ++/* Platform Keystore */ ++static grub_size_t pks_max_object_size; ++grub_uint8_t grub_pks_use_keystore = 0; ++grub_pks_t grub_pks_keystore = { .db = NULL, .dbx = NULL, .db_entries = 0, .dbx_entries = 0 }; ++ ++/* Convert the esl data into the ESL */ ++static grub_esl_t * ++convert_to_esl (const grub_uint8_t *esl_data, const grub_size_t esl_data_size) ++{ ++ grub_esl_t *esl = NULL; ++ ++ if (esl_data_size < sizeof (grub_esl_t) || esl_data == NULL) ++ return esl; ++ ++ esl = (grub_esl_t *) esl_data; ++ ++ return esl; ++} ++ ++/* ++ * Import the GUID, esd, and its size into the pks sd buffer and ++ * pks sd entries from the EFI signature list. ++ */ ++static grub_err_t ++esd_from_esl (const grub_uint8_t *esl_data, grub_size_t esl_size, ++ const grub_size_t signature_size, const grub_uuid_t *guid, ++ grub_pks_sd_t **pks_sd, grub_size_t *pks_sd_entries) ++{ ++ grub_esd_t *esd = NULL; ++ grub_pks_sd_t *signature = *pks_sd; ++ grub_size_t entries = *pks_sd_entries; ++ grub_size_t data_size = 0, offset = 0; ++ ++ /* reads the esd from esl */ ++ while (esl_size > 0) ++ { ++ esd = (grub_esd_t *) (esl_data + offset); ++ data_size = signature_size - sizeof (grub_esd_t); ++ ++ signature = grub_realloc (signature, (entries + 1) * sizeof (grub_pks_sd_t)); ++ if (signature == NULL) ++ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory"); ++ ++ signature[entries].data = grub_malloc (data_size * sizeof (grub_uint8_t)); ++ if (signature[entries].data == NULL) ++ { ++ /* ++ * allocated memory will be freed by ++ * grub_free_platform_keystore ++ */ ++ *pks_sd = signature; ++ *pks_sd_entries = entries + 1; ++ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory"); ++ } ++ ++ grub_memcpy (signature[entries].data, esd->signaturedata, data_size); ++ signature[entries].data_size = data_size; ++ signature[entries].guid = *guid; ++ entries++; ++ esl_size -= signature_size; ++ offset += signature_size; ++ } ++ ++ *pks_sd = signature; ++ *pks_sd_entries = entries; ++ ++ return GRUB_ERR_NONE; ++} ++ ++/* ++ * Extract the esd after removing the esl header from esl. ++ */ ++static grub_err_t ++esl_to_esd (const grub_uint8_t *esl_data, grub_size_t *next_esl, ++ grub_pks_sd_t **pks_sd, grub_size_t *pks_sd_entries) ++{ ++ grub_uuid_t guid = { 0 }; ++ grub_esl_t *esl = NULL; ++ grub_size_t offset = 0, esl_size = 0, ++ signature_size = 0, signature_header_size = 0; ++ ++ esl = convert_to_esl (esl_data, *next_esl); ++ if (esl == NULL) ++ return grub_error (GRUB_ERR_BUG, "invalid ESL"); ++ ++ esl_size = grub_le_to_cpu32 (esl->signaturelistsize); ++ signature_header_size = grub_le_to_cpu32 (esl->signatureheadersize); ++ signature_size = grub_le_to_cpu32 (esl->signaturesize); ++ guid = esl->signaturetype; ++ ++ if (esl_size < sizeof (grub_esl_t) || esl_size > *next_esl) ++ return grub_error (GRUB_ERR_BUG, "invalid ESL size (%u)\n", esl_size); ++ ++ *next_esl = esl_size; ++ offset = sizeof (grub_esl_t) + signature_header_size; ++ esl_size = esl_size - offset; ++ ++ return esd_from_esl (esl_data + offset, esl_size, signature_size, &guid, ++ pks_sd, pks_sd_entries); ++} ++ ++/* ++ * Import the EFI signature data and the number of esd from the esl ++ * into the pks sd buffer and pks sd entries. ++ */ ++static grub_err_t ++pks_sd_from_esl (const grub_uint8_t *esl_data, grub_size_t esl_size, ++ grub_pks_sd_t **pks_sd, grub_size_t *pks_sd_entries) ++{ ++ grub_err_t rc = GRUB_ERR_NONE; ++ grub_size_t next_esl = esl_size; ++ ++ do ++ { ++ rc = esl_to_esd (esl_data, &next_esl, pks_sd, pks_sd_entries); ++ if (rc != GRUB_ERR_NONE) ++ break; ++ ++ esl_data += next_esl; ++ esl_size -= next_esl; ++ next_esl = esl_size; ++ } ++ while (esl_size > 0); ++ ++ return rc; ++} ++ ++/* ++ * Read the secure boot version from PKS as an object. ++ * caller must free result ++ */ ++static grub_err_t ++read_sbversion_from_pks (grub_uint8_t **out, grub_size_t *outlen, grub_size_t *policy) ++{ ++ *out = grub_malloc (pks_max_object_size); ++ if (*out == NULL) ++ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory"); ++ ++ return grub_ieee1275_pks_read_object (PKS_CONSUMER_FW, SB_VERSION_KEY_NAME, ++ SB_VERSION_KEY_LEN, *out, pks_max_object_size, ++ outlen, policy); ++} ++ ++/* ++ * reads the secure boot variable from PKS. ++ * caller must free result ++ */ ++static grub_err_t ++read_sbvar_from_pks (const grub_uint8_t sbvarflags, const grub_uint8_t sbvartype, ++ grub_uint8_t **out, grub_size_t *outlen) ++{ ++ *out = grub_malloc (pks_max_object_size); ++ if (*out == NULL) ++ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory"); ++ ++ return grub_ieee1275_pks_read_sbvar (sbvarflags, sbvartype, *out, ++ pks_max_object_size, outlen); ++} ++ ++/* Test the availability of PKS support. */ ++static grub_err_t ++is_support_pks (void) ++{ ++ grub_err_t rc = GRUB_ERR_NONE; ++ grub_ieee1275_cell_t missing = 0; ++ ++ rc = grub_ieee1275_test ("pks-max-object-size", &missing); ++ if (rc != GRUB_ERR_NONE || (int) missing == -1) ++ rc = grub_error (GRUB_ERR_BAD_FIRMWARE, "Firmware doesn't have PKS support!\n"); ++ else ++ { ++ rc = grub_ieee1275_pks_max_object_size (&pks_max_object_size); ++ if (rc != GRUB_ERR_NONE) ++ rc = grub_error (GRUB_ERR_BAD_NUMBER, "PKS support is there but it has zero objects!\n"); ++ } ++ ++ return rc; ++} ++ ++/* ++ * Retrieve the secure boot variable from PKS, unpacks it, read the esd ++ * from ESL, and store the information in the pks sd buffer. ++ */ ++static grub_err_t ++read_secure_boot_variables (const grub_uint8_t sbvarflags, const grub_uint8_t sbvartype, ++ grub_pks_sd_t **pks_sd, grub_size_t *pks_sd_entries) ++{ ++ grub_err_t rc = GRUB_ERR_NONE; ++ grub_uint8_t *esl_data = NULL; ++ grub_size_t esl_data_size = 0; ++ ++ rc = read_sbvar_from_pks (sbvarflags, sbvartype, &esl_data, &esl_data_size); ++ /* ++ * at this point we have SB_VERSION, so any error is worth ++ * at least some user-visible info ++ */ ++ if (rc != GRUB_ERR_NONE) ++ rc = grub_error (rc, "secure boot variable %s reading (%d)", ++ (sbvartype == DB ? "db" : "dbx"), rc); ++ else if (esl_data_size != 0) ++ rc = pks_sd_from_esl ((const grub_uint8_t *) esl_data, esl_data_size, ++ pks_sd, pks_sd_entries); ++ grub_free (esl_data); ++ ++ return rc; ++} ++ ++/* reads secure boot version (SB_VERSION) and it supports following ++ * SB_VERSION ++ * 1 - PKS ++ * 0 - static key (embeded key) ++ */ ++static grub_err_t ++get_secure_boot_version (void) ++{ ++ grub_err_t rc = GRUB_ERR_NONE; ++ grub_uint8_t *data = NULL; ++ grub_size_t len = 0, policy = 0; ++ ++ rc = read_sbversion_from_pks (&data, &len, &policy); ++ if (rc != GRUB_ERR_NONE) ++ rc = grub_error (GRUB_ERR_READ_ERROR, "SB version read failed! (%d)\n", rc); ++ else if (len != 1 || (*data >= 2)) ++ rc = grub_error (GRUB_ERR_BAD_NUMBER, "found unexpected SB version! (%d)\n", *data); ++ ++ if (rc != GRUB_ERR_NONE) ++ { ++ grub_printf ("Switch to Static Key!\n"); ++ if (grub_is_lockdown () == GRUB_LOCKDOWN_ENABLED) ++ grub_fatal ("Secure Boot locked down"); ++ } ++ else ++ grub_pks_use_keystore = *data; ++ ++ grub_free (data); ++ ++ return rc; ++} ++ ++/* Free allocated memory */ ++void ++grub_pks_free_keystore (void) ++{ ++ grub_size_t i = 0; ++ ++ for (i = 0; i < grub_pks_keystore.db_entries; i++) ++ grub_free (grub_pks_keystore.db[i].data); ++ ++ for (i = 0; i < grub_pks_keystore.dbx_entries; i++) ++ grub_free (grub_pks_keystore.dbx[i].data); ++ ++ grub_free (grub_pks_keystore.db); ++ grub_free (grub_pks_keystore.dbx); ++ grub_memset (&grub_pks_keystore, 0, sizeof (grub_pks_t)); ++} ++ ++/* Initialization of the Platform Keystore */ ++grub_err_t ++grub_pks_keystore_init (void) ++{ ++ grub_err_t rc = GRUB_ERR_NONE; ++ ++ grub_dprintf ("ieee1275", "trying to load Platform Keystore\n"); ++ ++ rc = is_support_pks (); ++ if (rc != GRUB_ERR_NONE) ++ { ++ grub_printf ("Switch to Static Key!\n"); ++ return rc; ++ } ++ ++ /* SB_VERSION */ ++ rc = get_secure_boot_version (); ++ if (rc != GRUB_ERR_NONE) ++ return rc; ++ ++ if (grub_pks_use_keystore) ++ { ++ grub_memset (&grub_pks_keystore, 0, sizeof (grub_pks_t)); ++ /* DB */ ++ rc = read_secure_boot_variables (0, DB, &grub_pks_keystore.db, &grub_pks_keystore.db_entries); ++ if (rc == GRUB_ERR_NONE) ++ { ++ /* DBX */ ++ rc = read_secure_boot_variables (0, DBX, &grub_pks_keystore.dbx, &grub_pks_keystore.dbx_entries); ++ if (rc == PKS_OBJECT_NOT_FOUND) ++ { ++ grub_dprintf ("ieee1275", "dbx is not found in PKS\n"); ++ rc = GRUB_ERR_NONE; ++ } ++ } ++ ++ } ++ ++ if (rc != GRUB_ERR_NONE) ++ grub_pks_free_keystore (); ++ ++ return rc; ++} +diff --git a/include/grub/powerpc/ieee1275/platform_keystore.h b/include/grub/powerpc/ieee1275/platform_keystore.h +new file mode 100644 +index 0000000..0641adb +--- /dev/null ++++ b/include/grub/powerpc/ieee1275/platform_keystore.h +@@ -0,0 +1,225 @@ ++/* ++ * Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved. This ++ * program and the accompanying materials are licensed and made available ++ * under the terms and conditions of the 2-Clause BSD License which ++ * accompanies this distribution. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are met: ++ * ++ * 1. Redistributions of source code must retain the above copyright notice, ++ * this list of conditions and the following disclaimer. ++ * ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE ++ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++ * POSSIBILITY OF SUCH DAMAGE. ++ * ++ * ++ * https://github.com/tianocore/edk2-staging (edk2-staging repo of tianocore), ++ * the ImageAuthentication.h file under it, and here's the copyright and license. ++ * ++ * MdePkg/Include/Guid/ImageAuthentication.h ++ * ++ * Copyright 2024 IBM Corp. ++ */ ++ ++#ifndef __PLATFORM_KEYSTORE_H__ ++#define __PLATFORM_KEYSTORE_H__ ++ ++#include ++#include ++#include ++ ++#if __GNUC__ >= 9 ++#pragma GCC diagnostic ignored "-Waddress-of-packed-member" ++#endif ++ ++#define GRUB_MAX_HASH_SIZE 64 ++ ++typedef struct grub_esd grub_esd_t; ++typedef struct grub_esl grub_esl_t; ++ ++/* ++ * It is derived from EFI_SIGNATURE_DATA ++ * https://github.com/tianocore/edk2-staging/blob/master/MdePkg/Include/Guid/ImageAuthentication.h ++ * ++ * The structure of an EFI signature database (ESD).*/ ++struct grub_esd ++{ ++ /* ++ * An identifier which identifies the agent which added ++ * the signature to the list. ++ */ ++ grub_uuid_t signatureowner; ++ /* The format of the signature is defined by the SignatureType.*/ ++ grub_uint8_t signaturedata[]; ++} GRUB_PACKED; ++ ++/* ++ * It is derived from EFI_SIGNATURE_LIST ++ * https://github.com/tianocore/edk2-staging/blob/master/MdePkg/Include/Guid/ImageAuthentication.h ++ * ++ * The structure of an EFI signature list (ESL).*/ ++struct grub_esl ++{ ++ /* Type of the signature. GUID signature types are defined in below.*/ ++ grub_uuid_t signaturetype; ++ /* Total size of the signature list, including this header.*/ ++ grub_uint32_t signaturelistsize; ++ /* ++ * Size of the signature header which precedes ++ * the array of signatures. ++ */ ++ grub_uint32_t signatureheadersize; ++ /* Size of each signature.*/ ++ grub_uint32_t signaturesize; ++} GRUB_PACKED; ++ ++/* ++ * It is derived from EFI_CERT_X509_GUID ++ * https://github.com/tianocore/edk2-staging/blob/master/MdePkg/Include/Guid/ImageAuthentication.h ++ */ ++#define GRUB_PKS_CERT_X509_GUID \ ++ (grub_uuid_t) \ ++ { \ ++ { \ ++ 0xa1, 0x59, 0xc0, 0xa5, 0xe4, 0x94, \ ++ 0xa7, 0x4a, 0x87, 0xb5, 0xab, 0x15, \ ++ 0x5c, 0x2b, 0xf0, 0x72 \ ++ } \ ++ } ++ ++/* ++ * It is derived from EFI_CERT_SHA256_GUID ++ * https://github.com/tianocore/edk2-staging/blob/master/MdePkg/Include/Guid/ImageAuthentication.h ++ */ ++#define GRUB_PKS_CERT_SHA256_GUID \ ++ (grub_uuid_t) \ ++ { \ ++ { \ ++ 0x26, 0x16, 0xc4, 0xc1, 0x4c, 0x50, \ ++ 0x92, 0x40, 0xac, 0xa9, 0x41, 0xf9, \ ++ 0x36, 0x93, 0x43, 0x28 \ ++ } \ ++ } ++ ++/* ++ * It is derived from EFI_CERT_SHA384_GUID ++ * https://github.com/tianocore/edk2-staging/blob/master/MdePkg/Include/Guid/ImageAuthentication.h ++ */ ++#define GRUB_PKS_CERT_SHA384_GUID \ ++ (grub_uuid_t) \ ++ { \ ++ { \ ++ 0x07, 0x53, 0x3e, 0xff, 0xd0, 0x9f, \ ++ 0xc9, 0x48, 0x85, 0xf1, 0x8a, 0xd5, \ ++ 0x6c, 0x70, 0x1e, 0x1 \ ++ } \ ++ } ++ ++/* ++ * It is derived from EFI_CERT_SHA512_GUID ++ * https://github.com/tianocore/edk2-staging/blob/master/MdePkg/Include/Guid/ImageAuthentication.h ++ */ ++#define GRUB_PKS_CERT_SHA512_GUID \ ++ (grub_uuid_t) \ ++ { \ ++ { \ ++ 0xae, 0x0f, 0x3e, 0x09, 0xc4, 0xa6, \ ++ 0x50, 0x4f, 0x9f, 0x1b, 0xd4, 0x1e, \ ++ 0x2b, 0x89, 0xc1, 0x9a \ ++ } \ ++ } ++ ++/* ++ * It is derived from EFI_CERT_X509_SHA256_GUID ++ * https://github.com/tianocore/edk2-staging/blob/master/MdePkg/Include/Guid/ImageAuthentication.h ++ */ ++#define GRUB_PKS_CERT_X509_SHA256_GUID \ ++ (grub_uuid_t) \ ++ { \ ++ { \ ++ 0x92, 0xa4, 0xd2, 0x3b, 0xc0, 0x96, \ ++ 0x79, 0x40, 0xb4, 0x20, 0xfc, 0xf9, \ ++ 0x8e, 0xf1, 0x03, 0xed \ ++ } \ ++ } ++ ++/* ++ * It is derived from EFI_CERT_X509_SHA384_GUID ++ * https://github.com/tianocore/edk2-staging/blob/master/MdePkg/Include/Guid/ImageAuthentication.h ++ */ ++#define GRUB_PKS_CERT_X509_SHA384_GUID \ ++ (grub_uuid_t) \ ++ { \ ++ { \ ++ 0x6e, 0x87, 0x76, 0x70, 0xc2, 0x80, \ ++ 0xe6, 0x4e, 0xaa, 0xd2, 0x28, 0xb3, \ ++ 0x49, 0xa6, 0x86, 0x5b \ ++ } \ ++ } ++ ++/* ++ * It is derived from EFI_CERT_X509_SHA512_GUID ++ * https://github.com/tianocore/edk2-staging/blob/master/MdePkg/Include/Guid/ImageAuthentication.h ++ */ ++#define GRUB_PKS_CERT_X509_SHA512_GUID \ ++ (grub_uuid_t) \ ++ { \ ++ { \ ++ 0x63, 0xbf, 0x6d, 0x44, 0x02, 0x25, \ ++ 0xda, 0x4c, 0xbc, 0xfa, 0x24, 0x65, \ ++ 0xd2, 0xb0, 0xfe, 0x9d \ ++ } \ ++ } ++ ++typedef struct grub_pks_sd grub_pks_sd_t; ++typedef struct grub_pks grub_pks_t; ++ ++/* The structure of a PKS signature data.*/ ++struct grub_pks_sd ++{ ++ grub_uuid_t guid; /* signature type */ ++ grub_uint8_t *data; /* signature data */ ++ grub_size_t data_size; /* size of signature data */ ++} GRUB_PACKED; ++ ++/* The structure of a PKS.*/ ++struct grub_pks ++{ ++ grub_pks_sd_t *db; /* signature database */ ++ grub_pks_sd_t *dbx; /* forbidden signature database */ ++ grub_size_t db_entries; /* size of signature database */ ++ grub_size_t dbx_entries; /* size of forbidden signature database */ ++} GRUB_PACKED; ++ ++#ifdef __powerpc__ ++ ++/* Initialization of the Platform Keystore */ ++grub_err_t grub_pks_keystore_init (void); ++/* Free allocated memory */ ++void EXPORT_FUNC(grub_pks_free_keystore) (void); ++extern grub_uint8_t EXPORT_VAR(grub_pks_use_keystore); ++extern grub_pks_t EXPORT_VAR(grub_pks_keystore); ++ ++#else ++ ++#define grub_pks_use_keystore 0 ++grub_pks_t grub_pks_keystore = {NULL, NULL, 0, 0}; ++void grub_pks_free_keystore (void); ++ ++#endif ++ ++#endif +diff --git a/include/grub/types.h b/include/grub/types.h +index 1587ff4..bfa7af9 100644 +--- a/include/grub/types.h ++++ b/include/grub/types.h +@@ -345,4 +345,12 @@ static inline void grub_set_unaligned64 (void *ptr, grub_uint64_t val) + dd->d = val; + } + ++#define GRUB_UUID_SIZE 16 ++typedef struct grub_uuid grub_uuid_t; ++/* The structure of a UUID.*/ ++struct grub_uuid ++{ ++ grub_uint8_t b[GRUB_UUID_SIZE]; ++}; ++ + #endif /* ! GRUB_TYPES_HEADER */ diff --git a/0470-appendedsig-The-creation-of-trusted-and-distrusted-l.patch b/0470-appendedsig-The-creation-of-trusted-and-distrusted-l.patch new file mode 100644 index 0000000..16786a8 --- /dev/null +++ b/0470-appendedsig-The-creation-of-trusted-and-distrusted-l.patch @@ -0,0 +1,764 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Sudhakar Kuppusamy +Date: Thu, 27 Mar 2025 01:02:37 +0530 +Subject: [PATCH] appendedsig: The creation of trusted and distrusted lists + +The trusted certificates and binary hashes, distrusted certificates and +binary/certificate hashes will be extracted from the platform keystore buffer +if Secure Boot is enabled with PKS. + +In order to verify the integrity of the kernel, the extracted data +needs to be stored stored in the buffer db and dbx. + +The trusted certificates will be extracted from the grub ELFNOTE if Secure Boot is +enabled with static key. In order to verify the integerity of the kernel, +the extracted data needs to be stored in the buffer db. + +Note:- + +If neither the trusted certificate nor binary hash exists in the distrusted list (dbx), +rejects it while extracting certificate/binary hash from the platform keystore buffer. + +Signed-off-by: Sudhakar Kuppusamy +Reviewed-by: Stefan Berger +Reviewed-by: Avnish Chouhan +--- + grub-core/commands/appendedsig/appendedsig.c | 584 +++++++++++++++++++++++++-- + grub-core/kern/file.c | 34 ++ + include/grub/file.h | 1 + + 3 files changed, 577 insertions(+), 42 deletions(-) + +diff --git a/grub-core/commands/appendedsig/appendedsig.c b/grub-core/commands/appendedsig/appendedsig.c +index f4eefe5..994852f 100644 +--- a/grub-core/commands/appendedsig/appendedsig.c ++++ b/grub-core/commands/appendedsig/appendedsig.c +@@ -34,7 +34,7 @@ + #include + #include + #include +- ++#include + #include "appendedsig.h" + + GRUB_MOD_LICENSE ("GPLv3+"); +@@ -64,8 +64,23 @@ struct grub_appended_signature + struct pkcs7_signedData pkcs7; /* Parsed PKCS#7 data */ + }; + +-/* Trusted certificates for verifying appended signatures */ +-struct x509_certificate *grub_trusted_key; ++/* This represents a trusted/distrusted list*/ ++struct grub_database ++{ ++ struct x509_certificate *keys; /* Certificates */ ++ grub_size_t key_entries; /* Number of certificates */ ++ grub_uint8_t **signatures; /* Certificate/binary hashes */ ++ grub_size_t *signature_size; /* Size of certificate/binary hashes */ ++ grub_size_t signature_entries; /* Number of certificate/binary hashes */ ++}; ++ ++/* Trusted list */ ++struct grub_database db = {.keys = NULL, .key_entries = 0, .signatures = NULL, ++ .signature_size = NULL, .signature_entries = 0}; ++ ++/* Distrusted list */ ++struct grub_database dbx = {.signatures = NULL, .signature_size = NULL, ++ .signature_entries = 0}; + + /* + * Force gcry_rsa to be a module dependency. +@@ -87,6 +102,13 @@ struct x509_certificate *grub_trusted_key; + * also resolves our concerns about loading from the filesystem. + */ + extern gcry_pk_spec_t _gcry_pubkey_spec_rsa; ++extern gcry_md_spec_t _gcry_digest_spec_sha224; ++extern gcry_md_spec_t _gcry_digest_spec_sha384; ++ ++/* Free trusted list memory */ ++static void free_trusted_list (void); ++/* Free distrusted list memory */ ++static void free_distrusted_list (void); + + static enum + { +@@ -95,6 +117,204 @@ static enum + check_sigs_forced = 2 + } check_sigs = check_sigs_no; + ++/* ++ * GUID can be used to determine the hashing function and ++ * generate the hash using determined hashing function. ++ */ ++static grub_err_t ++get_hash (const grub_uuid_t *guid, const grub_uint8_t *data, const grub_size_t data_size, ++ grub_uint8_t *hash, grub_size_t *hash_size) ++{ ++ gcry_md_spec_t *hash_func = NULL; ++ ++ if (guid == NULL) ++ return grub_error (GRUB_ERR_OUT_OF_RANGE, "GUID is null"); ++ ++ if (grub_memcmp (guid, &GRUB_PKS_CERT_SHA256_GUID, GRUB_UUID_SIZE) == 0 || ++ grub_memcmp (guid, &GRUB_PKS_CERT_X509_SHA256_GUID, GRUB_UUID_SIZE) == 0) ++ hash_func = &_gcry_digest_spec_sha256; ++ else if (grub_memcmp (guid, &GRUB_PKS_CERT_SHA384_GUID, GRUB_UUID_SIZE) == 0 || ++ grub_memcmp (guid, &GRUB_PKS_CERT_X509_SHA384_GUID, GRUB_UUID_SIZE) == 0) ++ hash_func = &_gcry_digest_spec_sha384; ++ else if (grub_memcmp (guid, &GRUB_PKS_CERT_SHA512_GUID, GRUB_UUID_SIZE) == 0 || ++ grub_memcmp (guid, &GRUB_PKS_CERT_X509_SHA512_GUID, GRUB_UUID_SIZE) == 0) ++ hash_func = &_gcry_digest_spec_sha512; ++ else ++ return grub_error (GRUB_ERR_OUT_OF_RANGE, "Unsupported GUID for hash"); ++ ++ grub_memset (hash, 0, GRUB_MAX_HASH_SIZE); ++ grub_crypto_hash (hash_func, hash, data, data_size); ++ *hash_size = hash_func->mdlen; ++ ++ return GRUB_ERR_NONE; ++} ++ ++/* Add the certificate/binary hash into the trusted/distrusted list */ ++static grub_err_t ++add_hash (const grub_uint8_t **data, const grub_size_t data_size, ++ grub_uint8_t ***signature_list, grub_size_t **signature_size_list, ++ grub_size_t *signature_list_entries) ++{ ++ grub_uint8_t **signatures = *signature_list; ++ grub_size_t *signature_size = *signature_size_list; ++ grub_size_t signature_entries = *signature_list_entries; ++ ++ if (*data == NULL || data_size == 0) ++ return grub_error (GRUB_ERR_OUT_OF_RANGE, "certificate/binary hash data/size is null"); ++ ++ signatures = grub_realloc (signatures, sizeof (grub_uint8_t *) * (signature_entries + 1)); ++ signature_size = grub_realloc (signature_size, ++ sizeof (grub_size_t) * (signature_entries + 1)); ++ ++ if (signatures == NULL || signature_size == NULL) ++ { ++ /* ++ * allocated memory will be freed by ++ * free_trusted_list/free_distrusted_list ++ */ ++ if (signatures != NULL) ++ { ++ *signature_list = signatures; ++ *signature_list_entries = signature_entries + 1; ++ } ++ ++ if (signature_size != NULL) ++ *signature_size_list = signature_size; ++ ++ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory"); ++ } ++ ++ signatures[signature_entries] = (grub_uint8_t *) *data; ++ signature_size[signature_entries] = data_size; ++ signature_entries++; ++ *data = NULL; ++ ++ *signature_list = signatures; ++ *signature_size_list = signature_size; ++ *signature_list_entries = signature_entries; ++ ++ return GRUB_ERR_NONE; ++} ++ ++static int ++is_x509 (const grub_uuid_t *guid) ++{ ++ if (grub_memcmp (guid, &GRUB_PKS_CERT_X509_GUID, GRUB_UUID_SIZE) == 0) ++ return GRUB_ERR_NONE; ++ ++ return GRUB_ERR_UNKNOWN_COMMAND; ++} ++ ++static int ++is_cert_match (const struct x509_certificate *distrusted_cert, ++ const struct x509_certificate *db_cert) ++{ ++ ++ if (grub_memcmp (distrusted_cert->subject, db_cert->subject, db_cert->subject_len) == 0 ++ && grub_memcmp (distrusted_cert->serial, db_cert->serial, db_cert->serial_len) == 0 ++ && grub_memcmp (distrusted_cert->mpis[0], db_cert->mpis[0], sizeof (db_cert->mpis[0])) == 0 ++ && grub_memcmp (distrusted_cert->mpis[1], db_cert->mpis[1], sizeof (db_cert->mpis[1])) == 0) ++ return GRUB_ERR_NONE; ++ ++ return GRUB_ERR_UNKNOWN_COMMAND; ++} ++ ++/* ++ * Verify the certificate against the certificate from platform keystore buffer's ++ * distrusted list. ++ */ ++static grub_err_t ++is_distrusted_cert (const struct x509_certificate *db_cert) ++{ ++ grub_err_t rc = GRUB_ERR_NONE; ++ grub_size_t i = 0; ++ struct x509_certificate *distrusted_cert = NULL; ++ ++ for (i = 0; i < grub_pks_keystore.dbx_entries; i++) ++ { ++ if (grub_pks_keystore.dbx[i].data == NULL) ++ continue; ++ ++ if (is_x509 (&grub_pks_keystore.dbx[i].guid) == GRUB_ERR_NONE) ++ { ++ distrusted_cert = grub_zalloc (sizeof (struct x509_certificate)); ++ if (distrusted_cert == NULL) ++ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory"); ++ ++ rc = parse_x509_certificate (grub_pks_keystore.dbx[i].data, ++ grub_pks_keystore.dbx[i].data_size, distrusted_cert); ++ if (rc != GRUB_ERR_NONE) ++ { ++ grub_free (distrusted_cert); ++ continue; ++ } ++ ++ if (is_cert_match (distrusted_cert, db_cert) == GRUB_ERR_NONE) ++ { ++ grub_printf ("Warning: a trusted certificate CN='%s' is ignored " ++ "because it is on the distrusted list (dbx).\n", db_cert->subject); ++ grub_free (grub_pks_keystore.dbx[i].data); ++ grub_memset (&grub_pks_keystore.dbx[i], 0, sizeof (grub_pks_sd_t)); ++ certificate_release (distrusted_cert); ++ grub_free (distrusted_cert); ++ return GRUB_ERR_ACCESS_DENIED; ++ } ++ ++ certificate_release (distrusted_cert); ++ grub_free (distrusted_cert); ++ } ++ } ++ ++ return GRUB_ERR_NONE; ++} ++ ++/* Add the certificate into the trusted/distrusted list */ ++static grub_err_t ++add_certificate (const grub_uint8_t *data, const grub_size_t data_size, ++ struct grub_database *database, const grub_size_t is_db) ++{ ++ grub_err_t rc = GRUB_ERR_NONE; ++ grub_size_t key_entries = database->key_entries; ++ struct x509_certificate *cert = NULL; ++ ++ if (data == NULL || data_size == 0) ++ return grub_error (GRUB_ERR_OUT_OF_RANGE, "certificate data/size is null"); ++ ++ cert = grub_zalloc (sizeof (struct x509_certificate)); ++ if (cert == NULL) ++ return grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory"); ++ ++ rc = parse_x509_certificate (data, data_size, cert); ++ if (rc != GRUB_ERR_NONE) ++ { ++ grub_dprintf ("appendedsig", "skipping %s certificate (%d)\n", ++ (is_db ? "trusted":"distrusted"), rc); ++ grub_free (cert); ++ return rc; ++ } ++ ++ if (is_db) ++ { ++ rc = is_distrusted_cert (cert); ++ if (rc != GRUB_ERR_NONE) ++ { ++ certificate_release (cert); ++ grub_free (cert); ++ return rc; ++ } ++ } ++ ++ grub_dprintf ("appendedsig", "add a %s certificate CN='%s'\n", ++ (is_db ? "trusted":"distrusted"), cert->subject); ++ ++ key_entries++; ++ cert->next = database->keys; ++ database->keys = cert; ++ database->key_entries = key_entries; ++ ++ return rc; ++} ++ + static const char * + grub_env_read_sec (struct grub_env_var *var __attribute__ ((unused)), + const char *val __attribute__ ((unused))) +@@ -248,7 +468,7 @@ grub_verify_appended_signature (const grub_uint8_t *buf, grub_size_t bufsize) + struct pkcs7_signerInfo *si; + int i; + +- if (!grub_trusted_key) ++ if (!db.key_entries) + return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("No trusted keys to verify against")); + + err = extract_appended_signature (buf, bufsize, &sig); +@@ -279,7 +499,7 @@ grub_verify_appended_signature (const grub_uint8_t *buf, grub_size_t bufsize) + datasize, i, hash[0], hash[1], hash[2], hash[3]); + + err = GRUB_ERR_BAD_SIGNATURE; +- for (pk = grub_trusted_key; pk; pk = pk->next) ++ for (pk = db.keys; pk; pk = pk->next) + { + rc = grub_crypto_rsa_pad (&hashmpi, hash, si->hash, pk->mpis[0]); + if (rc) +@@ -376,16 +596,16 @@ grub_cmd_distrust (grub_command_t cmd __attribute__ ((unused)), int argc, char * + + if (cert_num == 1) + { +- cert = grub_trusted_key; +- grub_trusted_key = cert->next; ++ cert = db.keys; ++ db.keys = cert->next; + + certificate_release (cert); + grub_free (cert); + return GRUB_ERR_NONE; + } + i = 2; +- prev = grub_trusted_key; +- cert = grub_trusted_key->next; ++ prev = db.keys; ++ cert = db.keys->next; + while (cert) + { + if (i == cert_num) +@@ -432,8 +652,8 @@ grub_cmd_trust (grub_command_t cmd __attribute__ ((unused)), int argc, char **ar + } + grub_dprintf ("appendedsig", "Loaded certificate with CN: %s\n", cert->subject); + +- cert->next = grub_trusted_key; +- grub_trusted_key = cert; ++ cert->next = db.keys; ++ db.keys = cert; + + return GRUB_ERR_NONE; + } +@@ -446,7 +666,7 @@ grub_cmd_list (grub_command_t cmd __attribute__ ((unused)), int argc __attribute + int cert_num = 1; + grub_size_t i; + +- for (cert = grub_trusted_key; cert; cert = cert->next) ++ for (cert = db.keys; cert; cert = cert->next) + { + grub_printf (N_("Certificate %d:\n"), cert_num); + +@@ -539,6 +759,274 @@ static struct grub_fs pseudo_fs = { .name = "pseudo", .fs_read = pseudo_read }; + + static grub_command_t cmd_verify, cmd_list, cmd_distrust, cmd_trust; + ++/* ++ * Verify the trusted certificate against the certificate hashes from platform keystore buffer's ++ * distrusted list. ++ */ ++static grub_err_t ++is_distrusted_cert_hash (const grub_uint8_t *data, const grub_size_t data_size) ++{ ++ grub_err_t rc = GRUB_ERR_NONE; ++ grub_size_t i = 0, cert_hash_size = 0; ++ grub_uint8_t cert_hash[GRUB_MAX_HASH_SIZE] = { 0 }; ++ ++ if (data == NULL || data_size == 0) ++ return grub_error (GRUB_ERR_OUT_OF_RANGE, "trusted certificate data/size is null"); ++ ++ for (i = 0; i < grub_pks_keystore.dbx_entries; i++) ++ { ++ if (grub_pks_keystore.dbx[i].data == NULL || ++ grub_pks_keystore.dbx[i].data_size == 0) ++ continue; ++ ++ rc = get_hash (&grub_pks_keystore.dbx[i].guid, data, data_size, ++ cert_hash, &cert_hash_size); ++ if (rc != GRUB_ERR_NONE) ++ continue; ++ ++ if (cert_hash_size == grub_pks_keystore.dbx[i].data_size && ++ grub_memcmp (grub_pks_keystore.dbx[i].data, cert_hash, cert_hash_size) == 0) ++ { ++ grub_printf ("Warning: a trusted certificate (%02x%02x%02x%02x) is ignored " ++ "because this certificate hash is on the distrusted list (dbx).\n", ++ cert_hash[0], cert_hash[1], cert_hash[2], cert_hash[3]); ++ grub_free (grub_pks_keystore.dbx[i].data); ++ grub_memset (&grub_pks_keystore.dbx[i], 0, sizeof (grub_pks_keystore.dbx[i])); ++ return GRUB_ERR_BAD_SIGNATURE; ++ } ++ } ++ ++ return GRUB_ERR_NONE; ++} ++ ++/* ++ * Verify the trusted binary hash against the platform keystore buffer's ++ * distrusted list. ++ */ ++static grub_err_t ++is_distrusted_binary_hash (const grub_uint8_t *binary_hash, ++ const grub_size_t binary_hash_size) ++{ ++ grub_size_t i = 0; ++ ++ for (i = 0; i < grub_pks_keystore.dbx_entries; i++) ++ { ++ if (grub_pks_keystore.dbx[i].data == NULL || ++ grub_pks_keystore.dbx[i].data_size == 0) ++ continue; ++ ++ if (binary_hash_size == grub_pks_keystore.dbx[i].data_size && ++ grub_memcmp (grub_pks_keystore.dbx[i].data, binary_hash, binary_hash_size) == 0) ++ { ++ grub_printf ("Warning: a trusted binary hash (%02x%02x%02x%02x) is ignored" ++ " because it is on the distrusted list (dbx).\n", ++ binary_hash[0], binary_hash[1], binary_hash[2], binary_hash[3]); ++ grub_free (grub_pks_keystore.dbx[i].data); ++ grub_memset (&grub_pks_keystore.dbx[i], 0, sizeof(grub_pks_keystore.dbx[i])); ++ return GRUB_ERR_BAD_SIGNATURE; ++ } ++ } ++ ++ return GRUB_ERR_NONE; ++} ++ ++/* ++ * Extract the binary hashes from the platform keystore buffer, ++ * and add it to the trusted list if it does not exist in the distrusted list. ++ */ ++static grub_err_t ++add_trusted_binary_hash (const grub_uint8_t **data, const grub_size_t data_size) ++{ ++ grub_err_t rc = GRUB_ERR_NONE; ++ ++ if (*data == NULL || data_size == 0) ++ return grub_error (GRUB_ERR_OUT_OF_RANGE, "trusted binary hash data/size is null"); ++ ++ rc = is_distrusted_binary_hash (*data, data_size); ++ if (rc != GRUB_ERR_NONE) ++ return rc; ++ ++ rc = add_hash (data, data_size, &db.signatures, &db.signature_size, ++ &db.signature_entries); ++ return rc; ++} ++ ++static int ++is_hash (const grub_uuid_t *guid) ++{ ++ /* GUID type of the binary hash */ ++ if (grub_memcmp (guid, &GRUB_PKS_CERT_SHA256_GUID, GRUB_UUID_SIZE) == 0 || ++ grub_memcmp (guid, &GRUB_PKS_CERT_SHA384_GUID, GRUB_UUID_SIZE) == 0 || ++ grub_memcmp (guid, &GRUB_PKS_CERT_SHA512_GUID, GRUB_UUID_SIZE) == 0) ++ return GRUB_ERR_NONE; ++ ++ /* GUID type of the certificate hash */ ++ if (grub_memcmp (guid, &GRUB_PKS_CERT_X509_SHA256_GUID, GRUB_UUID_SIZE) == 0 || ++ grub_memcmp (guid, &GRUB_PKS_CERT_X509_SHA384_GUID, GRUB_UUID_SIZE) == 0 || ++ grub_memcmp (guid, &GRUB_PKS_CERT_X509_SHA512_GUID, GRUB_UUID_SIZE) == 0) ++ return GRUB_ERR_NONE; ++ ++ return GRUB_ERR_UNKNOWN_COMMAND; ++} ++ ++/* ++ * Extract the x509 certificates/binary hashes from the platform keystore buffer, ++ * parse it, and add it to the trusted list. ++ */ ++static grub_err_t ++create_trusted_list (void) ++{ ++ grub_err_t rc = GRUB_ERR_NONE; ++ grub_size_t i = 0; ++ ++ for (i = 0; i < grub_pks_keystore.db_entries; i++) ++ { ++ if (is_hash (&grub_pks_keystore.db[i].guid) == GRUB_ERR_NONE) ++ { ++ rc = add_trusted_binary_hash ((const grub_uint8_t **) ++ &grub_pks_keystore.db[i].data, ++ grub_pks_keystore.db[i].data_size); ++ if (rc == GRUB_ERR_OUT_OF_MEMORY) ++ return rc; ++ } ++ else if (is_x509 (&grub_pks_keystore.db[i].guid) == GRUB_ERR_NONE) ++ { ++ rc = is_distrusted_cert_hash (grub_pks_keystore.db[i].data, ++ grub_pks_keystore.db[i].data_size); ++ if (rc != GRUB_ERR_NONE) ++ continue; ++ ++ rc = add_certificate (grub_pks_keystore.db[i].data, ++ grub_pks_keystore.db[i].data_size, &db, 1); ++ if (rc == GRUB_ERR_OUT_OF_MEMORY) ++ return rc; ++ else if (rc != GRUB_ERR_NONE) ++ continue; ++ } ++ else ++ grub_dprintf ("appendedsig", "unsupported signature data type and " ++ "skipping trusted data (%" PRIuGRUB_SIZE ")\n", i + 1); ++ } ++ ++ return GRUB_ERR_NONE; ++} ++ ++/* ++ * Extract the certificates, certificate/binary hashes out of the platform keystore buffer, ++ * and add it to the distrusted list. ++ */ ++static grub_err_t ++create_distrusted_list (void) ++{ ++ grub_err_t rc = GRUB_ERR_NONE; ++ grub_size_t i = 0; ++ ++ for (i = 0; i < grub_pks_keystore.dbx_entries; i++) ++ { ++ if (grub_pks_keystore.dbx[i].data != NULL || ++ grub_pks_keystore.dbx[i].data_size > 0) ++ { ++ if (is_x509 (&grub_pks_keystore.dbx[i].guid) == GRUB_ERR_NONE) ++ { ++ rc = add_certificate (grub_pks_keystore.dbx[i].data, ++ grub_pks_keystore.dbx[i].data_size, &dbx, 0); ++ if (rc == GRUB_ERR_OUT_OF_MEMORY) ++ return rc; ++ } ++ else if (is_hash (&grub_pks_keystore.dbx[i].guid) == GRUB_ERR_NONE) ++ { ++ rc = add_hash ((const grub_uint8_t **) &grub_pks_keystore.dbx[i].data, ++ grub_pks_keystore.dbx[i].data_size, ++ &dbx.signatures, &dbx.signature_size, ++ &dbx.signature_entries); ++ if (rc != GRUB_ERR_NONE) ++ return rc; ++ } ++ else ++ grub_dprintf ("appendedsig", "unsupported signature data type and " ++ "skipping distrusted data (%" PRIuGRUB_SIZE ")\n", i + 1); ++ } ++ } ++ ++ return rc; ++} ++ ++/* ++ * Extract the x509 certificates from the ELF note header, ++ * parse it, and add it to the trusted list. ++ */ ++static grub_err_t ++build_static_trusted_list (const struct grub_module_header *header) ++{ ++ grub_err_t err = GRUB_ERR_NONE; ++ struct grub_file pseudo_file; ++ grub_uint8_t *cert_data = NULL; ++ grub_ssize_t cert_data_size = 0; ++ ++ grub_memset (&pseudo_file, 0, sizeof (pseudo_file)); ++ pseudo_file.fs = &pseudo_fs; ++ 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); ++ ++ err = grub_read_file (&pseudo_file, &cert_data, &cert_data_size); ++ if (err != GRUB_ERR_NONE) ++ return err; ++ ++ err = add_certificate (cert_data, cert_data_size, &db, 1); ++ grub_free (cert_data); ++ ++ return err; ++} ++ ++/* releasing memory */ ++static void ++free_trusted_list (void) ++{ ++ struct x509_certificate *cert; ++ grub_size_t i = 0; ++ ++ while (db.keys != NULL) ++ { ++ cert = db.keys; ++ db.keys = db.keys->next; ++ certificate_release (cert); ++ grub_free (cert); ++ } ++ ++ for (i = 0; i < db.signature_entries; i++) ++ grub_free (db.signatures[i]); ++ ++ grub_free (db.signatures); ++ grub_free (db.signature_size); ++ grub_memset (&db, 0, sizeof (db)); ++} ++ ++/* releasing memory */ ++static void ++free_distrusted_list (void) ++{ ++ struct x509_certificate *cert; ++ grub_size_t i = 0; ++ ++ while (dbx.keys != NULL) ++ { ++ cert = dbx.keys; ++ dbx.keys = dbx.keys->next; ++ certificate_release (cert); ++ grub_free (cert); ++ } ++ ++ for (i = 0; i < dbx.signature_entries; i++) ++ grub_free (dbx.signatures[i]); ++ ++ grub_free (dbx.signatures); ++ grub_free (dbx.signature_size); ++ grub_memset (&dbx, 0, sizeof (dbx)); ++} ++ + GRUB_MOD_INIT (appendedsig) + { + int rc; +@@ -548,7 +1036,6 @@ GRUB_MOD_INIT (appendedsig) + if (grub_is_lockdown () == GRUB_LOCKDOWN_ENABLED) + 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_env_export ("check_appended_signatures"); + +@@ -556,37 +1043,50 @@ GRUB_MOD_INIT (appendedsig) + if (rc) + grub_fatal ("Error initing ASN.1 data structures: %d: %s\n", rc, asn1_strerror (rc)); + +- FOR_MODULES (header) +- { +- struct grub_file pseudo_file; +- struct x509_certificate *pk = NULL; +- grub_err_t err; ++ if (!grub_pks_use_keystore && check_sigs == check_sigs_forced) ++ { ++ FOR_MODULES (header) ++ { ++ /* Not an ELF module, skip. */ ++ if (header->type != OBJ_TYPE_X509_PUBKEY) ++ continue; + +- /* Not an X.509 certificate, skip. */ +- if (header->type != OBJ_TYPE_X509_PUBKEY) +- continue; ++ rc = build_static_trusted_list (header); ++ if (rc != GRUB_ERR_NONE) ++ { ++ free_trusted_list (); ++ grub_error (rc, "static trusted list creation failed"); ++ } ++ else ++ grub_dprintf ("appendedsig", "the trusted list now has %" PRIuGRUB_SIZE " static keys\n", ++ db.key_entries); ++ } ++ } ++ else if (grub_pks_use_keystore && check_sigs == check_sigs_forced) ++ { ++ rc = create_trusted_list (); ++ if (rc != GRUB_ERR_NONE) ++ { ++ free_trusted_list (); ++ grub_error (rc, "trusted list creation failed"); ++ } ++ else ++ { ++ rc = create_distrusted_list (); ++ if (rc != GRUB_ERR_NONE) ++ { ++ free_trusted_list (); ++ free_distrusted_list (); ++ grub_error (rc, "distrusted list creation failed"); ++ } ++ else ++ grub_dprintf ("appendedsig", "the trusted list now has %" PRIuGRUB_SIZE " keys.\n" ++ "the distrusted list now has %" PRIuGRUB_SIZE " keys.\n", ++ db.signature_entries + db.key_entries, dbx.signature_entries); ++ } + +- grub_memset (&pseudo_file, 0, sizeof (pseudo_file)); +- pseudo_file.fs = &pseudo_fs; +- 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); +- +- pk = grub_zalloc (sizeof (struct x509_certificate)); +- if (!pk) +- grub_fatal ("Out of memory loading initial certificates"); +- +- err = read_cert_from_file (&pseudo_file, pk); +- if (err != GRUB_ERR_NONE) +- grub_fatal ("Error loading initial key: %s", grub_errmsg); +- +- grub_dprintf ("appendedsig", "loaded certificate CN='%s'\n", pk->subject); +- +- pk->next = grub_trusted_key; +- grub_trusted_key = pk; +- } ++ grub_pks_free_keystore (); ++ } + + cmd_trust = grub_register_command ("trust_certificate", grub_cmd_trust, N_("X509_CERTIFICATE"), + N_("Add X509_CERTIFICATE to trusted certificates.")); +diff --git a/grub-core/kern/file.c b/grub-core/kern/file.c +index 58fae17..fa73c04 100644 +--- a/grub-core/kern/file.c ++++ b/grub-core/kern/file.c +@@ -256,3 +256,37 @@ grub_file_seek (grub_file_t file, grub_off_t offset) + + return old; + } ++ ++grub_err_t ++grub_read_file (const grub_file_t file, grub_uint8_t **data, grub_ssize_t *data_size) ++{ ++ grub_uint8_t *buffer = NULL; ++ grub_ssize_t read_size = 0; ++ grub_off_t total_read_size = 0; ++ grub_off_t file_size = grub_file_size (file); ++ ++ if (file_size == GRUB_FILE_SIZE_UNKNOWN) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, ++ N_("could not determine the size of the file.")); ++ ++ buffer = grub_zalloc (file_size); ++ if (buffer == NULL) ++ return grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory")); ++ ++ while (total_read_size < file_size) ++ { ++ read_size = grub_file_read (file, &buffer[total_read_size], file_size - total_read_size); ++ if (read_size < 0) ++ { ++ grub_free (buffer); ++ return grub_error (GRUB_ERR_READ_ERROR, N_("unable to read the file")); ++ } ++ ++ total_read_size += read_size; ++ } ++ ++ *data = buffer; ++ *data_size = total_read_size; ++ ++ return GRUB_ERR_NONE; ++} +diff --git a/include/grub/file.h b/include/grub/file.h +index d678de0..27e1a88 100644 +--- a/include/grub/file.h ++++ b/include/grub/file.h +@@ -217,6 +217,7 @@ grub_ssize_t EXPORT_FUNC(grub_file_read) (grub_file_t file, void *buf, + grub_size_t len); + grub_off_t EXPORT_FUNC(grub_file_seek) (grub_file_t file, grub_off_t offset); + grub_err_t EXPORT_FUNC(grub_file_close) (grub_file_t file); ++grub_err_t EXPORT_FUNC(grub_read_file) (const grub_file_t file, grub_uint8_t **data, grub_ssize_t *data_size); + + /* Return value of grub_file_size() in case file size is unknown. */ + #define GRUB_FILE_SIZE_UNKNOWN 0xffffffffffffffffULL diff --git a/0471-appendedsig-While-verifying-the-kernel-use-trusted-a.patch b/0471-appendedsig-While-verifying-the-kernel-use-trusted-a.patch new file mode 100644 index 0000000..2011cb7 --- /dev/null +++ b/0471-appendedsig-While-verifying-the-kernel-use-trusted-a.patch @@ -0,0 +1,250 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Sudhakar Kuppusamy +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 +Reviewed-by: Stefan Berger +Reviewed-by: Avnish Chouhan +--- + 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; + } + diff --git a/0472-powerpc_ieee1275-set-use_static_keys-flag.patch b/0472-powerpc_ieee1275-set-use_static_keys-flag.patch new file mode 100644 index 0000000..f0837fc --- /dev/null +++ b/0472-powerpc_ieee1275-set-use_static_keys-flag.patch @@ -0,0 +1,107 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Sudhakar Kuppusamy +Date: Thu, 27 Mar 2025 01:02:39 +0530 +Subject: [PATCH] powerpc_ieee1275: set use_static_keys flag + +Introduce the use_static_keys flag to indicate that static keys are to be used +rather than keys from the PKS storage's DB variable. This variable is set when +Secure Boot is enabled with PKS but the DB variable is not present in the PKS storage. +The appendedsig module would use this variable to extract the default DB keys from +the ELF note and store the keys found there in the trustedlist. + +Signed-off-by: Sudhakar Kuppusamy +Reviewed-by: Stefan Berger +Reviewed-by: Avnish Chouhan +--- + grub-core/kern/powerpc/ieee1275/platform_keystore.c | 16 +++++++++++++++- + grub-core/term/tparm.c | 1 - + include/grub/powerpc/ieee1275/platform_keystore.h | 11 ++++++----- + include/grub/types.h | 2 ++ + 4 files changed, 23 insertions(+), 7 deletions(-) + +diff --git a/grub-core/kern/powerpc/ieee1275/platform_keystore.c b/grub-core/kern/powerpc/ieee1275/platform_keystore.c +index cf1d055..a83fcf7 100644 +--- a/grub-core/kern/powerpc/ieee1275/platform_keystore.c ++++ b/grub-core/kern/powerpc/ieee1275/platform_keystore.c +@@ -34,7 +34,11 @@ + /* Platform Keystore */ + static grub_size_t pks_max_object_size; + grub_uint8_t grub_pks_use_keystore = 0; +-grub_pks_t grub_pks_keystore = { .db = NULL, .dbx = NULL, .db_entries = 0, .dbx_entries = 0 }; ++grub_pks_t grub_pks_keystore = { .db = NULL, ++ .dbx = NULL, ++ .db_entries = 0, ++ .dbx_entries = 0, ++ .use_static_keys = false }; + + /* Convert the esl data into the ESL */ + static grub_esl_t * +@@ -312,6 +316,16 @@ grub_pks_keystore_init (void) + grub_memset (&grub_pks_keystore, 0, sizeof (grub_pks_t)); + /* DB */ + rc = read_secure_boot_variables (0, DB, &grub_pks_keystore.db, &grub_pks_keystore.db_entries); ++ if (rc == PKS_OBJECT_NOT_FOUND) ++ { ++ rc = GRUB_ERR_NONE; ++ /* ++ * DB variable won't be available by default in PKS. ++ * So, it will load the Default Keys from ELF Note ++ */ ++ grub_pks_keystore.use_static_keys = true; ++ } ++ + if (rc == GRUB_ERR_NONE) + { + /* DBX */ +diff --git a/grub-core/term/tparm.c b/grub-core/term/tparm.c +index fb5b15a..f2db325 100644 +--- a/grub-core/term/tparm.c ++++ b/grub-core/term/tparm.c +@@ -46,7 +46,6 @@ + /* + * Common/troublesome character definitions + */ +-typedef char grub_bool_t; + #ifndef FALSE + # define FALSE (0) + #endif +diff --git a/include/grub/powerpc/ieee1275/platform_keystore.h b/include/grub/powerpc/ieee1275/platform_keystore.h +index 0641adb..870fb8c 100644 +--- a/include/grub/powerpc/ieee1275/platform_keystore.h ++++ b/include/grub/powerpc/ieee1275/platform_keystore.h +@@ -199,10 +199,11 @@ struct grub_pks_sd + /* The structure of a PKS.*/ + struct grub_pks + { +- grub_pks_sd_t *db; /* signature database */ +- grub_pks_sd_t *dbx; /* forbidden signature database */ +- grub_size_t db_entries; /* size of signature database */ +- grub_size_t dbx_entries; /* size of forbidden signature database */ ++ grub_pks_sd_t *db; /* signature database */ ++ grub_pks_sd_t *dbx; /* forbidden signature database */ ++ grub_size_t db_entries; /* size of signature database */ ++ grub_size_t dbx_entries; /* size of forbidden signature database */ ++ grub_bool_t use_static_keys;/* flag to indicate use of static keys */ + } GRUB_PACKED; + + #ifdef __powerpc__ +@@ -217,7 +218,7 @@ extern grub_pks_t EXPORT_VAR(grub_pks_keystore); + #else + + #define grub_pks_use_keystore 0 +-grub_pks_t grub_pks_keystore = {NULL, NULL, 0, 0}; ++grub_pks_t grub_pks_keystore = {NULL, NULL, 0, 0, false}; + void grub_pks_free_keystore (void); + + #endif +diff --git a/include/grub/types.h b/include/grub/types.h +index bfa7af9..32ca225 100644 +--- a/include/grub/types.h ++++ b/include/grub/types.h +@@ -353,4 +353,6 @@ struct grub_uuid + grub_uint8_t b[GRUB_UUID_SIZE]; + }; + ++typedef char grub_bool_t; ++ + #endif /* ! GRUB_TYPES_HEADER */ diff --git a/0473-appendedsig-Reads-the-default-DB-keys-from-ELF-Note.patch b/0473-appendedsig-Reads-the-default-DB-keys-from-ELF-Note.patch new file mode 100644 index 0000000..64f2b89 --- /dev/null +++ b/0473-appendedsig-Reads-the-default-DB-keys-from-ELF-Note.patch @@ -0,0 +1,108 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Sudhakar Kuppusamy +Date: Thu, 27 Mar 2025 01:02:40 +0530 +Subject: [PATCH] appendedsig: Reads the default DB keys from ELF Note + +If Secure Boot is enabled with PKS and the use_static_keys flag is set, +then read the DB default keys from the ELF note and store them in the trusted list buffer. + +Signed-off-by: Sudhakar Kuppusamy +Reviewed-by: Stefan Berger +Reviewed-by: Avnish Chouhan +--- + grub-core/commands/appendedsig/appendedsig.c | 56 ++++++++++++++++++++-------- + 1 file changed, 40 insertions(+), 16 deletions(-) + +diff --git a/grub-core/commands/appendedsig/appendedsig.c b/grub-core/commands/appendedsig/appendedsig.c +index 07d6b39..f51942b 100644 +--- a/grub-core/commands/appendedsig/appendedsig.c ++++ b/grub-core/commands/appendedsig/appendedsig.c +@@ -1041,7 +1041,7 @@ create_distrusted_list (void) + * parse it, and add it to the trusted list. + */ + static grub_err_t +-build_static_trusted_list (const struct grub_module_header *header) ++build_static_trusted_list (const struct grub_module_header *header, const grub_bool_t is_pks) + { + grub_err_t err = GRUB_ERR_NONE; + struct grub_file pseudo_file; +@@ -1060,6 +1060,13 @@ build_static_trusted_list (const struct grub_module_header *header) + if (err != GRUB_ERR_NONE) + return err; + ++ if (is_pks) ++ { ++ err = is_distrusted_cert_hash (cert_data, cert_data_size); ++ if (err != GRUB_ERR_NONE) ++ return err; ++ } ++ + err = add_certificate (cert_data, cert_data_size, &db, 1); + grub_free (cert_data); + +@@ -1112,6 +1119,22 @@ free_distrusted_list (void) + grub_memset (&dbx, 0, sizeof (dbx)); + } + ++static grub_err_t ++load_static_keys (const struct grub_module_header *header, const grub_bool_t is_pks) ++{ ++ int rc = GRUB_ERR_NONE; ++ FOR_MODULES (header) ++ { ++ /* Not an ELF module, skip. */ ++ if (header->type != OBJ_TYPE_X509_PUBKEY) ++ continue; ++ rc = build_static_trusted_list (header, is_pks); ++ if (rc != GRUB_ERR_NONE) ++ return rc; ++ } ++ return rc; ++} ++ + GRUB_MOD_INIT (appendedsig) + { + int rc; +@@ -1130,26 +1153,27 @@ GRUB_MOD_INIT (appendedsig) + + if (!grub_pks_use_keystore && check_sigs == check_sigs_forced) + { +- FOR_MODULES (header) ++ rc = load_static_keys (header, false); ++ if (rc != GRUB_ERR_NONE) + { +- /* Not an ELF module, skip. */ +- if (header->type != OBJ_TYPE_X509_PUBKEY) +- continue; +- +- rc = build_static_trusted_list (header); +- if (rc != GRUB_ERR_NONE) +- { +- free_trusted_list (); +- grub_error (rc, "static trusted list creation failed"); +- } +- else +- grub_dprintf ("appendedsig", "the trusted list now has %" PRIuGRUB_SIZE " static keys\n", +- db.key_entries); ++ free_trusted_list (); ++ grub_error (rc, "static trusted list creation failed"); + } ++ else ++ grub_dprintf ("appendedsig", "the trusted list now has %" PRIuGRUB_SIZE " static keys\n", ++ db.key_entries); + } + else if (grub_pks_use_keystore && check_sigs == check_sigs_forced) + { +- rc = create_trusted_list (); ++ if (grub_pks_keystore.use_static_keys) ++ { ++ grub_printf ("Warning: db variable is not available at PKS and using a static keys " ++ "as a default key in trusted list\n"); ++ rc = load_static_keys (header, grub_pks_keystore.use_static_keys); ++ } ++ else ++ rc = create_trusted_list (); ++ + if (rc != GRUB_ERR_NONE) + { + free_trusted_list (); diff --git a/0474-appendedsig-The-grub-command-s-trusted-and-distruste.patch b/0474-appendedsig-The-grub-command-s-trusted-and-distruste.patch new file mode 100644 index 0000000..6dc51f6 --- /dev/null +++ b/0474-appendedsig-The-grub-command-s-trusted-and-distruste.patch @@ -0,0 +1,660 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Sudhakar Kuppusamy +Date: Thu, 27 Mar 2025 01:02:41 +0530 +Subject: [PATCH] appendedsig: The grub command's trusted and distrusted + support + +To support the following trusted and distrusted commands + + 1. trusted_list: + It will show the list of trusted certificates and binary hashes + 2. distrusted_list: + It will show the list of distrusted certificates and binary/certificate hashes + 3. trusted_certificate: + It will add the trusted certificate to the trusted list + 4. trusted_signature: + It will add the certificate/binary hash to the trusted list + 5. distrusted_certificate: + It will remove the trusted certificate from trsuted list + 6. distrusted_signature: + It will add the certificate/binary hash to the distrsuted list + +Note:- + The addition/deletion of trusted certificates and binary hashes +are not allowed in grub command prompt while secure boot is enabled. + +Signed-off-by: Sudhakar Kuppusamy +Reviewed-by: Avnish Chouhan +--- + grub-core/commands/appendedsig/appendedsig.c | 528 ++++++++++++++++++--------- + 1 file changed, 356 insertions(+), 172 deletions(-) + +diff --git a/grub-core/commands/appendedsig/appendedsig.c b/grub-core/commands/appendedsig/appendedsig.c +index f51942b..b8548f8 100644 +--- a/grub-core/commands/appendedsig/appendedsig.c ++++ b/grub-core/commands/appendedsig/appendedsig.c +@@ -117,6 +117,36 @@ static enum + check_sigs_forced = 2 + } check_sigs = check_sigs_no; + ++enum ++{ ++ OPTION_BINARY_HASH = 0, ++ OPTION_CERT_HASH = 1 ++}; ++ ++static const struct grub_arg_option options[] = ++{ ++ {"binary-hash", 'b', 0, N_("hash file of the binary."), 0, ARG_TYPE_NONE}, ++ {"cert-hash", 'c', 1, N_("hash file of the certificate."), 0, ARG_TYPE_NONE}, ++ {0, 0, 0, 0, 0, 0} ++}; ++ ++static void ++print_hex (const grub_uint8_t *data, const grub_size_t length) ++{ ++ grub_size_t i, count = 0; ++ for (i = 0; i < length-1; i++) ++ { ++ grub_printf ("%02x:", data[i]); ++ count++; ++ if (count == 16) ++ { ++ grub_printf ("\n\t "); ++ count = 0; ++ } ++ } ++ grub_printf ("%02x\n", data[i]); ++} ++ + /* + * GUID can be used to determine the hashing function and + * generate the hash using determined hashing function. +@@ -344,72 +374,6 @@ grub_env_write_sec (struct grub_env_var *var __attribute__ ((unused)), const cha + 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; +- grub_size_t file_size; +- +- err = file_read_all (f, &buf, &file_size); +- if (err != GRUB_ERR_NONE) +- return err; +- +- err = parse_x509_certificate (buf, file_size, certificate); +- grub_free (buf); +- +- return err; +-} +- + static grub_err_t + extract_appended_signature (const grub_uint8_t *buf, grub_size_t bufsize, + struct grub_appended_signature *sig) +@@ -630,143 +594,345 @@ grub_verify_appended_signature (const grub_uint8_t *buf, grub_size_t bufsize) + static grub_err_t + 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_size_t file_size; ++ grub_file_t signed_file = NULL; ++ grub_uint8_t *signed_data = NULL; ++ grub_ssize_t signed_data_size = 0; + +- if (argc < 1) +- return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected")); ++ if (argc != 1) ++ { ++ grub_printf (N_("a signed file is expected\n" ++ "Example:\n\tverify_appended \n")); ++ return GRUB_ERR_BAD_ARGUMENT; ++ } + + grub_dprintf ("appendedsig", "verifying %s\n", args[0]); + +- f = grub_file_open (args[0], GRUB_FILE_TYPE_VERIFY_SIGNATURE); +- if (!f) ++ signed_file = grub_file_open (args[0], GRUB_FILE_TYPE_VERIFY_SIGNATURE); ++ if (signed_file == NULL) ++ return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("unable to open a signed file")); ++ ++ err = grub_read_file (signed_file, &signed_data, &signed_data_size); ++ if (err != GRUB_ERR_NONE) ++ { ++ grub_file_close (signed_file); ++ return err; ++ } ++ ++ grub_file_close (signed_file); ++ err = grub_verify_appended_signature (signed_data, signed_data_size); ++ grub_free (signed_data); ++ ++ return err; ++} ++ ++static grub_err_t ++grub_cmd_trusted_list (grub_command_t cmd __attribute__((unused)), ++ int argc __attribute__((unused)), char **args __attribute__((unused))) ++{ ++ struct x509_certificate *cert = NULL; ++ grub_size_t i = 0, cert_num = 1; ++ ++ for (cert = db.keys; cert; cert = cert->next) ++ { ++ grub_printf (N_("trusted certificate %" PRIuGRUB_SIZE ":\n"), cert_num); ++ grub_printf (N_("\tserial: ")); ++ ++ for (i = 0; i < cert->serial_len - 1; 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++; ++ } ++ ++ for (i = 0; i < db.signature_entries; i++) ++ { ++ grub_printf (N_("trusted binary hash %" PRIuGRUB_SIZE ":\n"), i+1); ++ grub_printf (N_("\thash: ")); ++ print_hex (db.signatures[i], db.signature_size[i]); ++ } ++ ++ return GRUB_ERR_NONE; ++} ++ ++static grub_err_t ++grub_cmd_distrusted_list (grub_command_t cmd __attribute__((unused)), ++ int argc __attribute__((unused)), ++ char **args __attribute__((unused))) ++{ ++ struct x509_certificate *cert = NULL; ++ grub_size_t i = 0, cert_num = 1; ++ ++ for (cert = dbx.keys; cert; cert = cert->next) ++ { ++ grub_printf (N_("distrusted certificate %" PRIuGRUB_SIZE ":\n"), cert_num); ++ grub_printf (N_("\tserial: ")); ++ ++ for (i = 0; i < cert->serial_len - 1; 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++; ++ } ++ ++ for (i = 0; i < dbx.signature_entries; i++) ++ { ++ grub_printf (N_("distrusted certificate/binary hash %" PRIuGRUB_SIZE ":\n"), i+1); ++ grub_printf (N_("\thash: ")); ++ print_hex (dbx.signatures[i], dbx.signature_size[i]); ++ } ++ ++ return GRUB_ERR_NONE; ++} ++ ++static grub_err_t ++grub_cmd_trusted_cert (grub_command_t cmd __attribute__((unused)), ++ int argc, char **args) ++{ ++ grub_err_t err = GRUB_ERR_NONE; ++ grub_file_t cert_file = NULL; ++ grub_uint8_t *cert_data = NULL; ++ grub_ssize_t cert_data_size = 0; ++ ++ if (argc != 1) + { +- err = grub_errno; +- goto cleanup; ++ grub_printf (N_("a trusted X.509 certificate file is expected\n" ++ "Example:\n\ttrusted_certificate \n")); ++ return GRUB_ERR_BAD_ARGUMENT; + } + +- err = file_read_all (f, &data, &file_size); ++ if (check_sigs == check_sigs_forced) ++ { ++ grub_printf ("Warning: since secure boot is enabled, " ++ "adding of trusted X.509 certificate is not permitted!\n"); ++ return grub_errno; ++ } ++ ++ if (grub_strlen (args[0]) == 0) ++ return grub_error (GRUB_ERR_BAD_FILENAME, ++ N_("missing trusted X.509 certificate file")); ++ ++ cert_file = grub_file_open (args[0], GRUB_FILE_TYPE_CERTIFICATE_TRUST | ++ GRUB_FILE_TYPE_NO_DECOMPRESS); ++ if (cert_file == NULL) ++ return grub_error (GRUB_ERR_FILE_NOT_FOUND, ++ N_("unable to open the trusted X.509 certificate file")); ++ ++ err = grub_read_file (cert_file, &cert_data, &cert_data_size); + if (err != GRUB_ERR_NONE) +- goto cleanup; ++ { ++ grub_file_close (cert_file); ++ return err; ++ } + +- err = grub_verify_appended_signature (data, file_size); ++ grub_file_close (cert_file); ++ err = add_certificate (cert_data, cert_data_size, &db, 1); ++ if (err != GRUB_ERR_NONE) ++ { ++ free_trusted_list (); ++ free_distrusted_list (); ++ grub_error (err, "adding of trusted certificate failed"); ++ } + +- grub_free (data); ++ grub_free (cert_data); + +-cleanup: +- if (f) +- grub_file_close (f); + return err; + } + + static grub_err_t +-grub_cmd_distrust (grub_command_t cmd __attribute__ ((unused)), int argc, char **args) ++grub_cmd_trusted_hash (grub_command_t cmd __attribute__((unused)), int argc, char**args) + { +- unsigned long cert_num, i; +- struct x509_certificate *cert, *prev; ++ grub_err_t rc = GRUB_ERR_NONE; ++ grub_file_t hash_file = NULL; ++ grub_uint8_t *hash_data = NULL; ++ grub_ssize_t hash_data_size = 0; + + if (argc != 1) +- return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("One argument expected")); ++ { ++ grub_printf (N_("a trusted binary hash file is expected\n" ++ "Example:\n\ttrusted_signature \n")); ++ return GRUB_ERR_BAD_ARGUMENT; ++ } ++ ++ if (check_sigs == check_sigs_forced) ++ { ++ grub_printf ("Warning: since secure boot is enabled, " ++ "adding of trusted binary hash is not permitted!\n"); ++ return grub_errno; ++ } ++ ++ if (grub_strlen (args[0]) == 0) ++ return grub_error (GRUB_ERR_BAD_FILENAME, N_("missing trusted binary hash file")); ++ ++ hash_file = grub_file_open (args[0], GRUB_FILE_TYPE_TO_HASH | GRUB_FILE_TYPE_NO_DECOMPRESS); ++ if (hash_file == NULL) ++ return grub_error (GRUB_ERR_FILE_NOT_FOUND, ++ N_("unable to open the trusted binary hash file")); ++ ++ rc = grub_read_file (hash_file, &hash_data, &hash_data_size); ++ if (rc != GRUB_ERR_NONE) ++ { ++ grub_file_close (hash_file); ++ return rc; ++ } ++ ++ grub_file_close (hash_file); ++ ++ grub_dprintf ("appendedsig", "adding a trusted binary hash %s\n with size of %" PRIuGRUB_SIZE "\n", ++ hash_data, hash_data_size); ++ ++ /* only accept SHA256, SHA384 and SHA512 binary hash */ ++ if (hash_data_size != 32 && hash_data_size != 48 && hash_data_size != 64) ++ return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("unacceptable trusted binary hash type")); ++ ++ rc = add_hash ((const grub_uint8_t **) &hash_data, hash_data_size, &db.signatures, ++ &db.signature_size, &db.signature_entries); ++ if (rc != GRUB_ERR_NONE) ++ { ++ free_trusted_list (); ++ free_distrusted_list (); ++ grub_error (rc, "adding of trusted binary hash failed"); ++ } ++ ++ grub_free (hash_data); ++ ++ return rc; ++} ++ ++static grub_err_t ++grub_cmd_distrusted_cert (grub_command_t cmd __attribute__((unused)), int argc, char **args) ++{ ++ grub_size_t cert_num = 0, i = 1; ++ struct x509_certificate *current_cert = db.keys; ++ struct x509_certificate *previous_cert = db.keys; ++ ++ if (argc != 1) ++ { ++ grub_printf (N_("trusted certificate number is expected\n" ++ "Example:\n\tdistrusted_certificate \n")); ++ return GRUB_ERR_BAD_ARGUMENT; ++ } ++ ++ if (check_sigs == check_sigs_forced) ++ { ++ grub_printf ("Warning: since secure boot is enabled, " ++ "removing of trusted certificate is not permitted!\n"); ++ return grub_errno; ++ } + +- grub_errno = GRUB_ERR_NONE; + cert_num = grub_strtoul (args[0], NULL, 10); +- if (grub_errno != GRUB_ERR_NONE) +- return grub_errno; +- + if (cert_num < 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, +- N_("Certificate number too small - numbers start at 1")); ++ N_("trusted certificate number should to begin with 1")); + +- if (cert_num == 1) +- { +- cert = db.keys; +- db.keys = cert->next; ++ if (cert_num > db.key_entries) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, ++ N_("trusted certificate number should not exceed %" PRIuGRUB_SIZE ""), ++ db.key_entries); ++ else if (cert_num < db.key_entries) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, ++ N_("there is no certificate on the trusted list. so, not permitted")); + +- certificate_release (cert); +- grub_free (cert); +- return GRUB_ERR_NONE; +- } +- i = 2; +- prev = db.keys; +- cert = db.keys->next; +- while (cert) ++ for (i = 1; i < db.key_entries; i++) + { +- if (i == cert_num) ++ if (cert_num == 1) ++ { ++ previous_cert = current_cert->next; ++ break; ++ } ++ else if (cert_num == i) + { +- prev->next = cert->next; +- certificate_release (cert); +- grub_free (cert); +- return GRUB_ERR_NONE; ++ previous_cert->next = current_cert->next; ++ break; + } +- i++; +- prev = cert; +- cert = cert->next; ++ ++ previous_cert = current_cert; ++ current_cert = current_cert->next; + } + +- return grub_error (GRUB_ERR_BAD_ARGUMENT, +- N_("No certificate number %lu found - only %lu certificates in the store"), +- cert_num, i - 1); ++ certificate_release (current_cert); ++ grub_free (current_cert); ++ ++ return GRUB_ERR_NONE; + } + + static grub_err_t +-grub_cmd_trust (grub_command_t cmd __attribute__ ((unused)), int argc, char **args) ++grub_cmd_distrusted_hash (grub_extcmd_context_t ctxt, int argc, char **args) + { +- grub_file_t certf; +- struct x509_certificate *cert = NULL; +- grub_err_t err; ++ grub_err_t rc = GRUB_ERR_NONE; ++ grub_file_t hash_file = NULL; ++ grub_uint8_t *hash_data = NULL; ++ grub_ssize_t hash_data_size = 0; + +- 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); +- 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")); ++ if (argc != 2) ++ { ++ grub_printf (N_("a distrusted certificate/binary hash file is expected\n" ++ "Example:\n\tdistrusted_signature [option] \n" ++ "option:\n[-b|--binary-hash] FILE [BINARY HASH FILE]\n" ++ "[-c|--cert-hash] FILE [CERTFICATE HASH FILE]\n")); ++ return GRUB_ERR_BAD_ARGUMENT; ++ } + +- err = read_cert_from_file (certf, cert); +- grub_file_close (certf); +- if (err != GRUB_ERR_NONE) ++ if (check_sigs == check_sigs_forced) + { +- grub_free (cert); +- return err; ++ grub_printf ("Warning: since secure boot is enabled, " ++ "adding of distrusted certificate/binary hash is not permitted!\n"); ++ return grub_errno; + } +- grub_dprintf ("appendedsig", "Loaded certificate with CN: %s\n", cert->subject); + +- cert->next = db.keys; +- db.keys = cert; ++ if (!ctxt->state[OPTION_BINARY_HASH].set && !ctxt->state[OPTION_CERT_HASH].set) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("missing options and use --help to konw")); + +- return GRUB_ERR_NONE; +-} ++ if (grub_strlen (args[1]) == 0) ++ return grub_error (GRUB_ERR_BAD_FILENAME, ++ N_("missing distrusted certificate/binary hash file")); + +-static grub_err_t +-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; +- grub_size_t i; ++ hash_file = grub_file_open (args[1], GRUB_FILE_TYPE_TO_HASH | GRUB_FILE_TYPE_NO_DECOMPRESS); ++ if (hash_file == NULL) ++ return grub_error (GRUB_ERR_FILE_NOT_FOUND, ++ N_("unable to open the distrusted certificate/binary hash file")); + +- for (cert = db.keys; cert; cert = cert->next) ++ rc = grub_read_file (hash_file, &hash_data, &hash_data_size); ++ if (rc != GRUB_ERR_NONE) + { +- grub_printf (N_("Certificate %d:\n"), cert_num); ++ grub_file_close (hash_file); ++ return rc; ++ } + +- grub_printf (N_("\tSerial: ")); +- for (i = 0; i < cert->serial_len - 1; i++) +- { +- grub_printf ("%02x:", cert->serial[i]); +- } +- grub_printf ("%02x\n", cert->serial[cert->serial_len - 1]); ++ grub_file_close (hash_file); + +- grub_printf ("\tCN: %s\n\n", cert->subject); +- cert_num++; ++ grub_dprintf ("appendedsig", "adding a distrusted certificate/binary hash %s\n" ++ " with size of %" PRIuGRUB_SIZE "\n", hash_data, hash_data_size); ++ ++ if (ctxt->state[OPTION_BINARY_HASH].set) ++ { ++ /* only accept SHA256, SHA384 and SHA512 binary hash */ ++ if (hash_data_size != 32 && hash_data_size != 48 && hash_data_size != 64) ++ return grub_error (GRUB_ERR_BAD_SIGNATURE, ++ N_("unacceptable distrusted binary hash type")); ++ } ++ else if (ctxt->state[OPTION_CERT_HASH].set) ++ { ++ /* only accept SHA256, SHA384 and SHA512 certificate hash */ ++ if (hash_data_size != 32 && hash_data_size != 48 && hash_data_size != 64) ++ return grub_error (GRUB_ERR_BAD_SIGNATURE, ++ N_("unacceptable distrusted certificate hash type")); + } + +- return GRUB_ERR_NONE; ++ rc = add_hash ((const grub_uint8_t **) &hash_data, hash_data_size, &dbx.signatures, ++ &dbx.signature_size, &dbx.signature_entries); ++ if (rc != GRUB_ERR_NONE) ++ { ++ free_trusted_list (); ++ free_distrusted_list (); ++ grub_error (rc, "adding of distrusted binary/certificate hash failed"); ++ } ++ ++ grub_free (hash_data); ++ ++ return rc; + } + + static grub_err_t +@@ -842,8 +1008,6 @@ 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 grub_command_t cmd_verify, cmd_list, cmd_distrust, cmd_trust; +- + /* + * Verify the trusted certificate against the certificate hashes from platform keystore buffer's + * distrusted list. +@@ -1135,6 +1299,10 @@ load_static_keys (const struct grub_module_header *header, const grub_bool_t is_ + return rc; + } + ++static grub_extcmd_t cmd_distrusted_hash; ++static grub_command_t cmd_verify, cmd_trusted_list, cmd_trusted_cert, cmd_trusted_hash, ++ cmd_distrusted_list, cmd_distrusted_cert; ++ + GRUB_MOD_INIT (appendedsig) + { + int rc; +@@ -1196,17 +1364,31 @@ GRUB_MOD_INIT (appendedsig) + + grub_pks_free_keystore (); + } +- +- 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_trusted_cert = grub_register_command ("trusted_certificate", grub_cmd_trusted_cert, ++ N_("X509_CERTIFICATE"), ++ N_("Add X509_CERTIFICATE to trusted list.")); ++ cmd_trusted_hash = grub_register_command ("trusted_signature", grub_cmd_trusted_hash, ++ N_("BINARY HASH FILE"), ++ N_("Add trusted BINARY HASH to trusted list.")); ++ cmd_distrusted_cert = grub_register_command ("distrusted_certificate", grub_cmd_distrusted_cert, ++ N_("CERT_NUMBER"), ++ N_("Remove CERT_NUMBER (as listed by list_trusted)" ++ " from trusted list.")); ++ cmd_distrusted_hash = grub_register_extcmd ("distrusted_signature", grub_cmd_distrusted_hash, 0, ++ N_("[-b|--binary-hash] FILE [BINARY HASH FILE]\n" ++ "[-c|--cert-hash] FILE [CERTFICATE HASH FILE]"), ++ N_("Add distrusted CERTFICATE/BINARY HASH " ++ "to distrusted list."), ++ options); ++ cmd_trusted_list = grub_register_command ("trusted_list", grub_cmd_trusted_list, 0, ++ N_("Show the list of trusted x509 certificates and" ++ " trusted binary hashes.")); ++ cmd_distrusted_list = grub_register_command ("distrusted_list", grub_cmd_distrusted_list, 0, ++ N_("Show the list of distrusted certificates and" ++ " certificate/binary hashes")); + 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.")); ++ N_("Verify FILE against the trusted x509 certificates/" ++ "trusted binary hashes.")); + + grub_verifier_register (&grub_appendedsig_verifier); + grub_dl_set_persistent (mod); +@@ -1218,10 +1400,12 @@ GRUB_MOD_FINI (appendedsig) + * grub_dl_set_persistent should prevent this from actually running, but + * it does still run under emu. + */ +- + grub_verifier_unregister (&grub_appendedsig_verifier); + grub_unregister_command (cmd_verify); +- grub_unregister_command (cmd_list); +- grub_unregister_command (cmd_trust); +- grub_unregister_command (cmd_distrust); ++ grub_unregister_command (cmd_trusted_list); ++ grub_unregister_command (cmd_distrusted_list); ++ grub_unregister_command (cmd_trusted_cert); ++ grub_unregister_command (cmd_distrusted_cert); ++ grub_unregister_command (cmd_trusted_hash); ++ grub_unregister_extcmd (cmd_distrusted_hash); + } diff --git a/0475-appendedsig-documentation.patch b/0475-appendedsig-documentation.patch new file mode 100644 index 0000000..1d78f42 --- /dev/null +++ b/0475-appendedsig-documentation.patch @@ -0,0 +1,210 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Sudhakar Kuppusamy +Date: Thu, 27 Mar 2025 01:02:42 +0530 +Subject: [PATCH] appendedsig: documentation + +This explains how static and dynamic key appended signatures can be used to form part of +a secure boot chain, and documents the commands and variables introduced. + +Signed-off-by: Sudhakar Kuppusamy +Reviewed-by: Avnish Chouhan +--- + docs/grub.texi | 109 +++++++++++++++++++++++++++++++++++++++------------------ + 1 file changed, 74 insertions(+), 35 deletions(-) + +diff --git a/docs/grub.texi b/docs/grub.texi +index 72cf517..a4ae8cd 100644 +--- a/docs/grub.texi ++++ b/docs/grub.texi +@@ -4016,7 +4016,9 @@ you forget a command, you can run the command @command{help} + * date:: Display or set current date and time + * devicetree:: Load a device tree blob + * distrust:: Remove a pubkey from trusted keys +-* distrust_certificate:: Remove a certificate from the list of trusted certificates ++* distrusted_certificate:: Remove a certificate from the trusted list ++* distrusted_list:: List distrusted certificates and binary/certificate hashes ++* distrusted_signature:: Add a binary hash to the distrusted list + * drivemap:: Map a drive to another + * echo:: Display a line of text + * eval:: Evaluate agruments as GRUB commands +@@ -4033,7 +4035,6 @@ you forget a command, you can run the command @command{help} + * keystatus:: Check key modifier status + * linux:: Load a Linux kernel + * linux16:: Load a Linux kernel (16-bit mode) +-* list_certificates:: List trusted certificates + * list_env:: List variables in environment block + * list_trusted:: List trusted public keys + * load_env:: Load variables from environment block +@@ -4072,7 +4073,9 @@ you forget a command, you can run the command @command{help} + * test:: Check file types and compare values + * true:: Do nothing, successfully + * trust:: Add public key to list of trusted keys +-* trust_certificate:: Add an x509 certificate to the list of trusted certificates ++* trusted_certificate:: Add an x509 certificate to the trusted list ++* trusted_list:: List trusted certificates and binary hashes ++* trusted_signature:: Add a binary hash to the trusted list. + * unset:: Unset an environment variable + @comment * vbeinfo:: List available video modes + * verify_appended:: Verify appended digital signature +@@ -4416,16 +4419,15 @@ These keys are used to validate signatures when environment variable + GPG-style digital signatures}, for more information. + @end deffn + ++@node distrusted_certificate ++@subsection distrusted_certificate + +-@node distrust_certificate +-@subsection distrust_certificate +- +-@deffn Command distrust_certificate cert_number ++@deffn Command distrusted_certificate cert_number + Remove the x509 certificate numbered @var{cert_number} from GRUB's keyring of + trusted x509 certificates for verifying appended signatures. + + @var{cert_number} is the certificate number as listed by +-@command{list_certificates} (@pxref{list_certificates}). ++@command{trusted_list} (@pxref{trusted_list}). + + These certificates are used to validate appended signatures when environment + variable @code{check_appended_signatures} is set to @code{enforce} or +@@ -4434,6 +4436,27 @@ variable @code{check_appended_signatures} is set to @code{enforce} or + @xref{Using appended signatures} for more information. + @end deffn + ++@node distrusted_list ++@subsection distrusted_list ++ ++@deffn Command distrusted_list ++List all the distrusted x509 certificates and binary/certificate hashes. ++The output is a numbered list of certificates and binary/certificate hashes, ++showing the certificate's serial number and Common Name. ++@end deffn ++ ++@node distrusted_signature ++@subsection distrusted_signature ++ ++@deffn Command distrusted_signature ++Read a binary hash from the file @var{binary hash file} ++and add it to GRUB's internal distrusted list. These hash are used to ++restrict validation of linux image integrity using trusted list if appended ++signatures validation failed when the environment variable ++@code{check_appended_signatures} is set to @code{enforce}. ++ ++See @xref{Using appended signatures} for more information. ++@end deffn + + @node drivemap + @subsection drivemap +@@ -4691,22 +4714,6 @@ for the sake of convenience. + This command is only available on x86 systems. + @end deffn + +- +-@node list_certificates +-@subsection list_certificates +- +-@deffn Command list_certificates +-List all x509 certificates trusted by GRUB for validating appended signatures. +-The output is a numbered list of certificates, showing the certificate's serial +-number and Common Name. +- +-The certificate number can be used as an argument to +-@command{distrust_certificate} (@pxref{distrust_certificate}). +- +-See @xref{Using appended signatures} for more information. +-@end deffn +- +- + @node list_env + @subsection list_env + +@@ -5540,10 +5547,10 @@ information. + @end deffn + + +-@node trust_certificate +-@subsection trust_certificate ++@node trusted_certificate ++@subsection trusted_certificate + +-@deffn Command trust_certificate x509_certificate ++@deffn Command trusted_certificate x509_certificate + Read an DER-formatted x509 certificate from the file @var{x509_certificate} + and add it to GRUB's internal list of trusted x509 certificates. These + certificates are used to validate appended signatures when the environment +@@ -5551,7 +5558,7 @@ variable @code{check_appended_signatures} is set to @code{enforce} or + @code{forced}. + + Note that if @code{check_appended_signatures} is set to @code{enforce} or +-@code{forced} when @command{trust_certificate} is executed, then ++@code{forced} when @command{trusted_certificate} is executed, then + @var{x509_certificate} must itself bear an appended signature. (It is not + sufficient that @var{x509_certificate} be signed by a trusted certificate + according to the x509 rules: grub does not include support for validating +@@ -5560,6 +5567,32 @@ signatures within x509 certificates themselves.) + See @xref{Using appended signatures} for more information. + @end deffn + ++@node trusted_list ++@subsection trusted_list ++ ++@deffn Command trusted_list ++List all x509 certificates and binary hases trusted by GRUB for validating ++appended signatures. The output is a numbered list of certificates and binary ++hashes, showing the certificate's serial number and Common Name. ++ ++The certificate number can be used as an argument to ++@command{distrusted_certificate} (@pxref{distrusted_certificate}). ++ ++See @xref{Using appended signatures} for more information. ++@end deffn ++ ++@node trusted_signature ++@subsection trusted_signature ++ ++@deffn Command trust_signature ++Read a binary hash from the file @var{binary hash file} ++and add it to GRUB's internal trusted list. These binary hash are used to ++validate linux image integrity if appended signatures validation failed ++when the environment variable @code{check_appended_signatures} is set ++to @code{enforce}. ++ ++See @xref{Using appended signatures} for more information. ++@end deffn + + @node unset + @subsection unset +@@ -5584,9 +5617,8 @@ only on PC BIOS platforms. + + @deffn Command verify_appended file + Verifies an appended signature on @var{file} against the trusted certificates +-known to GRUB (See @pxref{list_certificates}, @pxref{trust_certificate}, and +-@pxref{distrust_certificate}). +- ++known to GRUB (See @pxref{trusted_list}, @pxref{trusted_certificate}, and ++@pxref{distrusted_certificate}). + Exit code @code{$?} is set to 0 if the signature validates + successfully. If validation fails, it is set to a non-zero value. + See @xref{Using appended signatures}, for more information. +@@ -6183,10 +6215,17 @@ with an appended signature ends with the magic string: + + where @code{\n} represents the line-feed character, @code{0x0a}. + +-Certificates can be managed at boot time using the @pxref{trust_certificate}, +-@pxref{distrust_certificate} and @pxref{list_certificates} commands. +-Certificates can also be built in to the core image using the @code{--x509} +-parameter to @command{grub-install} or @command{grub-mkimage}. ++For static key, Certificates will be built in to the core image using ++the @code{--x509} parameter to @command{grub-install} or @command{grub-mkimage}. ++it can allow to list the trusted certificates and binary hashes at boot time using ++@pxref{trusted_list} and list distrusted certificates and binary/certificate hashes ++at boot time using @pxref{distrusted_list} commands. ++ ++For dynamic key, loads the signature database (DB) and forbidden ++signature database (DBX) from platform keystore (PKS) and it can allow to list ++the trusted certificates and binary hashes at boot time using @pxref{trusted_list} ++and list distrusted certificates and binary/certificate hashes at boot time using ++@pxref{distrusted_list} commands. + + A file can be explictly verified using the @pxref{verify_appended} command. + diff --git a/0476-efi-Add-efitextmode-command-for-getting-setting-the-.patch b/0476-efi-Add-efitextmode-command-for-getting-setting-the-.patch new file mode 100644 index 0000000..0d1d43a --- /dev/null +++ b/0476-efi-Add-efitextmode-command-for-getting-setting-the-.patch @@ -0,0 +1,301 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Glenn Washburn +Date: Fri, 22 Jul 2022 02:16:33 -0500 +Subject: [PATCH] efi: Add efitextmode command for getting/setting the text + mode resolution + +This command is meant to behave similarly to the "mode" command of the EFI +Shell application. In addition to allowing mode selection by giving the +number of columns and rows as arguments, the command allows specifying the +mode number to select the mode. Also supported are the arguments "min" and +"max", which set the mode to the minimum and maximum mode respectively as +calculated by the columns * rows of that mode. + +Signed-off-by: Glenn Washburn +Reviewed-by: Daniel Kiper +--- + docs/grub.texi | 42 ++++++++++ + grub-core/Makefile.core.def | 6 ++ + grub-core/commands/efi/efitextmode.c | 155 +++++++++++++++++++++++++++++++++++ + include/grub/efi/api.h | 6 ++ + include/grub/err.h | 1 + + 5 files changed, 210 insertions(+) + create mode 100644 grub-core/commands/efi/efitextmode.c + +diff --git a/docs/grub.texi b/docs/grub.texi +index a4ae8cd..2677059 100644 +--- a/docs/grub.texi ++++ b/docs/grub.texi +@@ -4021,6 +4021,7 @@ you forget a command, you can run the command @command{help} + * distrusted_signature:: Add a binary hash to the distrusted list + * drivemap:: Map a drive to another + * echo:: Display a line of text ++* efitextmode:: Set/Get text output mode resolution + * eval:: Evaluate agruments as GRUB commands + * export:: Export an environment variable + * false:: Do nothing, unsuccessfully +@@ -4527,6 +4528,47 @@ character will print that character. + @end deffn + + ++@node efitextmode ++@subsection efitextmode ++ ++@deffn Command efitextmode [min | max | | ] ++When used with no arguments displays all available text output modes. The ++set mode determines the columns and rows of the text display when in ++text mode. An asterisk, @samp{*}, will be at the end of the line of the ++currently set mode. ++ ++If given a single parameter, it must be @samp{min}, @samp{max}, or a mode ++number given by the listing when run with no arguments. These arguments set ++the mode to the minimum, maximum, and particular mode respectively. ++ ++Otherwise, the command must be given two numerical arguments specifying the ++columns and rows of the desired mode. Specifying a columns and rows ++combination that corresponds to no supported mode, will return error, but ++otherwise have no effect. ++ ++By default GRUB will start in whatever mode the EFI firmware defaults to. ++There are firmwares known to set up the default mode such that output ++behaves strangely, for example the cursor in the GRUB shell never reaches ++the bottom of the screen or, when typing characters at the prompt, ++characters from previous command output are overwritten. Setting the mode ++may fix this. ++ ++The EFI specification says that mode 0 must be available and have ++columns and rows of 80 and 25 respectively. Mode 1 may be defined and if ++so must have columns and rows of 80 and 50 respectively. Any other modes ++may have columns and rows arbitrarily defined by the firmware. This means ++that a mode with columns and rows of 100 and 31 on one firmware may be ++a different mode number on a different firmware or not exist at all. ++Likewise, mode number 2 on one firmware may have a different number of ++columns and rows than mode 2 on a different firmware. So one should not ++rely on a particular mode number or a mode of a certain number of columns ++and rows existing on all firmwares, except for mode 0. ++ ++Note: This command is only available on EFI platforms and is similar to ++EFI shell "mode" command. ++@end deffn ++ ++ + @node eval + @subsection eval + +diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def +index 218068b..4588fa7 100644 +--- a/grub-core/Makefile.core.def ++++ b/grub-core/Makefile.core.def +@@ -844,6 +844,12 @@ module = { + enable = efi; + }; + ++module = { ++ name = efitextmode; ++ efi = commands/efi/efitextmode.c; ++ enable = efi; ++}; ++ + module = { + name = blocklist; + common = commands/blocklist.c; +diff --git a/grub-core/commands/efi/efitextmode.c b/grub-core/commands/efi/efitextmode.c +new file mode 100644 +index 0000000..3679f6b +--- /dev/null ++++ b/grub-core/commands/efi/efitextmode.c +@@ -0,0 +1,155 @@ ++/* ++ * GRUB -- GRand Unified Bootloader ++ * Copyright (C) 2022 Free Software Foundation, Inc. ++ * ++ * GRUB is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 3 of the License, or ++ * (at your option) any later version. ++ * ++ * GRUB is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with GRUB. If not, see . ++ * ++ * Set/Get UEFI text output mode resolution. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++GRUB_MOD_LICENSE ("GPLv3+"); ++ ++static grub_err_t ++grub_efi_set_mode (grub_efi_simple_text_output_interface_t *o, ++ grub_efi_int32_t mode) ++{ ++ grub_efi_status_t status; ++ ++ if (mode != o->mode->mode) ++ { ++ status = efi_call_2 (o->set_mode, o, mode); ++ if (status == GRUB_EFI_SUCCESS) ++ ; ++ else if (status == GRUB_EFI_DEVICE_ERROR) ++ return grub_error (GRUB_ERR_BAD_DEVICE, ++ N_("device error: could not set requested mode")); ++ else if (status == GRUB_EFI_UNSUPPORTED) ++ return grub_error (GRUB_ERR_OUT_OF_RANGE, ++ N_("invalid mode: number not valid")); ++ else ++ return grub_error (GRUB_ERR_BAD_FIRMWARE, ++ N_("unexpected EFI error number: `%u'"), ++ (unsigned) status); ++ } ++ ++ return GRUB_ERR_NONE; ++} ++ ++static grub_err_t ++grub_cmd_efitextmode (grub_command_t cmd __attribute__ ((unused)), ++ int argc, char **args) ++{ ++ grub_efi_simple_text_output_interface_t *o = grub_efi_system_table->con_out; ++ unsigned long mode; ++ const char *p = NULL; ++ grub_err_t err; ++ grub_efi_uintn_t columns, rows; ++ grub_efi_int32_t i; ++ ++ if (o == NULL) ++ return grub_error (GRUB_ERR_BAD_DEVICE, N_("no UEFI output console interface")); ++ ++ if (o->mode == NULL) ++ return grub_error (GRUB_ERR_BUG, N_("no mode struct for UEFI output console")); ++ ++ if (argc > 2) ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("at most two arguments expected")); ++ ++ if (argc == 0) ++ { ++ grub_printf_ (N_("Available modes for console output device.\n")); ++ ++ for (i = 0; i < o->mode->max_mode; i++) ++ if (GRUB_EFI_SUCCESS == efi_call_4 (o->query_mode, o, i, ++ &columns, &rows)) ++ grub_printf_ (N_(" [%" PRIuGRUB_EFI_UINT32_T "] Col %5" ++ PRIuGRUB_EFI_UINTN_T " Row %5" PRIuGRUB_EFI_UINTN_T ++ " %c\n"), ++ i, columns, rows, (i == o->mode->mode) ? '*' : ' '); ++ } ++ else if (argc == 1) ++ { ++ if (grub_strcmp (args[0], "min") == 0) ++ mode = 0; ++ else if (grub_strcmp (args[0], "max") == 0) ++ mode = o->mode->max_mode - 1; ++ else ++ { ++ mode = grub_strtoul (args[0], &p, 0); ++ ++ if (*args[0] == '\0' || *p != '\0') ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, ++ N_("non-numeric or invalid mode `%s'"), args[0]); ++ } ++ ++ if (mode < (unsigned long) o->mode->max_mode) ++ { ++ err = grub_efi_set_mode (o, (grub_efi_int32_t) mode); ++ if (err != GRUB_ERR_NONE) ++ return err; ++ } ++ else ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, ++ N_("invalid mode: `%lu' is greater than maximum mode `%lu'"), ++ mode, (unsigned long) o->mode->max_mode); ++ } ++ else if (argc == 2) ++ { ++ grub_efi_uintn_t u_columns, u_rows; ++ ++ u_columns = (grub_efi_uintn_t) grub_strtoul (args[0], &p, 0); ++ ++ if (*args[0] == '\0' || *p != '\0') ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, ++ N_("non-numeric or invalid columns number `%s'"), args[0]); ++ ++ u_rows = (grub_efi_uintn_t) grub_strtoul (args[1], &p, 0); ++ ++ if (*args[1] == '\0' || *p != '\0') ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, ++ N_("non-numeric or invalid rows number `%s'"), args[1]); ++ ++ for (i = 0; i < o->mode->max_mode; i++) ++ if (GRUB_EFI_SUCCESS == efi_call_4 (o->query_mode, o, i, ++ &columns, &rows)) ++ if (u_columns == columns && u_rows == rows) ++ return grub_efi_set_mode (o, (grub_efi_int32_t) i); ++ ++ return grub_error (GRUB_ERR_BAD_ARGUMENT, ++ N_("no mode found with requested columns and rows")); ++ } ++ ++ return GRUB_ERR_NONE; ++} ++ ++static grub_command_t cmd; ++GRUB_MOD_INIT (efitextmode) ++{ ++ cmd = grub_register_command ("efitextmode", grub_cmd_efitextmode, ++ N_("[min | max | | ]"), ++ N_("Get or set EFI text mode.")); ++} ++ ++GRUB_MOD_FINI (efitextmode) ++{ ++ grub_unregister_command (cmd); ++} +diff --git a/include/grub/efi/api.h b/include/grub/efi/api.h +index 464842b..f224037 100644 +--- a/include/grub/efi/api.h ++++ b/include/grub/efi/api.h +@@ -545,9 +545,13 @@ typedef char grub_efi_boolean_t; + #if GRUB_CPU_SIZEOF_VOID_P == 8 + typedef grub_int64_t grub_efi_intn_t; + typedef grub_uint64_t grub_efi_uintn_t; ++#define PRIxGRUB_EFI_UINTN_T PRIxGRUB_UINT64_T ++#define PRIuGRUB_EFI_UINTN_T PRIuGRUB_UINT64_T + #else + typedef grub_int32_t grub_efi_intn_t; + typedef grub_uint32_t grub_efi_uintn_t; ++#define PRIxGRUB_EFI_UINTN_T PRIxGRUB_UINT32_T ++#define PRIuGRUB_EFI_UINTN_T PRIuGRUB_UINT32_T + #endif + typedef grub_int8_t grub_efi_int8_t; + typedef grub_uint8_t grub_efi_uint8_t; +@@ -555,6 +559,8 @@ typedef grub_int16_t grub_efi_int16_t; + typedef grub_uint16_t grub_efi_uint16_t; + typedef grub_int32_t grub_efi_int32_t; + typedef grub_uint32_t grub_efi_uint32_t; ++#define PRIxGRUB_EFI_UINT32_T PRIxGRUB_UINT32_T ++#define PRIuGRUB_EFI_UINT32_T PRIuGRUB_UINT32_T + typedef grub_int64_t grub_efi_int64_t; + typedef grub_uint64_t grub_efi_uint64_t; + typedef grub_uint8_t grub_efi_char8_t; +diff --git a/include/grub/err.h b/include/grub/err.h +index 905a6df..7530f58 100644 +--- a/include/grub/err.h ++++ b/include/grub/err.h +@@ -73,6 +73,7 @@ typedef enum + GRUB_ERR_NET_NO_DOMAIN, + GRUB_ERR_EOF, + GRUB_ERR_BAD_SIGNATURE, ++ GRUB_ERR_BAD_FIRMWARE, + GRUB_ERR_STILL_REFERENCED, + GRUB_ERR_RECURSION_DEPTH + } diff --git a/grub.patches b/grub.patches index c3cfa95..dfb943d 100644 --- a/grub.patches +++ b/grub.patches @@ -463,3 +463,13 @@ Patch0463: 0463-fs-xfs-Handle-non-continuous-data-blocks-in-director.patch Patch0464: 0464-fs-xfs-fix-large-extent-counters-incompat-feature-su.patch Patch0465: 0465-grub-mkimage-Create-new-ELF-note-for-SBAT.patch Patch0466: 0466-grub-mkimage-Add-SBAT-metadata-into-ELF-note-for-Pow.patch +Patch0467: 0467-appended-sig-sync-d-with-upstream-code.patch +Patch0468: 0468-ieee1275-Platform-Keystore-PKS-Support.patch +Patch0469: 0469-ieee1275-Read-the-DB-and-DBX-secure-boot-variables.patch +Patch0470: 0470-appendedsig-The-creation-of-trusted-and-distrusted-l.patch +Patch0471: 0471-appendedsig-While-verifying-the-kernel-use-trusted-a.patch +Patch0472: 0472-powerpc_ieee1275-set-use_static_keys-flag.patch +Patch0473: 0473-appendedsig-Reads-the-default-DB-keys-from-ELF-Note.patch +Patch0474: 0474-appendedsig-The-grub-command-s-trusted-and-distruste.patch +Patch0475: 0475-appendedsig-documentation.patch +Patch0476: 0476-efi-Add-efitextmode-command-for-getting-setting-the-.patch diff --git a/grub2.spec b/grub2.spec index 95161a4..b6e9c65 100644 --- a/grub2.spec +++ b/grub2.spec @@ -16,7 +16,7 @@ Name: grub2 Epoch: 1 Version: 2.06 -Release: 107%{?dist} +Release: 108%{?dist} Summary: Bootloader with support for Linux, Multiboot and more License: GPLv3+ URL: http://www.gnu.org/software/grub/ @@ -547,6 +547,10 @@ mv ${EFI_HOME}/grub.cfg.stb ${EFI_HOME}/grub.cfg %endif %changelog +* Wed May 21 2025 Nicolas Frayer - 2.06-108 +- ieee1275: Appended signature support +- Resolves: #RHEL-24742 + * Wed May 14 2025 Nicolas Frayer - 2.06-107 - Remove BLS fake config in case of kernel removal - Resolves: #RHEL-83915