On Wed, Dec 11, 2013 at 03:26:16PM +0800, Lee, Chun-Yi wrote: > This patch introduces a blacklist list of kernel module's hash. It check > the blacklist before checking kernel module signature. > It didn't limit what hash algorithm used but the module of hash algorithm > need build-in or put in initrd for verify kernel module in initrd. > > Signed-off-by: Lee, Chun-Yi <jlee@xxxxxxxx> > --- > kernel/module-internal.h | 14 ++++++++ > kernel/module.c | 9 +++++- > kernel/module_signing.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 101 insertions(+), 1 deletions(-) > > diff --git a/kernel/module-internal.h b/kernel/module-internal.h > index 915e123..f1b6477 100644 > --- a/kernel/module-internal.h > +++ b/kernel/module-internal.h > @@ -9,4 +9,18 @@ > * 2 of the Licence, or (at your option) any later version. > */ > > +/* > + * Module hash. > + */ > +struct module_hash { > + struct list_head list; /* list of all hashs */ > + u8 hash; /* Hash algorithm [enum pkey_hash_algo] */ > + char *hash_name; /* nams string of hash */ > + size_t size; /* size of hash */ > + u8 hash_data[]; /* Hash data */ > +}; > + > +extern struct list_head module_hash_blacklist; > + > +extern int mod_verify_hash(const void *mod, unsigned long modlen); > extern int mod_verify_sig(const void *mod, unsigned long *_modlen); > diff --git a/kernel/module.c b/kernel/module.c > index 3305511..dd06d8a 100644 > --- a/kernel/module.c > +++ b/kernel/module.c > @@ -2562,10 +2562,16 @@ static inline void kmemleak_load_module(const struct module *mod, > #ifdef CONFIG_MODULE_SIG > static int module_sig_check(struct load_info *info) > { > - int err = -ENOKEY; > + int err; > const unsigned long markerlen = sizeof(MODULE_SIG_STRING) - 1; > const void *mod = info->hdr; > > + /* check hash of module in blacklist */ > + err = mod_verify_hash(mod, info->len); > + if (err) > + goto match_blacklist_hash; > + > + err = -ENOKEY; > if (info->len > markerlen && > memcmp(mod + info->len - markerlen, MODULE_SIG_STRING, markerlen) == 0) { > /* We truncate the module to discard the signature */ > @@ -2578,6 +2584,7 @@ static int module_sig_check(struct load_info *info) > return 0; > } > > +match_blacklist_hash: > /* Not having a signature is only an error if we're strict. */ > if (err < 0 && fips_enabled) > panic("Module verification failed with error %d in FIPS mode\n", > diff --git a/kernel/module_signing.c b/kernel/module_signing.c > index 0a29b40..cd7f441 100644 > --- a/kernel/module_signing.c > +++ b/kernel/module_signing.c > @@ -11,12 +11,15 @@ > > #include <linux/kernel.h> > #include <linux/err.h> > +#include <linux/module.h> > #include <crypto/public_key.h> > #include <crypto/hash.h> > #include <keys/asymmetric-type.h> > #include <keys/system_keyring.h> > #include "module-internal.h" > > +LIST_HEAD(module_hash_blacklist); > + > /* > * Module signature information block. > * > @@ -193,6 +196,82 @@ static struct key *request_asymmetric_key(const char *signer, size_t signer_len, > return key_ref_to_ptr(key); > } > > +int mod_verify_hash(const void *mod, unsigned long modlen) > +{ > + struct module_signature ms; > + struct module_hash *module_hash; > + struct crypto_shash *tfm; > + struct shash_desc *desc; > + size_t digest_size, desc_size; > + size_t sig_len; > + u8 *digest; > + int ret = 0; > + > + const unsigned long markerlen = sizeof(MODULE_SIG_STRING) - 1; > + > + /* truncate the module to discard the signature when it signed */ > + if (modlen > markerlen && > + memcmp(mod + modlen - markerlen, MODULE_SIG_STRING, markerlen) == 0) { > + modlen -= markerlen; > + if (modlen <= sizeof(ms)) > + return -EBADMSG; > + memcpy(&ms, mod + (modlen - sizeof(ms)), sizeof(ms)); > + modlen -= sizeof(ms); > + sig_len = be32_to_cpu(ms.sig_len); > + if (sig_len >= modlen) > + return -EBADMSG; > + modlen -= sig_len; > + if ((size_t)ms.signer_len + ms.key_id_len >= modlen) > + return -EBADMSG; > + modlen -= (size_t)ms.signer_len + ms.key_id_len; > + } Hm. Why do we discard the signature before we calculate the hash? It seems we might need to check for a hash of both the signed and unsigned module, correct? > + > + list_for_each_entry(module_hash, &module_hash_blacklist, list) { > + tfm = crypto_alloc_shash(module_hash->hash_name, 0, 0); > + if (IS_ERR(tfm)) { > + printk_once(KERN_WARNING "The %s hash algorithm did " > + "not load for check blacklisted module hash: " > + "%*phN\n", module_hash->hash_name, > + (int) module_hash->size, > + module_hash->hash_data); "Cannot check for blacklisted module hash. The %s hash algorithm did not load." might read better. > + continue; > + } > + > + desc_size = crypto_shash_descsize(tfm) + sizeof(*desc); > + digest_size = crypto_shash_digestsize(tfm); > + digest = kzalloc(digest_size + desc_size, GFP_KERNEL); > + if (!digest) { > + pr_err("digest memory buffer allocate fail\n"); > + ret = -ENOMEM; > + goto error_digest; > + } > + desc = (void *)digest + digest_size; > + desc->tfm = tfm; > + desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP; > + ret = crypto_shash_init(desc); > + if (ret < 0) > + goto error_shash; > + > + ret = crypto_shash_finup(desc, mod, modlen, digest); > + if (ret < 0) > + goto error_shash; > + > + if (!memcmp(digest, module_hash->hash_data, digest_size)) { > + ret = -EKEYREJECTED; > + pr_info("Module Hash is in MOKx blacklisted: %*phN\n", > + (int) module_hash->size, module_hash->hash_data); Maybe change the message to "Module hash is in the module hash blacklist:". josh _______________________________________________ kernel mailing list kernel@xxxxxxxxxxxxxxxxxxxxxxx https://admin.fedoraproject.org/mailman/listinfo/kernel