From f474a49bfdeee08e07666f66f4a3a38153fc4d83 Mon Sep 17 00:00:00 2001
From: "Lee, Chun-Yi" <jlee@suse.com>
Date: Wed, 25 May 2016 22:14:56 +0800
Subject: [PATCH] MODSIGN: check hash of PKCS#7 signed kernel module in
blacklist
Patch-mainline: Not yet, waiting Matthew Garrett's BSD-style securelevel
References: fate#319460
This patch checks the hash of kernel module that it should not be in
blacklist. It will stripped the PKCS#7 signature of kernel module then
compare the hash of kernel module with the hash numbers in MOKx.
Signed-off-by: Lee, Chun-Yi <jlee@suse.com>
---
kernel/module_signing.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 83 insertions(+), 2 deletions(-)
--- a/kernel/module_signing.c
+++ b/kernel/module_signing.c
@@ -47,19 +47,24 @@ static int old_mod_verify_sig(const void
static int mod_verify_hash(const void *mod, unsigned long modlen,
struct public_key_signature *pks);
+static int new_check_blacklist(const void *mod, size_t wholelen,
+ size_t modlen);
+
/*
* Verify the signature on a module.
*/
int mod_verify_sig(const void *mod, unsigned long *_modlen)
{
struct module_signature ms;
- size_t modlen = *_modlen, sig_len;
+ size_t modlen = *_modlen, sig_len, wholelen;
+ int ret;
pr_devel("==>%s(,%zu)\n", __func__, modlen);
if (modlen <= sizeof(ms))
return -EBADMSG;
+ wholelen = modlen + sizeof(MODULE_SIG_STRING) - 1;
memcpy(&ms, mod + (modlen - sizeof(ms)), sizeof(ms));
modlen -= sizeof(ms);
*_modlen = modlen;
@@ -91,8 +96,15 @@ int mod_verify_sig(const void *mod, unsi
return -EBADMSG;
}
- return system_verify_data(mod, modlen, mod + modlen, sig_len,
+ ret = system_verify_data(mod, modlen, mod + modlen, sig_len,
VERIFYING_MODULE_SIGNATURE);
+ pr_devel("system_verify_data() = %d\n", ret);
+
+ /* check hash of module not in blacklist */
+ if (!ret)
+ ret = new_check_blacklist(mod, wholelen, modlen);
+
+ return ret;
}
/*
@@ -331,6 +343,75 @@ error_return:
return ret;
}
+static int new_mod_verify_hash(const void *mod, size_t verifylen,
+ struct module_hash *module_hash)
+{
+ struct crypto_shash *tfm;
+ struct shash_desc *desc;
+ size_t digest_size, desc_size;
+ u8 *digest;
+ int ret = 0;
+
+ tfm = crypto_alloc_shash(module_hash->hash_name, 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, verifylen, digest);
+ if (ret < 0)
+ goto error_shash;
+
+ pr_debug("%ld digest: %*phN\n", verifylen, (int) digest_size, digest);
+
+ if (!memcmp(digest, 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);
+ }
+
+error_shash:
+ kfree(digest);
+error_digest:
+ crypto_free_shash(tfm);
+error_return:
+ return ret;
+}
+
+static int new_check_blacklist(const void *mod, size_t wholelen, size_t modlen)
+{
+ struct module_hash *module_hash;
+ int ret = 0;
+
+ if (list_empty(&module_hash_blacklist))
+ return 0;
+
+ list_for_each_entry(module_hash, &module_hash_blacklist, list) {
+ /* compare hash of whole kernel module */
+ ret = new_mod_verify_hash(mod, wholelen, module_hash);
+
+ /* compare hash of kernel module without signature */
+ if (!ret)
+ ret = new_mod_verify_hash(mod, modlen, module_hash);
+ }
+
+ return ret;
+}
+
/*
* Verify the signature on a module
*/