2019-06-01 16:08:55 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
2013-02-07 06:12:08 +08:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2013 Intel Corporation
|
|
|
|
*
|
|
|
|
* Author:
|
|
|
|
* Dmitry Kasatkin <dmitry.kasatkin@intel.com>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
|
|
|
|
|
|
#include <linux/err.h>
|
2014-07-02 20:12:26 +08:00
|
|
|
#include <linux/ratelimit.h>
|
2013-02-07 06:12:08 +08:00
|
|
|
#include <linux/key-type.h>
|
|
|
|
#include <crypto/public_key.h>
|
2016-03-04 05:49:27 +08:00
|
|
|
#include <crypto/hash_info.h>
|
2013-02-07 06:12:08 +08:00
|
|
|
#include <keys/asymmetric-type.h>
|
2015-12-02 23:47:55 +08:00
|
|
|
#include <keys/system_keyring.h>
|
2013-02-07 06:12:08 +08:00
|
|
|
|
|
|
|
#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];
|
|
|
|
|
2014-10-07 00:31:58 +08:00
|
|
|
sprintf(name, "id:%08x", keyid);
|
2013-02-07 06:12:08 +08:00
|
|
|
|
|
|
|
pr_debug("key search: \"%s\"\n", name);
|
|
|
|
|
2015-12-02 23:47:55 +08:00
|
|
|
key = get_ima_blacklist_keyring();
|
|
|
|
if (key) {
|
|
|
|
key_ref_t kref;
|
|
|
|
|
|
|
|
kref = keyring_search(make_key_ref(key, 1),
|
2019-06-27 04:02:32 +08:00
|
|
|
&key_type_asymmetric, name, true);
|
2015-12-02 23:47:55 +08:00
|
|
|
if (!IS_ERR(kref)) {
|
|
|
|
pr_err("Key '%s' is in ima_blacklist_keyring\n", name);
|
|
|
|
return ERR_PTR(-EKEYREJECTED);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-07 06:12:08 +08:00
|
|
|
if (keyring) {
|
|
|
|
/* search in specific keyring */
|
|
|
|
key_ref_t kref;
|
2015-12-02 23:47:55 +08:00
|
|
|
|
2013-02-07 06:12:08 +08:00
|
|
|
kref = keyring_search(make_key_ref(keyring, 1),
|
2019-06-27 04:02:32 +08:00
|
|
|
&key_type_asymmetric, name, true);
|
2013-02-07 06:12:08 +08:00
|
|
|
if (IS_ERR(kref))
|
|
|
|
key = ERR_CAST(kref);
|
|
|
|
else
|
|
|
|
key = key_ref_to_ptr(kref);
|
|
|
|
} else {
|
2019-07-11 09:43:43 +08:00
|
|
|
key = request_key(&key_type_asymmetric, name, NULL);
|
2013-02-07 06:12:08 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (IS_ERR(key)) {
|
2014-07-02 20:12:26 +08:00
|
|
|
pr_err_ratelimited("Request for unknown key '%s' err %ld\n",
|
|
|
|
name, PTR_ERR(key));
|
2013-02-07 06:12:08 +08:00
|
|
|
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);
|
|
|
|
|
2017-06-08 09:49:10 +08:00
|
|
|
if (siglen != be16_to_cpu(hdr->sig_size))
|
2013-02-07 06:12:08 +08:00
|
|
|
return -EBADMSG;
|
|
|
|
|
2016-03-04 05:49:27 +08:00
|
|
|
if (hdr->hash_algo >= HASH_ALGO__LAST)
|
2013-02-07 06:12:08 +08:00
|
|
|
return -ENOPKG;
|
|
|
|
|
2017-06-08 09:49:10 +08:00
|
|
|
key = request_asymmetric_key(keyring, be32_to_cpu(hdr->keyid));
|
2013-02-07 06:12:08 +08:00
|
|
|
if (IS_ERR(key))
|
|
|
|
return PTR_ERR(key);
|
|
|
|
|
|
|
|
memset(&pks, 0, sizeof(pks));
|
|
|
|
|
2016-03-04 05:49:27 +08:00
|
|
|
pks.hash_algo = hash_algo_name[hdr->hash_algo];
|
2019-04-11 23:51:22 +08:00
|
|
|
if (hdr->hash_algo == HASH_ALGO_STREEBOG_256 ||
|
|
|
|
hdr->hash_algo == HASH_ALGO_STREEBOG_512) {
|
|
|
|
/* EC-RDSA and Streebog should go together. */
|
|
|
|
pks.pkey_algo = "ecrdsa";
|
|
|
|
pks.encoding = "raw";
|
|
|
|
} else {
|
|
|
|
pks.pkey_algo = "rsa";
|
|
|
|
pks.encoding = "pkcs1";
|
|
|
|
}
|
2013-02-07 06:12:08 +08:00
|
|
|
pks.digest = (u8 *)data;
|
|
|
|
pks.digest_size = datalen;
|
2016-02-03 02:08:58 +08:00
|
|
|
pks.s = hdr->sig;
|
|
|
|
pks.s_size = siglen;
|
|
|
|
ret = verify_signature(key, &pks);
|
2013-02-07 06:12:08 +08:00
|
|
|
key_put(key);
|
|
|
|
pr_debug("%s() = %d\n", __func__, ret);
|
|
|
|
return ret;
|
|
|
|
}
|
integrity: prevent deadlock during digsig verification.
This patch aimed to prevent deadlock during digsig verification.The point
of issue - user space utility modprobe and/or it's dependencies (ld-*.so,
libz.so.*, libc-*.so and /lib/modules/ files) that could be used for
kernel modules load during digsig verification and could be signed by
digsig in the same time.
First at all, look at crypto_alloc_tfm() work algorithm:
crypto_alloc_tfm() will first attempt to locate an already loaded
algorithm. If that fails and the kernel supports dynamically loadable
modules, it will then attempt to load a module of the same name or alias.
If that fails it will send a query to any loaded crypto manager to
construct an algorithm on the fly.
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.
1) we can't do anything with crypto module work, since it designed to work
exactly in this way;
2) we can't globally filter module requests for modprobe, since it
designed to work with any requests.
In this patch, I propose add an exception for "crypto-pkcs1pad(rsa,*)"
module requests only in case of enabled integrity asymmetric keys support.
Since we don't have any real "crypto-pkcs1pad(rsa,*)" kernel modules for
sure, 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.
Requested "crypto-pkcs1pad(rsa,*)" kernel module name formed by:
1) "pkcs1pad(rsa,%s)" in public_key_verify_signature();
2) "crypto-%s" / "crypto-%s-all" in crypto_larval_lookup().
"crypto-pkcs1pad(rsa," part of request is a constant and unique and could
be used as filter.
Signed-off-by: Mikhail Kurinnoi <viewizard@viewizard.com>
Signed-off-by: Mimi Zohar <zohar@linux.vnet.ibm.com>
include/linux/integrity.h | 13 +++++++++++++
security/integrity/digsig_asymmetric.c | 23 +++++++++++++++++++++++
security/security.c | 7 ++++++-
3 files changed, 42 insertions(+), 1 deletion(-)
2018-06-27 21:33:42 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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;
|
|
|
|
}
|