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.

v2:
 + Use the digest generated by mod_make_digest() to avoid computing the hash 
twice.
 + Also check the hash of whole module file is not in the module hash blacklist.
 + Change the statements of information log.

Signed-off-by: Lee, Chun-Yi <[email protected]>
---
 kernel/module-internal.h |   14 ++++++++
 kernel/module.c          |    9 +++++-
 kernel/module_signing.c  |   79 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 101 insertions(+), 1 deletions(-)

Index: linux-3.12-SLE12/kernel/module-internal.h
===================================================================
--- linux-3.12-SLE12.orig/kernel/module-internal.h
+++ linux-3.12-SLE12/kernel/module-internal.h
@@ -9,4 +9,17 @@
  * 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_sig(const void *mod, unsigned long *_modlen);
Index: linux-3.12-SLE12/kernel/module_signing.c
===================================================================
--- linux-3.12-SLE12.orig/kernel/module_signing.c
+++ linux-3.12-SLE12/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_ke
        return key_ref_to_ptr(key);
 }
 
+static int check_blacklist(const char *hash_algo_name, const void *hash)
+{
+       struct module_hash *module_hash;
+       int ret = 0;
+
+       list_for_each_entry(module_hash, &module_hash_blacklist, list) {
+               if (strcmp(hash_algo_name, module_hash->hash_name))
+                       continue;
+
+               if (!memcmp(hash, module_hash->hash_data, module_hash->size)) {
+                       ret = -EKEYREJECTED;
+                       pr_info("Module hash is in the module hash blacklist: "
+                               "%*phN\n", (int)module_hash->size,
+                               module_hash->hash_data);
+                       break;
+               }
+       }
+
+       return ret;
+}
+
+static int mod_verify_hash(const void *mod, unsigned long modlen,
+               struct public_key_signature *pks)
+{
+       const char *pks_hash_algo = pkey_hash_algo_name[pks->pkey_hash_algo];
+       struct crypto_shash *tfm;
+       struct shash_desc *desc;
+       size_t digest_size, desc_size;
+       u8 *digest;
+       int ret = 0;
+
+       if (list_empty(&module_hash_blacklist))
+               return 0;
+
+       /* check digest of module is not in hash blacklist */
+       ret = check_blacklist(pks_hash_algo, pks->digest);
+       if (ret)
+               goto error_return;
+
+       /* check hash of whole module file */
+       tfm = crypto_alloc_shash(pks_hash_algo, 0, 0);
+       if (IS_ERR(tfm))
+               goto error_return;
+
+       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;
+
+       pr_debug("%ld digest: %*phN\n", modlen, (int) digest_size, digest);
+
+       /* check the hash of whole module file including signature */
+       ret = check_blacklist(pks_hash_algo, digest);
+
+error_shash:
+       kfree(digest);
+error_digest:
+       crypto_free_shash(tfm);
+error_return:
+       return ret;
+}
+
 /*
  * Verify the signature on a module.
  */
@@ -202,7 +281,7 @@ int mod_verify_sig(const void *mod, unsi
        struct module_signature ms;
        struct key *key;
        const void *sig;
-       size_t modlen = *_modlen, sig_len;
+       size_t modlen = *_modlen, sig_len, wholelen;
        int ret;
 
        pr_devel("==>%s(,%zu)\n", __func__, modlen);
@@ -210,6 +289,7 @@ int mod_verify_sig(const void *mod, unsi
        if (modlen <= sizeof(ms))
                return -EBADMSG;
 
+       wholelen = modlen + sizeof(MODULE_SIG_STRING) - 1;
        memcpy(&ms, mod + (modlen - sizeof(ms)), sizeof(ms));
        modlen -= sizeof(ms);
 
@@ -252,6 +332,10 @@ int mod_verify_sig(const void *mod, unsi
        ret = verify_signature(key, pks);
        pr_devel("verify_signature() = %d\n", ret);
 
+       /* check hash of module not in blacklist */
+       if (!ret)
+               ret = mod_verify_hash(mod, wholelen, pks);
+
 error_free_pks:
        mpi_free(pks->rsa.s);
        kfree(pks);
_______________________________________________
kernel mailing list
[email protected]
https://admin.fedoraproject.org/mailman/listinfo/kernel

Reply via email to