diff mbox

integrity: prevent deadlock during digsig verification.

Message ID 20180627163342.3e1d6333@totoro (mailing list archive)
State New, archived
Headers show

Commit Message

Mikhail Kurinnoi June 27, 2018, 1:33 p.m. UTC
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>

 include/linux/integrity.h              | 13 +++++++++++++
 security/integrity/digsig_asymmetric.c | 23 +++++++++++++++++++++++
 security/security.c                    |  7 ++++++-
 3 files changed, 42 insertions(+), 1 deletion(-)

Comments

Matthias Gerstner June 28, 2018, 4:39 p.m. UTC | #1
Hi,

> In this patch, I propose add an exception for "crypto-pkcs1pad(rsa,*)"
> module requests only in case of enabled integrity asymmetric keys support.

I have tested the patch in my test setup and it looks good. No deadlocks
so far.

Regards

Matthias
Mimi Zohar June 28, 2018, 6:43 p.m. UTC | #2
Hi Mikhail,

On Wed, 2018-06-27 at 16:33 +0300, Mikhail Kurinnoi wrote:
> 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>

Thanks!  I've rebased the existing queued patches on James' next-
general branch and pushed it out to next-integrity.  This patch is now
queued in the next-integrity-queued branch.

Mimi

> 
>  include/linux/integrity.h              | 13 +++++++++++++
>  security/integrity/digsig_asymmetric.c | 23 +++++++++++++++++++++++
>  security/security.c                    |  7 ++++++-
>  3 files changed, 42 insertions(+), 1 deletion(-)
> 
> diff --git a/include/linux/integrity.h b/include/linux/integrity.h
> index c2d6082..8678e32 100644
> --- a/include/linux/integrity.h
> +++ b/include/linux/integrity.h
> @@ -43,4 +43,17 @@ static inline void integrity_load_keys(void)
>  }
>  #endif /* CONFIG_INTEGRITY */
> 
> +#ifdef CONFIG_INTEGRITY_ASYMMETRIC_KEYS
> +
> +extern int integrity_kernel_module_request(char *kmod_name);
> +
> +#else
> +
> +static inline int integrity_kernel_module_request(char *kmod_name)
> +{
> +	return 0;
> +}
> +
> +#endif /* CONFIG_INTEGRITY_ASYMMETRIC_KEYS */
> +
>  #endif /* _LINUX_INTEGRITY_H */
> diff --git a/security/integrity/digsig_asymmetric.c b/security/integrity/digsig_asymmetric.c
> index 80052ed..f1ab90c 100644
> --- a/security/integrity/digsig_asymmetric.c
> +++ b/security/integrity/digsig_asymmetric.c
> @@ -115,3 +115,26 @@ int asymmetric_verify(struct key *keyring, const char *sig,
>  	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;
> +}
> diff --git a/security/security.c b/security/security.c
> index f825304..d53b4cb 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -929,7 +929,12 @@ int security_kernel_create_files_as(struct cred *new, struct inode *inode)
> 
>  int security_kernel_module_request(char *kmod_name)
>  {
> -	return call_int_hook(kernel_module_request, 0, kmod_name);
> +	int ret;
> +
> +	ret = call_int_hook(kernel_module_request, 0, kmod_name);
> +	if (ret)
> +		return ret;
> +	return integrity_kernel_module_request(kmod_name);
>  }
> 
>  int security_kernel_read_file(struct file *file, enum kernel_read_file_id id)
>
Mimi Zohar June 28, 2018, 7:14 p.m. UTC | #3
On Thu, 2018-06-28 at 18:39 +0200, Matthias Gerstner wrote:
> Hi,
> 
> > In this patch, I propose add an exception for "crypto-pkcs1pad(rsa,*)"
> > module requests only in case of enabled integrity asymmetric keys support.
> 
> I have tested the patch in my test setup and it looks good. No deadlocks
> so far.

I really wish we didn't have to do a string compare "crypto-
pkcs1pad(rsa" each and every time.  Is the check once per crypto
algorithm?

Mimi
Mikhail Kurinnoi June 28, 2018, 8:50 p.m. UTC | #4
В Thu, 28 Jun 2018 15:14:38 -0400
Mimi Zohar <zohar@linux.vnet.ibm.com> пишет:

> On Thu, 2018-06-28 at 18:39 +0200, Matthias Gerstner wrote:
> > Hi,
> >   
> > > In this patch, I propose add an exception for
> > > "crypto-pkcs1pad(rsa,*)" module requests only in case of enabled
> > > integrity asymmetric keys support.  
> > 
> > I have tested the patch in my test setup and it looks good. No
> > deadlocks so far.  
> 
> I really wish we didn't have to do a string compare "crypto-
> pkcs1pad(rsa" each and every time.  Is the check once per crypto
> algorithm?

As I understood, it check once per crypto algorithm:

"crypto_alloc_tfm() will first attempt to locate an already loaded
algorithm.
...
If that fails it will send a query to any loaded crypto manager to
construct an algorithm on the fly.
A refcount is grabbed on the algorithm which is then associated with
the new transform."

https://github.com/torvalds/linux/blob/a97d8efd9d350bd9c6cf13689c7cc09049b42acd/crypto/api.c#L515
Mimi Zohar June 28, 2018, 9:27 p.m. UTC | #5
On Thu, 2018-06-28 at 23:50 +0300, Mikhail Kurinnoi wrote:
> В Thu, 28 Jun 2018 15:14:38 -0400
> Mimi Zohar <zohar@linux.vnet.ibm.com> пишет:
> 
> > On Thu, 2018-06-28 at 18:39 +0200, Matthias Gerstner wrote:
> > > Hi,
> > >   
> > > > In this patch, I propose add an exception for
> > > > "crypto-pkcs1pad(rsa,*)" module requests only in case of enabled
> > > > integrity asymmetric keys support.  
> > > 
> > > I have tested the patch in my test setup and it looks good. No
> > > deadlocks so far.  
> > 
> > I really wish we didn't have to do a string compare "crypto-
> > pkcs1pad(rsa" each and every time.  Is the check once per crypto
> > algorithm?
> 
> As I understood, it check once per crypto algorithm:
> 
> "crypto_alloc_tfm() will first attempt to locate an already loaded
> algorithm.
> ...
> If that fails it will send a query to any loaded crypto manager to
> construct an algorithm on the fly.
> A refcount is grabbed on the algorithm which is then associated with
> the new transform."
> 
> https://github.com/torvalds/linux/blob/a97d8efd9d350bd9c6cf13689c7cc09049b42acd/crypto/api.c#L515

After having loaded "all" the crypto algorithms, we wouldn't need to
ever do the string compare again.  As this isn't on a critical path,
nor is it likely for all crypto algorithms to be loaded, it probably
doesn't make sense to address it.

Mimi
Lukas Wunner Sept. 11, 2024, 10 a.m. UTC | #6
On Wed, Jun 27, 2018 at 04:33:42PM +0300, Mikhail Kurinnoi wrote:
> +int integrity_kernel_module_request(char *kmod_name)
> +{
> +	if (strncmp(kmod_name, "crypto-pkcs1pad(rsa,", 20) == 0)
> +		return -EINVAL;
> +
> +	return 0;
> +}

Just a heads-up:  The above was applied as commit 6eb864c1d9dd
("integrity: prevent deadlock during digsig verification.") in 2018.

In 2021 the kernel gained support for ECDSA signatures,
in 2019 for ECRDSA (GOST) signatures.

If kernel modules are signed with ECDSA or ECRDSA, you may once again
see the deadlock addressed by the above-quoted patch and you may need
additional string comparisons to avoid it.

There's a (stalled) effort to support RSA PSS, this may likewise
require yet another string comparison:
https://lore.kernel.org/all/20210420114124.9684-1-varad.gautam@suse.com/

You may have to come up with a solution that doesn't require adding
new string comparisons as this isn't scalable and it's easy to forget
amending the string comparisons when new algorithms or encodings are
added to the crypto subsystem.

Thanks,

Lukas
diff mbox

Patch

diff --git a/include/linux/integrity.h b/include/linux/integrity.h
index c2d6082..8678e32 100644
--- a/include/linux/integrity.h
+++ b/include/linux/integrity.h
@@ -43,4 +43,17 @@  static inline void integrity_load_keys(void)
 }
 #endif /* CONFIG_INTEGRITY */
 
+#ifdef CONFIG_INTEGRITY_ASYMMETRIC_KEYS
+
+extern int integrity_kernel_module_request(char *kmod_name);
+
+#else
+
+static inline int integrity_kernel_module_request(char *kmod_name)
+{
+	return 0;
+}
+
+#endif /* CONFIG_INTEGRITY_ASYMMETRIC_KEYS */
+
 #endif /* _LINUX_INTEGRITY_H */
diff --git a/security/integrity/digsig_asymmetric.c b/security/integrity/digsig_asymmetric.c
index 80052ed..f1ab90c 100644
--- a/security/integrity/digsig_asymmetric.c
+++ b/security/integrity/digsig_asymmetric.c
@@ -115,3 +115,26 @@  int asymmetric_verify(struct key *keyring, const char *sig,
 	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;
+}
diff --git a/security/security.c b/security/security.c
index f825304..d53b4cb 100644
--- a/security/security.c
+++ b/security/security.c
@@ -929,7 +929,12 @@  int security_kernel_create_files_as(struct cred *new, struct inode *inode)
 
 int security_kernel_module_request(char *kmod_name)
 {
-	return call_int_hook(kernel_module_request, 0, kmod_name);
+	int ret;
+
+	ret = call_int_hook(kernel_module_request, 0, kmod_name);
+	if (ret)
+		return ret;
+	return integrity_kernel_module_request(kmod_name);
 }
 
 int security_kernel_read_file(struct file *file, enum kernel_read_file_id id)