Blob Blame History Raw
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
  */