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 <jlee@xxxxxxxx> --- 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 kernel@xxxxxxxxxxxxxxxxxxxxxxx https://admin.fedoraproject.org/mailman/listinfo/kernel