Thank you Daniel for the review. > On 21 Aug 2025, at 8:53 PM, Daniel Kiper <dki...@net-space.pl> wrote: > > On Thu, Aug 21, 2025 at 01:25:03PM +0530, Sudhakar Kuppusamy wrote: >> Building on the parsers and the ability to embed X.509 certificates, as >> well as the existing gcrypt functionality, add a module for verifying >> appended signatures. >> >> This includes a verifier that requires that Linux kernels and >> GRUB modules have appended signatures, and commands to manage the >> list of trusted certificates for verification. > > This suggests that this patch should be split into two separate ones. I will split the patch into two separate ones. > >> Verification must be enabled by setting check_appended_signatures. If >> secure boot is enabled with enforced mode when the module is loaded, >> verification will be enabled and locked automatically. If verification >> is enabled, extract trusted keys from the GRUB ELF Note and store them in >> the db. >> >> As with the PGP verifier, it is not a complete secure-boot solution: >> other mechanisms, such as a password or lockdown, must be used to ensure >> that a user cannot drop to the GRUB shell and disable verification. >> >> Introducing the following GRUB commands to access db. >> >> 1. append_list_db: >> Show the list of trusted certificates from the db list >> 2. append_add_db_cert: >> Add the trusted certificate to the db list >> 3. append_rm_dbx_cert: >> Remove the distrusted certificate from the db list >> 4. append_verify: >> Verify the signed file using db list >> >> Signed-off-by: Daniel Axtens <d...@axtens.net> >> Signed-off-by: Sudhakar Kuppusamy <sudha...@linux.ibm.com> >> Reviewed-by: Stefan Berger <stef...@linux.ibm.com> >> Reviewed-by: Avnish Chouhan <avn...@linux.ibm.com> >> --- >> grub-core/Makefile.core.def | 15 + >> grub-core/commands/appendedsig/appendedsig.c | 793 +++++++++++++++++++ >> include/grub/err.h | 3 +- >> include/grub/file.h | 2 + >> 4 files changed, 812 insertions(+), 1 deletion(-) >> create mode 100644 grub-core/commands/appendedsig/appendedsig.c >> >> diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def >> index b72f322b1..d91694de0 100644 >> --- a/grub-core/Makefile.core.def >> +++ b/grub-core/Makefile.core.def >> @@ -980,6 +980,21 @@ module = { >> cppflags = '$(CPPFLAGS_GCRY)'; >> }; >> >> +module = { >> + name = appendedsig; >> + common = commands/appendedsig/appendedsig.c; >> + common = commands/appendedsig/x509.c; >> + common = commands/appendedsig/pkcs7.c; >> + common = commands/appendedsig/asn1util.c; >> + common = commands/appendedsig/gnutls_asn1_tab.c; >> + common = commands/appendedsig/pkix_asn1_tab.c; >> + enable = emu; >> + enable = powerpc_ieee1275; >> + cflags = '$(CFLAGS_GCRY) -Wno-redundant-decls'; >> + cppflags = '$(CPPFLAGS_GCRY) -I$(srcdir)/lib/libtasn1-grub'; >> + depends = crypto, gcry_rsa, gcry_sha256, gcry_sha512, mpi, asn1; >> +}; >> + >> module = { >> name = hdparm; >> common = commands/hdparm.c; >> diff --git a/grub-core/commands/appendedsig/appendedsig.c >> b/grub-core/commands/appendedsig/appendedsig.c >> new file mode 100644 >> index 000000000..2ddbb3a15 >> --- /dev/null >> +++ b/grub-core/commands/appendedsig/appendedsig.c >> @@ -0,0 +1,793 @@ >> +/* >> + * GRUB -- GRand Unified Bootloader >> + * Copyright (C) 2020, 2021, 2022 Free Software Foundation, Inc. >> + * Copyright (C) 2020, 2021, 2022, 2025 IBM Corporation >> + * >> + * GRUB is free software: you can redistribute it and/or modify >> + * it under the terms of the GNU General Public License as published by >> + * 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 <http://www.gnu.org/licenses/>. >> + */ >> + >> +#include <grub/types.h> >> +#include <grub/misc.h> >> +#include <grub/mm.h> >> +#include <grub/err.h> >> +#include <grub/dl.h> >> +#include <grub/file.h> >> +#include <grub/command.h> >> +#include <grub/crypto.h> >> +#include <grub/i18n.h> >> +#include <grub/gcrypt/gcrypt.h> >> +#include <grub/kernel.h> >> +#include <grub/extcmd.h> >> +#include <grub/verify.h> >> +#include <libtasn1.h> >> +#include <grub/env.h> >> +#include <grub/lockdown.h> >> + >> +#include "appendedsig.h" >> + >> +GRUB_MOD_LICENSE ("GPLv3+"); >> + >> +/* Public key type. */ >> +#define GRUB_PKEY_ID_PKCS7 2 >> + >> +/* Appended signature magic string. */ >> +static const char magic[] = "~Module signature appended~\n"; > > This could be a constant... Two useful constants... > > #define SIG_MAGIC "~Module signature appended~\n" > #define SIG_MAGIC_SIZE ((sizeof(SIG_MAGIC) - 1) Sure, will do it. > >> +/* >> + * This structure is extracted from scripts/sign-file.c in the linux kernel >> + * source. It was licensed as LGPLv2.1+, which is GPLv3+ compatible. >> + */ >> +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 [GRUB_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_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. */ >> +}; >> + >> +/* This represents a trusted certificates. */ >> +struct grub_database >> +{ >> + struct x509_certificate *certs; /* Certificates. */ >> + grub_uint32_t cert_entries; /* Number of certificates. */ >> +}; >> + >> +/* The db list is used to validate appended signatures. */ >> +struct grub_database db = {.certs = NULL, .cert_entries = 0}; >> + >> +/* Appended signature size. */ >> +static grub_size_t append_sig_len = 0; >> + >> +/* >> + * Signature verification flag (check_sigs). >> + * check_sigs: false >> + * - No signature verification. This is the default. >> + * check_sigs: true >> + * - Enforce signature verification, and if signature verification fails, >> + * post the errors and stop the boot. >> + */ >> +static bool check_sigs = false; >> + >> +static void >> +register_appended_signatures_cmd (void); >> +static void >> +unregister_appended_signatures_cmd (void); >> +static void >> +free_db_list (void); >> +static void >> +build_static_db_list (void); >> + >> +static const char * >> +grub_env_read_sec (struct grub_env_var *var __attribute__ ((unused)), >> + const char *val __attribute__ ((unused))) >> +{ >> + if (check_sigs == true) >> + return "enforce"; >> + >> + return "no"; >> +} >> + >> +static char * >> +grub_env_write_sec (struct grub_env_var *var __attribute__ ((unused)), >> const char *val) >> +{ >> + char *ret; >> + >> + /* >> + * Do not allow the value to be changed If signature verification is >> + * (check_sigs is set to enforce) enabled and GRUB is locked down. >> + */ >> + if (check_sigs == true && grub_is_lockdown () == GRUB_LOCKDOWN_ENABLED) >> + { >> + ret = grub_strdup ("enforce"); >> + if (ret == NULL) >> + grub_error (GRUB_ERR_OUT_OF_MEMORY, "could not duplicate a string >> enforce"); >> + >> + return ret; >> + } >> + >> + if ((*val == '1') || (*val == 'e')) >> + check_sigs = true; >> + else if ((*val == '0') || (*val == 'n')) >> + check_sigs = false; >> + >> + ret = grub_strdup (grub_env_read_sec (NULL, NULL)); >> + if (ret == NULL) >> + grub_error (GRUB_ERR_OUT_OF_MEMORY, "could not duplicate a string %s", >> + grub_env_read_sec (NULL, NULL)); >> + >> + return ret; >> +} >> + >> +static bool >> +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->issuer, db_cert->issuer, >> db_cert->issuer_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 true; >> + >> + return false; >> +} >> + >> +static grub_err_t >> +file_read_whole (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, >> + "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, >> + "file is too large to read: %" PRIuGRUB_UINT64_T " >> bytes", > > s/PRIuGRUB_UINT64_T/PRIuGRUB_OFFSET/ will change it. > >> + full_file_size); >> + >> + file_size = (grub_size_t) full_file_size; >> + *buf = grub_malloc (file_size); >> + if (*buf == NULL) >> + return grub_error (GRUB_ERR_OUT_OF_MEMORY, >> + "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, >> + "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 >> +extract_appended_signature (const grub_uint8_t *buf, grub_size_t bufsize, >> + struct grub_appended_signature *sig) >> +{ >> + grub_size_t pkcs7_size; >> + grub_size_t remaining_len; >> + const grub_uint8_t *appsigdata = buf + bufsize - grub_strlen (magic); > > s/grub_strlen (magic)/sizeof (magic) - 1/ > >> + >> + if (bufsize < grub_strlen (magic)) > > s/grub_strlen (magic)/sizeof (magic) - 1/ > > Please be more consistent… Sure, will do it. > >> + return grub_error (GRUB_ERR_BAD_SIGNATURE, "file too short for >> signature magic"); >> + >> + if (grub_strncmp ((const char *) appsigdata, magic, sizeof (magic) - 1)) >> + return grub_error (GRUB_ERR_BAD_SIGNATURE, "missing or invalid >> signature magic"); >> + >> + remaining_len = bufsize - grub_strlen (magic); > > Ditto... > > Or even define a constant, as above, and use it everywhere... Sure, will do it. > >> + if (remaining_len < sizeof (struct module_signature)) >> + return grub_error (GRUB_ERR_BAD_SIGNATURE, "file too short for >> signature metadata"); >> + >> + appsigdata -= sizeof (struct module_signature); >> + /* Extract the metadata. */ >> + grub_memcpy (&(sig->sig_metadata), appsigdata, sizeof (struct >> module_signature)); >> + remaining_len -= sizeof (struct module_signature); >> + >> + if (sig->sig_metadata.id_type != GRUB_PKEY_ID_PKCS7) >> + return grub_error (GRUB_ERR_BAD_SIGNATURE, "wrong signature type"); >> + >> + pkcs7_size = grub_be_to_cpu32 (sig->sig_metadata.sig_len); >> + >> + if (pkcs7_size > remaining_len) >> + return grub_error (GRUB_ERR_BAD_SIGNATURE, "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; > > Ditto… > >> + /* Rewind pointer and parse pkcs7 data. */ >> + appsigdata -= pkcs7_size; >> + >> + return parse_pkcs7_signedData (appsigdata, pkcs7_size, &sig->pkcs7); >> +} >> + >> +/* >> + * Given a hash value 'hval', of hash specification 'hash', prepare >> + * the S-expressions (sexp) and perform the signature verification. >> + */ >> +static grub_err_t >> +verify_signature (const gcry_mpi_t *pkmpi, const gcry_mpi_t hmpi, >> + const gcry_md_spec_t *hash, const grub_uint8_t *hval) >> +{ >> + gcry_sexp_t hsexp, pubkey, sig; >> + grub_size_t errof; >> + >> + if (_gcry_sexp_build(&hsexp, &errof, "(data (flags %s) (hash %s %b))", >> "pkcs1", >> + hash->name, hash->mdlen, hval) != GPG_ERR_NO_ERROR) > > Missing space after "_gcry_sexp_build" here and below… Sure, will do it.
Thanks, Sudhakar > >> + return GRUB_ERR_BAD_SIGNATURE; >> + >> + if (_gcry_sexp_build(&pubkey, &errof, "(public-key (dsa (n %M) (e %M)))", >> + pkmpi[0], pkmpi[1]) != GPG_ERR_NO_ERROR) >> + return GRUB_ERR_BAD_SIGNATURE; >> + >> + if (_gcry_sexp_build(&sig, &errof, "(sig-val (rsa (s %M)))", hmpi) != >> GPG_ERR_NO_ERROR) >> + return GRUB_ERR_BAD_SIGNATURE; >> + >> + _gcry_sexp_dump(sig); >> + _gcry_sexp_dump(hsexp); >> + _gcry_sexp_dump(pubkey); >> + >> + if (grub_crypto_pk_rsa->verify (sig, hsexp, pubkey) != GPG_ERR_NO_ERROR) >> + return GRUB_ERR_BAD_SIGNATURE; >> + >> + return GRUB_ERR_NONE; >> +} > > Daniel _______________________________________________ Grub-devel mailing list Grub-devel@gnu.org https://lists.gnu.org/mailman/listinfo/grub-devel