Hello Eric, On Mon, 2026-06-22 at 16:48 -0700, Eric Biggers wrote: > AF_ALG is a frequent source of vulnerabilities and a maintenance > nightmare. It exposes far more functionality to userspace than ever > should have been exposed, especially to unprivileged processes. > Recent > exploits have targeted kernel internal implementation details like > "authencesn" that have zero use case for userspace access.
You should also CC: [email protected] for AF_ALG related changes, as ell uses AF_ALG extensively for crypto and checksumming. Cheers > > Fortunately, AF_ALG is rarely used in practice, as userspace crypto > libraries exist. And when it is used, only some functionality is > known > to be used, and many users are known to hold capabilities already. > iwd for example requires CAP_NET_ADMIN and has a known algorithm list > ( > https://lore.kernel.org/linux-crypto/[email protected] > /). > > Thus, let's restrict the set of allowed algorithms by default, > depending > on the capabilities held. > > Add a sysctl /proc/sys/crypto/af_alg_restrict with meaning: > > 0: unrestricted > 1: limited functionality > 2: completely disabled > > Set the default value to 1, which enables an algorithm allowlist for > unprivileged processes and a slightly longer allowlist for privileged > processes. > > Note that the list may be tweaked in the future. However, the common > use cases such as iwd and bluez are taken into account already. I've > tested that iwd still works with the default value of 1. > > Signed-off-by: Eric Biggers <[email protected]> > --- > Documentation/admin-guide/sysctl/crypto.rst | 36 +++++++++++ > Documentation/crypto/userspace-if.rst | 13 +++- > crypto/af_alg.c | 72 > +++++++++++++++++++-- > crypto/algif_aead.c | 11 ++++ > crypto/algif_hash.c | 24 +++++++ > crypto/algif_rng.c | 9 +++ > crypto/algif_skcipher.c | 20 ++++++ > include/crypto/if_alg.h | 8 +++ > 8 files changed, 184 insertions(+), 9 deletions(-) > > diff --git a/Documentation/admin-guide/sysctl/crypto.rst > b/Documentation/admin-guide/sysctl/crypto.rst > index b707bd314a64..9a1bd53287f4 100644 > --- a/Documentation/admin-guide/sysctl/crypto.rst > +++ b/Documentation/admin-guide/sysctl/crypto.rst > @@ -5,10 +5,46 @@ > These files show up in ``/proc/sys/crypto/``, depending on the > kernel configuration: > > .. contents:: :local: > > +.. _af_alg_restrict: > + > +af_alg_restrict > +=============== > + > +Controls the level of restriction of AF_ALG. > + > +AF_ALG is a deprecated and rarely-used userspace interface that is a > +frequent source of vulnerabilities. It also unnecessarily exposes a > +large number of kernel implementation details. For more information > +about AF_ALG, see :ref:`Documentation/crypto/userspace-if.rst > +<crypto_userspace_interface>`. > + > +Starting in Linux v7.3, AF_ALG supports only a limited set of > +algorithms by default. This sysctl allows the system administrator > to > +remove this restriction when needed for compatibility reasons, or to > +go further and disable AF_ALG entirely. The default value is 1. > + > +=== > ================================================================== > +0 AF_ALG is unrestricted. > + > +1 AF_ALG is supported with a limited list of algorithms. The list > + is designed for compatibility with known users such as iwd and > + bluez that haven't yet been fixed to use userspace crypto code. > + > + Specifically, there is an allowlist for unprivileged processes > + and a somewhat longer allowlist for processes that hold > + CAP_SYS_ADMIN or CAP_NET_ADMIN in the initial user namespace. > + > + Attempts to bind() an AF_ALG socket with a disallowed algorithm > + fail with ENOENT. > + > +2 AF_ALG is completely disabled. Attempts to create an AF_ALG > + socket fail with EAFNOSUPPORT. > +=== > ================================================================== > + > fips_enabled > ============ > > Read-only flag that indicates whether FIPS mode is enabled. > > diff --git a/Documentation/crypto/userspace-if.rst > b/Documentation/crypto/userspace-if.rst > index ab93300c8e04..d6194346e366 100644 > --- a/Documentation/crypto/userspace-if.rst > +++ b/Documentation/crypto/userspace-if.rst > @@ -1,5 +1,7 @@ > +.. _crypto_userspace_interface: > + > User Space Interface > ==================== > > Introduction > ------------ > @@ -10,13 +12,18 @@ code. > > AF_ALG is insecure and is deprecated. Originally added to the kernel > in 2010, > most kernel developers now consider it to be a mistake. Support for > hardware > accelerators, which was the original purpose of AF_ALG, has been > removed. > > -AF_ALG continues to be supported only for backwards compatibility. > On systems > -where no programs using AF_ALG remain, the support for it should be > disabled by > -disabling ``CONFIG_CRYPTO_USER_API_*``. > +AF_ALG continues to be supported only for backwards compatibility. > + > +Starting in Linux v7.3, the set of algorithms supported by AF_ALG is > limited by > +default. See :ref:`/proc/sys/crypto/af_alg_restrict > <af_alg_restrict>`. > + > +On systems where no programs using AF_ALG remain, the support for it > should be > +disabled entirely by setting ``/proc/sys/crypto/af_alg_restrict`` to > 2 or by > +disabling ``CONFIG_CRYPTO_USER_API_*`` in the kernel configuration. > > Deprecation > ----------- > > AF_ALG was originally intended to provide userspace programs access > to crypto > diff --git a/crypto/af_alg.c b/crypto/af_alg.c > index cce000e8590e..34b801568fba 100644 > --- a/crypto/af_alg.c > +++ b/crypto/af_alg.c > @@ -6,10 +6,11 @@ > * > * Copyright (c) 2010 Herbert Xu <[email protected]> > */ > > #include <linux/atomic.h> > +#include <linux/capability.h> > #include <crypto/if_alg.h> > #include <linux/crypto.h> > #include <linux/init.h> > #include <linux/kernel.h> > #include <linux/key.h> > @@ -20,14 +21,32 @@ > #include <linux/rwsem.h> > #include <linux/sched.h> > #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; > }; > > @@ -108,10 +127,43 @@ int af_alg_unregister_type(const struct > af_alg_type *type) > > return err; > } > 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 && > + (!ent->privileged || af_alg_capable())) > + return 0; > + } > + } > + /* > + * 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) > return; > > @@ -504,10 +556,13 @@ static int alg_create(struct net *net, struct > socket *sock, int protocol, > int kern) > { > struct sock *sk; > int err; > > + if (READ_ONCE(af_alg_restrict) == 2) > + return -EAFNOSUPPORT; > + > if (sock->type != SOCK_SEQPACKET) > return -ESOCKTNOSUPPORT; > if (protocol != 0) > return -EPROTONOSUPPORT; > > @@ -1220,31 +1275,36 @@ int af_alg_get_rsgl(struct sock *sk, struct > msghdr *msg, int flags, > } > EXPORT_SYMBOL_GPL(af_alg_get_rsgl); > > 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); > module_exit(af_alg_exit); > MODULE_DESCRIPTION("Crypto userspace interface"); > diff --git a/crypto/algif_aead.c b/crypto/algif_aead.c > index 787aac8aeb24..b9217f9086aa 100644 > --- a/crypto/algif_aead.c > +++ b/crypto/algif_aead.c > @@ -32,10 +32,15 @@ > #include <linux/mm.h> > #include <linux/module.h> > #include <linux/net.h> > #include <net/sock.h> > > +static const struct af_alg_allowlist_entry aead_allowlist[] = { > + { "ccm(aes)", true }, /* bluez */ > + {}, > +}; > + > static inline bool aead_sufficient_data(struct sock *sk) > { > struct alg_sock *ask = alg_sk(sk); > struct sock *psk = ask->parent; > struct alg_sock *pask = alg_sk(psk); > @@ -342,10 +347,16 @@ static struct proto_ops algif_aead_ops_nokey = > { > .poll = af_alg_poll, > }; > > 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); > } > > static void aead_release(void *private) > { > diff --git a/crypto/algif_hash.c b/crypto/algif_hash.c > index 5452ad6c1506..a8d958d51ece 100644 > --- a/crypto/algif_hash.c > +++ b/crypto/algif_hash.c > @@ -14,10 +14,28 @@ > #include <linux/mm.h> > #include <linux/module.h> > #include <linux/net.h> > #include <net/sock.h> > > +static const struct af_alg_allowlist_entry hash_allowlist[] = { > + { "cmac(aes)", true }, /* iwd, bluez */ > + { "hmac(md5)", true }, /* iwd */ > + { "hmac(sha1)", true }, /* iwd */ > + { "hmac(sha224)", true }, /* iwd */ > + { "hmac(sha256)", true }, /* iwd */ > + { "hmac(sha384)", true }, /* iwd */ > + { "hmac(sha512)", true }, /* iwd, sha512hmac */ > + { "md4", true }, /* iwd */ > + { "md5", true }, /* iwd */ > + { "sha1", false }, /* iwd, iproute2 < 7.0 */ > + { "sha224", true }, /* iwd */ > + { "sha256", true }, /* iwd */ > + { "sha384", true }, /* iwd */ > + { "sha512", true }, /* iwd */ > + {}, > +}; > + > struct hash_ctx { > struct af_alg_sgl sgl; > > u8 *result; > > @@ -380,10 +398,16 @@ static struct proto_ops algif_hash_ops_nokey = > { > .accept = hash_accept_nokey, > }; > > 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); > } > > static void hash_release(void *private) > { > diff --git a/crypto/algif_rng.c b/crypto/algif_rng.c > index 4dfe7899f8fa..bd522915d56d 100644 > --- a/crypto/algif_rng.c > +++ b/crypto/algif_rng.c > @@ -48,10 +48,14 @@ > > MODULE_LICENSE("GPL"); > MODULE_AUTHOR("Stephan Mueller <[email protected]>"); > 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; > struct crypto_rng *drng; > u8 *addtl; > @@ -199,10 +203,15 @@ static struct proto_ops __maybe_unused > algif_rng_test_ops = { > > static void *rng_bind(const char *name) > { > 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) > return ERR_PTR(-ENOMEM); > > diff --git a/crypto/algif_skcipher.c b/crypto/algif_skcipher.c > index df20bdfe1f1f..2b8069667974 100644 > --- a/crypto/algif_skcipher.c > +++ b/crypto/algif_skcipher.c > @@ -32,10 +32,24 @@ > #include <linux/mm.h> > #include <linux/module.h> > #include <linux/net.h> > #include <net/sock.h> > > +static const struct af_alg_allowlist_entry skcipher_allowlist[] = { > + { "adiantum(xchacha12,aes)", false }, /* cryptsetup */ > + { "adiantum(xchacha20,aes)", false }, /* cryptsetup */ > + { "cbc(aes)", true }, /* iwd */ > + { "cbc(des)", true }, /* iwd */ > + { "cbc(des3_ede)", true }, /* iwd */ > + { "ctr(aes)", true }, /* iwd */ > + { "ecb(aes)", true }, /* iwd, bluez */ > + { "ecb(des)", true }, /* iwd */ > + { "hctr2(aes)", false }, /* cryptsetup */ > + { "xts(aes)", false }, /* cryptsetup benchmark */ > + {}, > +}; > + > static int skcipher_sendmsg(struct socket *sock, struct msghdr *msg, > size_t size) > { > struct sock *sk = sock->sk; > struct alg_sock *ask = alg_sk(sk); > @@ -307,10 +321,16 @@ static struct proto_ops > algif_skcipher_ops_nokey = { > .poll = af_alg_poll, > }; > > static void *skcipher_bind(const char *name) > { > + int err; > + > + err = af_alg_check_restriction(name, skcipher_allowlist); > + if (err) > + return ERR_PTR(err); > + > return crypto_alloc_skcipher(name, 0, > AF_ALG_CRYPTOAPI_MASK); > } > > static void skcipher_release(void *private) > { > diff --git a/include/crypto/if_alg.h b/include/crypto/if_alg.h > index 7643ba954125..4e9ed8e73403 100644 > --- a/include/crypto/if_alg.h > +++ b/include/crypto/if_alg.h > @@ -159,13 +159,21 @@ struct af_alg_ctx { > unsigned int len; > > unsigned int inflight; > }; > > +struct af_alg_allowlist_entry { > + const char *name; > + bool privileged; > +}; > + > 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, > struct proto_accept_arg *arg); > > > base-commit: 1dc18801be29bc54709aa355b8acd80e183b03cd

