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 @@ -7,6 +7,42 @@ 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 +`. + +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 ============ 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,3 +1,5 @@ +.. _crypto_userspace_interface: + User Space Interface ==================== @@ -12,9 +14,14 @@ 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 `. + +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 ----------- 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 @@ -8,6 +8,7 @@ */ #include +#include #include #include #include @@ -22,10 +23,28 @@ #include #include #include +#include +#include #include #include #include +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,39 @@ int af_alg_unregister_type(const struct af_alg_type *type) } 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) @@ -506,6 +558,9 @@ static int alg_create(struct net *net, struct socket *sock, int protocol, 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 +1277,32 @@ 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); 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 @@ -34,6 +34,11 @@ #include #include +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); @@ -344,6 +349,12 @@ static struct proto_ops algif_aead_ops_nokey = { 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); } 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 @@ -16,6 +16,24 @@ #include #include +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; @@ -382,6 +400,12 @@ static struct proto_ops algif_hash_ops_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); } 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 @@ -50,6 +50,10 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Stephan Mueller "); 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 @@ 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) 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 @@ -34,6 +34,20 @@ #include #include +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) { @@ -309,6 +323,12 @@ static struct proto_ops algif_skcipher_ops_nokey = { 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); } 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 @@ -161,9 +161,17 @@ struct af_alg_ctx { 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,