--- 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,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 #include +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 #include +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 "); 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 #include +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 #include #include #include @@ -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) {