| /* | 
 |  * Copyright (C) 2013 Intel Corporation | 
 |  * | 
 |  * Author: | 
 |  * Dmitry Kasatkin <dmitry.kasatkin@intel.com> | 
 |  * | 
 |  * This program is free software; you can redistribute it and/or modify | 
 |  * it under the terms of the GNU General Public License as published by | 
 |  * the Free Software Foundation, version 2 of the License. | 
 |  * | 
 |  */ | 
 |  | 
 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | 
 |  | 
 | #include <linux/err.h> | 
 | #include <linux/ratelimit.h> | 
 | #include <linux/key-type.h> | 
 | #include <crypto/public_key.h> | 
 | #include <crypto/hash_info.h> | 
 | #include <keys/asymmetric-type.h> | 
 | #include <keys/system_keyring.h> | 
 |  | 
 | #include "integrity.h" | 
 |  | 
 | /* | 
 |  * Request an asymmetric key. | 
 |  */ | 
 | static struct key *request_asymmetric_key(struct key *keyring, uint32_t keyid) | 
 | { | 
 | 	struct key *key; | 
 | 	char name[12]; | 
 |  | 
 | 	sprintf(name, "id:%08x", keyid); | 
 |  | 
 | 	pr_debug("key search: \"%s\"\n", name); | 
 |  | 
 | 	key = get_ima_blacklist_keyring(); | 
 | 	if (key) { | 
 | 		key_ref_t kref; | 
 |  | 
 | 		kref = keyring_search(make_key_ref(key, 1), | 
 | 				     &key_type_asymmetric, name); | 
 | 		if (!IS_ERR(kref)) { | 
 | 			pr_err("Key '%s' is in ima_blacklist_keyring\n", name); | 
 | 			return ERR_PTR(-EKEYREJECTED); | 
 | 		} | 
 | 	} | 
 |  | 
 | 	if (keyring) { | 
 | 		/* search in specific keyring */ | 
 | 		key_ref_t kref; | 
 |  | 
 | 		kref = keyring_search(make_key_ref(keyring, 1), | 
 | 				      &key_type_asymmetric, name); | 
 | 		if (IS_ERR(kref)) | 
 | 			key = ERR_CAST(kref); | 
 | 		else | 
 | 			key = key_ref_to_ptr(kref); | 
 | 	} else { | 
 | 		key = request_key(&key_type_asymmetric, name, NULL); | 
 | 	} | 
 |  | 
 | 	if (IS_ERR(key)) { | 
 | 		pr_err_ratelimited("Request for unknown key '%s' err %ld\n", | 
 | 				   name, PTR_ERR(key)); | 
 | 		switch (PTR_ERR(key)) { | 
 | 			/* Hide some search errors */ | 
 | 		case -EACCES: | 
 | 		case -ENOTDIR: | 
 | 		case -EAGAIN: | 
 | 			return ERR_PTR(-ENOKEY); | 
 | 		default: | 
 | 			return key; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	pr_debug("%s() = 0 [%x]\n", __func__, key_serial(key)); | 
 |  | 
 | 	return key; | 
 | } | 
 |  | 
 | int asymmetric_verify(struct key *keyring, const char *sig, | 
 | 		      int siglen, const char *data, int datalen) | 
 | { | 
 | 	struct public_key_signature pks; | 
 | 	struct signature_v2_hdr *hdr = (struct signature_v2_hdr *)sig; | 
 | 	struct key *key; | 
 | 	int ret = -ENOMEM; | 
 |  | 
 | 	if (siglen <= sizeof(*hdr)) | 
 | 		return -EBADMSG; | 
 |  | 
 | 	siglen -= sizeof(*hdr); | 
 |  | 
 | 	if (siglen != be16_to_cpu(hdr->sig_size)) | 
 | 		return -EBADMSG; | 
 |  | 
 | 	if (hdr->hash_algo >= HASH_ALGO__LAST) | 
 | 		return -ENOPKG; | 
 |  | 
 | 	key = request_asymmetric_key(keyring, be32_to_cpu(hdr->keyid)); | 
 | 	if (IS_ERR(key)) | 
 | 		return PTR_ERR(key); | 
 |  | 
 | 	memset(&pks, 0, sizeof(pks)); | 
 |  | 
 | 	pks.pkey_algo = "rsa"; | 
 | 	pks.hash_algo = hash_algo_name[hdr->hash_algo]; | 
 | 	pks.digest = (u8 *)data; | 
 | 	pks.digest_size = datalen; | 
 | 	pks.s = hdr->sig; | 
 | 	pks.s_size = siglen; | 
 | 	ret = verify_signature(key, &pks); | 
 | 	key_put(key); | 
 | 	pr_debug("%s() = %d\n", __func__, ret); | 
 | 	return ret; | 
 | } | 
 |  | 
 | /** | 
 |  * integrity_kernel_module_request - prevent crypto-pkcs1pad(rsa,*) requests | 
 |  * @kmod_name: kernel module name | 
 |  * | 
 |  * We have situation, when public_key_verify_signature() in case of RSA | 
 |  * algorithm use alg_name to store internal information in order to | 
 |  * construct an algorithm on the fly, but crypto_larval_lookup() will try | 
 |  * to use alg_name in order to load kernel module with same name. | 
 |  * Since we don't have any real "crypto-pkcs1pad(rsa,*)" kernel modules, | 
 |  * we are safe to fail such module request from crypto_larval_lookup(). | 
 |  * | 
 |  * In this way we prevent modprobe execution during digsig verification | 
 |  * and avoid possible deadlock if modprobe and/or it's dependencies | 
 |  * also signed with digsig. | 
 |  */ | 
 | int integrity_kernel_module_request(char *kmod_name) | 
 | { | 
 | 	if (strncmp(kmod_name, "crypto-pkcs1pad(rsa,", 20) == 0) | 
 | 		return -EINVAL; | 
 |  | 
 | 	return 0; | 
 | } |