[PATCH 1/2] MODSIGN: check hash of kernel module in blacklist
Josh Boyer
jwboyer at redhat.com
Wed Dec 11 16:07:56 UTC 2013
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 at suse.com>
> ---
> 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
More information about the kernel
mailing list