364 lines
9.1 KiB
Diff
364 lines
9.1 KiB
Diff
--- a/crypto/af_alg.c
|
|
+++ b/crypto/af_alg.c
|
|
@@ -8,6 +8,7 @@
|
|
*/
|
|
|
|
#include <linux/atomic.h>
|
|
+#include <linux/capability.h>
|
|
#include <crypto/if_alg.h>
|
|
#include <linux/crypto.h>
|
|
#include <linux/init.h>
|
|
@@ -22,10 +23,28 @@
|
|
#include <linux/sched/signal.h>
|
|
#include <linux/security.h>
|
|
#include <linux/string.h>
|
|
+#include <linux/sysctl.h>
|
|
+#include <linux/user_namespace.h>
|
|
#include <keys/user-type.h>
|
|
#include <keys/trusted-type.h>
|
|
#include <keys/encrypted-type.h>
|
|
|
|
+static int af_alg_restrict = 1;
|
|
+
|
|
+static const struct ctl_table af_alg_table[] = {
|
|
+ {
|
|
+ .procname = "af_alg_restrict",
|
|
+ .data = &af_alg_restrict,
|
|
+ .maxlen = sizeof(int),
|
|
+ .mode = 0644,
|
|
+ .proc_handler = proc_dointvec_minmax,
|
|
+ .extra1 = SYSCTL_ZERO,
|
|
+ .extra2 = SYSCTL_TWO,
|
|
+ },
|
|
+};
|
|
+
|
|
+static struct ctl_table_header *af_alg_header;
|
|
+
|
|
struct alg_type_list {
|
|
const struct af_alg_type *type;
|
|
struct list_head list;
|
|
@@ -110,6 +129,43 @@
|
|
}
|
|
EXPORT_SYMBOL_GPL(af_alg_unregister_type);
|
|
|
|
+static bool af_alg_capable(void)
|
|
+{
|
|
+ return ns_capable_noaudit(&init_user_ns, CAP_NET_ADMIN) ||
|
|
+ capable(CAP_SYS_ADMIN);
|
|
+}
|
|
+
|
|
+int af_alg_check_restriction(const char *name,
|
|
+ const struct af_alg_allowlist_entry allowlist[])
|
|
+{
|
|
+ int level = READ_ONCE(af_alg_restrict);
|
|
+
|
|
+ if (level == 0)
|
|
+ return 0;
|
|
+ if (level == 1) {
|
|
+ for (const struct af_alg_allowlist_entry *ent = allowlist;
|
|
+ ent->name; ent++) {
|
|
+ if (strcmp(name, ent->name) == 0) {
|
|
+ if ((ent->flags & AF_ALG_UNPRIVILEGED) ||
|
|
+ af_alg_capable())
|
|
+ return 0;
|
|
+ /* List contains at most one entry per name. */
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ /*
|
|
+ * Use -ENOENT (the error code for "algorithm not found") instead of
|
|
+ * -EACCES or -EPERM, for the highest chance of correctly triggering
|
|
+ * fallback code paths in userspace programs.
|
|
+ *
|
|
+ * Don't log a warning, since it would be noisy. iwd tries to bind a
|
|
+ * bunch of algorithms that it never uses.
|
|
+ */
|
|
+ return -ENOENT;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(af_alg_check_restriction);
|
|
+
|
|
static void alg_do_release(const struct af_alg_type *type, void *private)
|
|
{
|
|
if (!type)
|
|
@@ -506,6 +562,9 @@
|
|
struct sock *sk;
|
|
int err;
|
|
|
|
+ if (READ_ONCE(af_alg_restrict) == 2)
|
|
+ return -EAFNOSUPPORT;
|
|
+
|
|
if (sock->type != SOCK_SEQPACKET)
|
|
return -ESOCKTNOSUPPORT;
|
|
if (protocol != 0)
|
|
@@ -1222,27 +1281,32 @@
|
|
|
|
static int __init af_alg_init(void)
|
|
{
|
|
- int err = proto_register(&alg_proto, 0);
|
|
+ int err;
|
|
+
|
|
+ af_alg_header = register_sysctl("crypto", af_alg_table);
|
|
|
|
+ err = proto_register(&alg_proto, 0);
|
|
if (err)
|
|
- goto out;
|
|
+ goto out_unregister_sysctl;
|
|
|
|
err = sock_register(&alg_family);
|
|
- if (err != 0)
|
|
+ if (err)
|
|
goto out_unregister_proto;
|
|
|
|
-out:
|
|
- return err;
|
|
+ return 0;
|
|
|
|
out_unregister_proto:
|
|
proto_unregister(&alg_proto);
|
|
- goto out;
|
|
+out_unregister_sysctl:
|
|
+ unregister_sysctl_table(af_alg_header);
|
|
+ return err;
|
|
}
|
|
|
|
static void __exit af_alg_exit(void)
|
|
{
|
|
sock_unregister(PF_ALG);
|
|
proto_unregister(&alg_proto);
|
|
+ unregister_sysctl_table(af_alg_header);
|
|
}
|
|
|
|
module_init(af_alg_init);
|
|
--- a/crypto/algif_aead.c
|
|
+++ b/crypto/algif_aead.c
|
|
@@ -34,6 +34,11 @@
|
|
#include <linux/net.h>
|
|
#include <net/sock.h>
|
|
|
|
+static const struct af_alg_allowlist_entry aead_allowlist[] = {
|
|
+ { "ccm(aes)" }, /* bluez */
|
|
+ {},
|
|
+};
|
|
+
|
|
static inline bool aead_sufficient_data(struct sock *sk)
|
|
{
|
|
struct alg_sock *ask = alg_sk(sk);
|
|
@@ -344,6 +349,12 @@
|
|
|
|
static void *aead_bind(const char *name)
|
|
{
|
|
+ int err;
|
|
+
|
|
+ err = af_alg_check_restriction(name, aead_allowlist);
|
|
+ if (err)
|
|
+ return ERR_PTR(err);
|
|
+
|
|
return crypto_alloc_aead(name, 0, AF_ALG_CRYPTOAPI_MASK);
|
|
}
|
|
|
|
--- a/crypto/algif_hash.c
|
|
+++ b/crypto/algif_hash.c
|
|
@@ -16,6 +16,24 @@
|
|
#include <linux/net.h>
|
|
#include <net/sock.h>
|
|
|
|
+static const struct af_alg_allowlist_entry hash_allowlist[] = {
|
|
+ { "cmac(aes)" }, /* iwd, bluez */
|
|
+ { "hmac(md5)" }, /* iwd */
|
|
+ { "hmac(sha1)" }, /* iwd */
|
|
+ { "hmac(sha224)" }, /* iwd */
|
|
+ { "hmac(sha256)" }, /* iwd */
|
|
+ { "hmac(sha384)" }, /* iwd */
|
|
+ { "hmac(sha512)" }, /* iwd, sha512hmac */
|
|
+ { "md4" }, /* iwd */
|
|
+ { "md5" }, /* iwd */
|
|
+ { "sha1", AF_ALG_UNPRIVILEGED }, /* iwd, iproute2 < 7.0 */
|
|
+ { "sha224" }, /* iwd */
|
|
+ { "sha256" }, /* iwd */
|
|
+ { "sha384" }, /* iwd */
|
|
+ { "sha512" }, /* iwd */
|
|
+ {},
|
|
+};
|
|
+
|
|
struct hash_ctx {
|
|
struct af_alg_sgl sgl;
|
|
|
|
@@ -382,6 +400,12 @@
|
|
|
|
static void *hash_bind(const char *name)
|
|
{
|
|
+ int err;
|
|
+
|
|
+ err = af_alg_check_restriction(name, hash_allowlist);
|
|
+ if (err)
|
|
+ return ERR_PTR(err);
|
|
+
|
|
return crypto_alloc_ahash(name, 0, AF_ALG_CRYPTOAPI_MASK);
|
|
}
|
|
|
|
--- a/crypto/algif_rng.c
|
|
+++ b/crypto/algif_rng.c
|
|
@@ -50,6 +50,10 @@
|
|
MODULE_AUTHOR("Stephan Mueller <smueller@chronox.de>");
|
|
MODULE_DESCRIPTION("User-space interface for random number generators");
|
|
|
|
+static const struct af_alg_allowlist_entry rng_allowlist[] = {
|
|
+ {},
|
|
+};
|
|
+
|
|
struct rng_ctx {
|
|
#define MAXSIZE 128
|
|
unsigned int len;
|
|
@@ -201,6 +205,11 @@
|
|
{
|
|
struct rng_parent_ctx *pctx;
|
|
struct crypto_rng *rng;
|
|
+ int err;
|
|
+
|
|
+ err = af_alg_check_restriction(name, rng_allowlist);
|
|
+ if (err)
|
|
+ return ERR_PTR(err);
|
|
|
|
pctx = kzalloc_obj(*pctx);
|
|
if (!pctx)
|
|
--- a/crypto/algif_skcipher.c
|
|
+++ b/crypto/algif_skcipher.c
|
|
@@ -35,6 +35,24 @@
|
|
#include <linux/string.h>
|
|
#include <net/sock.h>
|
|
|
|
+static const struct af_alg_allowlist_entry skcipher_allowlist[] = {
|
|
+ { "adiantum(xchacha12,aes)", AF_ALG_UNPRIVILEGED }, /* cryptsetup */
|
|
+ { "adiantum(xchacha20,aes)", AF_ALG_UNPRIVILEGED }, /* cryptsetup */
|
|
+ { "cbc(aes)" }, /* iwd */
|
|
+ { "cbc(des)" }, /* iwd */
|
|
+ { "cbc(des3_ede)" }, /* iwd */
|
|
+ { "cbc(paes)" }, /* caam and others */
|
|
+ { "ctr(aes)" }, /* iwd */
|
|
+ { "ecb(aes)" }, /* iwd, bluez */
|
|
+ { "ecb(des)" }, /* iwd */
|
|
+ { "hctr2(aes)", AF_ALG_UNPRIVILEGED }, /* cryptsetup */
|
|
+ { "xts(aes)", AF_ALG_UNPRIVILEGED }, /* cryptsetup benchmark */
|
|
+ { "xts(camellia)", AF_ALG_UNPRIVILEGED }, /* cryptsetup */
|
|
+ { "xts(serpent)", AF_ALG_UNPRIVILEGED }, /* cryptsetup */
|
|
+ { "xts(twofish)", AF_ALG_UNPRIVILEGED }, /* cryptsetup */
|
|
+ {},
|
|
+};
|
|
+
|
|
static int skcipher_sendmsg(struct socket *sock, struct msghdr *msg,
|
|
size_t size)
|
|
{
|
|
@@ -311,6 +329,11 @@
|
|
static void *skcipher_bind(const char *name)
|
|
{
|
|
u32 mask = AF_ALG_CRYPTOAPI_MASK;
|
|
+ int err;
|
|
+
|
|
+ err = af_alg_check_restriction(name, skcipher_allowlist);
|
|
+ if (err)
|
|
+ return ERR_PTR(err);
|
|
|
|
if (strcmp(name, "cbc(paes)") == 0)
|
|
mask = 0;
|
|
--- a/include/crypto/if_alg.h
|
|
+++ b/include/crypto/if_alg.h
|
|
@@ -8,6 +8,7 @@
|
|
#ifndef _CRYPTO_IF_ALG_H
|
|
#define _CRYPTO_IF_ALG_H
|
|
|
|
+#include <linux/bits.h>
|
|
#include <linux/compiler.h>
|
|
#include <linux/completion.h>
|
|
#include <linux/if_alg.h>
|
|
@@ -121,7 +122,7 @@
|
|
* @iv: IV for cipher operation
|
|
* @state: Existing state for continuing operation
|
|
* @aead_assoclen: Length of AAD for AEAD cipher operations
|
|
- * @completion: Work queue for synchronous operation
|
|
+ * @wait: For waiting for completion of async crypto ops
|
|
* @used: TX bytes sent to kernel. This variable is used to
|
|
* ensure that user space cannot cause the kernel
|
|
* to allocate too much memory in sendmsg operation.
|
|
@@ -161,9 +162,20 @@
|
|
unsigned int inflight;
|
|
};
|
|
|
|
+/* Flags for af_alg_allowlist_entry::flags: */
|
|
+#define AF_ALG_UNPRIVILEGED BIT(0) /* Unprivileged use is allowed */
|
|
+
|
|
+struct af_alg_allowlist_entry {
|
|
+ const char *name;
|
|
+ u32 flags;
|
|
+};
|
|
+
|
|
int af_alg_register_type(const struct af_alg_type *type);
|
|
int af_alg_unregister_type(const struct af_alg_type *type);
|
|
|
|
+int af_alg_check_restriction(const char *name,
|
|
+ const struct af_alg_allowlist_entry allowlist[]);
|
|
+
|
|
int af_alg_release(struct socket *sock);
|
|
void af_alg_release_parent(struct sock *sk);
|
|
int af_alg_accept(struct sock *sk, struct socket *newsock,
|
|
@@ -177,10 +189,11 @@
|
|
}
|
|
|
|
/**
|
|
- * Size of available buffer for sending data from user space to kernel.
|
|
+ * af_alg_sndbuf - Size of available buffer for sending data from user space to kernel.
|
|
*
|
|
- * @sk socket of connection to user space
|
|
- * @return number of bytes still available
|
|
+ * @sk: socket of connection to user space
|
|
+ *
|
|
+ * Returns: number of bytes still available
|
|
*/
|
|
static inline int af_alg_sndbuf(struct sock *sk)
|
|
{
|
|
@@ -192,10 +205,11 @@
|
|
}
|
|
|
|
/**
|
|
- * Can the send buffer still be written to?
|
|
+ * af_alg_writable - Can the send buffer still be written to?
|
|
+ *
|
|
+ * @sk: socket of connection to user space
|
|
*
|
|
- * @sk socket of connection to user space
|
|
- * @return true => writable, false => not writable
|
|
+ * Returns: true => writable, false => not writable
|
|
*/
|
|
static inline bool af_alg_writable(struct sock *sk)
|
|
{
|
|
@@ -203,10 +217,11 @@
|
|
}
|
|
|
|
/**
|
|
- * Size of available buffer used by kernel for the RX user space operation.
|
|
+ * af_alg_rcvbuf - Size of available buffer used by kernel for the RX user space operation.
|
|
*
|
|
- * @sk socket of connection to user space
|
|
- * @return number of bytes still available
|
|
+ * @sk: socket of connection to user space
|
|
+ *
|
|
+ * Returns: number of bytes still available
|
|
*/
|
|
static inline int af_alg_rcvbuf(struct sock *sk)
|
|
{
|
|
@@ -218,10 +233,11 @@
|
|
}
|
|
|
|
/**
|
|
- * Can the RX buffer still be written to?
|
|
+ * af_alg_readable - Can the RX buffer still be read from?
|
|
+ *
|
|
+ * @sk: socket of connection to user space
|
|
*
|
|
- * @sk socket of connection to user space
|
|
- * @return true => writable, false => not writable
|
|
+ * Returns: true => readable, false => not readable
|
|
*/
|
|
static inline bool af_alg_readable(struct sock *sk)
|
|
{
|