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. > 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) > +/* > + * 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/ > + 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... > + 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... > + 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... > + 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