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(a)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