| diff --git a/crypto/af_alg.c b/crypto/af_alg.c |
| index 4a2e91baa..fb6b4f8a0 100644 |
| --- a/crypto/af_alg.c |
| +++ b/crypto/af_alg.c |
| @@ -226,6 +226,30 @@ static int alg_setkey(struct sock *sk, char __user *ukey, |
| return err; |
| } |
| |
| +static int alg_setentropy(struct sock *sk, char __user *ukey, |
| + unsigned int entropy_len) |
| +{ |
| + struct alg_sock *ask = alg_sk(sk); |
| + const struct af_alg_type *type = ask->type; |
| + u8 *entropy; |
| + int err; |
| + |
| + entropy = sock_kmalloc(sk, entropy_len, GFP_KERNEL); |
| + if (!entropy) |
| + return -ENOMEM; |
| + |
| + err = -EFAULT; |
| + if (copy_from_user(entropy, ukey, entropy_len)) |
| + goto out; |
| + |
| + err = type->setentropy(ask->private, entropy, entropy_len); |
| + |
| +out: |
| + sock_kzfree_s(sk, entropy, entropy_len); |
| + |
| + return err; |
| +} |
| + |
| static int alg_setsockopt(struct socket *sock, int level, int optname, |
| char __user *optval, unsigned int optlen) |
| { |
| @@ -250,7 +274,6 @@ static int alg_setsockopt(struct socket *sock, int level, int optname, |
| goto unlock; |
| if (!type->setkey) |
| goto unlock; |
| - |
| err = alg_setkey(sk, optval, optlen); |
| break; |
| case ALG_SET_AEAD_AUTHSIZE: |
| @@ -259,6 +282,15 @@ static int alg_setsockopt(struct socket *sock, int level, int optname, |
| if (!type->setauthsize) |
| goto unlock; |
| err = type->setauthsize(ask->private, optlen); |
| + break; |
| + case ALG_SET_DRBG_ENTROPY: |
| + if (sock->state == SS_CONNECTED) |
| + goto unlock; |
| + if (!type->setentropy) |
| + goto unlock; |
| + err = alg_setentropy(sk, optval, optlen); |
| + break; |
| + |
| } |
| |
| unlock: |
| @@ -291,6 +323,11 @@ int af_alg_accept(struct sock *sk, struct socket *newsock, bool kern) |
| security_sock_graft(sk2, newsock); |
| security_sk_clone(sk, sk2); |
| |
| + /* |
| + * newsock->ops assigned here to allow type->accept call to override |
| + * them when required. |
| + */ |
| + newsock->ops = type->ops; |
| err = type->accept(ask->private, sk2); |
| |
| nokey = err == -ENOKEY; |
| @@ -309,7 +346,6 @@ int af_alg_accept(struct sock *sk, struct socket *newsock, bool kern) |
| alg_sk(sk2)->parent = sk; |
| alg_sk(sk2)->type = type; |
| |
| - newsock->ops = type->ops; |
| newsock->state = SS_CONNECTED; |
| |
| if (nokey) |
| diff --git a/crypto/algif_rng.c b/crypto/algif_rng.c |
| index 22df3799a..bce3aef53 100644 |
| --- a/crypto/algif_rng.c |
| +++ b/crypto/algif_rng.c |
| @@ -38,6 +38,7 @@ |
| * DAMAGE. |
| */ |
| |
| +#include <linux/capability.h> |
| #include <linux/module.h> |
| #include <crypto/rng.h> |
| #include <linux/random.h> |
| @@ -53,15 +54,26 @@ struct rng_ctx { |
| #define MAXSIZE 128 |
| unsigned int len; |
| struct crypto_rng *drng; |
| + u8 *addtl; |
| + size_t addtl_len; |
| }; |
| |
| -static int rng_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, |
| - int flags) |
| +struct rng_parent_ctx { |
| + struct crypto_rng *drng; |
| + u8 *entropy; |
| +}; |
| + |
| +static void rng_reset_addtl(struct rng_ctx *ctx) |
| { |
| - struct sock *sk = sock->sk; |
| - struct alg_sock *ask = alg_sk(sk); |
| - struct rng_ctx *ctx = ask->private; |
| - int err = -EFAULT; |
| + kzfree(ctx->addtl); |
| + ctx->addtl = NULL; |
| + ctx->addtl_len = 0; |
| +} |
| + |
| +static int _rng_recvmsg(struct crypto_rng *drng, struct msghdr *msg, size_t len, |
| + u8 *addtl, size_t addtl_len) |
| +{ |
| + int err = 0; |
| int genlen = 0; |
| u8 result[MAXSIZE]; |
| |
| @@ -82,7 +94,7 @@ static int rng_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, |
| * seeding as they automatically seed. The X9.31 DRNG will return |
| * an error if it was not seeded properly. |
| */ |
| - genlen = crypto_rng_get_bytes(ctx->drng, result, len); |
| + genlen = crypto_rng_generate(drng, addtl, addtl_len, result, len); |
| if (genlen < 0) |
| return genlen; |
| |
| @@ -92,6 +104,63 @@ static int rng_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, |
| return err ? err : len; |
| } |
| |
| +static int rng_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, |
| + int flags) |
| +{ |
| + struct sock *sk = sock->sk; |
| + struct alg_sock *ask = alg_sk(sk); |
| + struct rng_ctx *ctx = ask->private; |
| + |
| + return _rng_recvmsg(ctx->drng, msg, len, NULL, 0); |
| +} |
| + |
| +static int rng_test_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, |
| + int flags) |
| +{ |
| + struct sock *sk = sock->sk; |
| + struct alg_sock *ask = alg_sk(sk); |
| + struct rng_ctx *ctx = ask->private; |
| + int ret; |
| + |
| + lock_sock(sock->sk); |
| + ret = _rng_recvmsg(ctx->drng, msg, len, ctx->addtl, ctx->addtl_len); |
| + rng_reset_addtl(ctx); |
| + release_sock(sock->sk); |
| + |
| + return ret; |
| +} |
| + |
| +static int rng_test_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) |
| +{ |
| + int err; |
| + struct alg_sock *ask = alg_sk(sock->sk); |
| + struct rng_ctx *ctx = ask->private; |
| + |
| + lock_sock(sock->sk); |
| + if (len > MAXSIZE) { |
| + err = -EMSGSIZE; |
| + goto unlock; |
| + } |
| + |
| + rng_reset_addtl(ctx); |
| + ctx->addtl = kmalloc(len, GFP_KERNEL); |
| + if (!ctx->addtl) { |
| + err = -ENOMEM; |
| + goto unlock; |
| + } |
| + |
| + err = memcpy_from_msg(ctx->addtl, msg, len); |
| + if (err) { |
| + rng_reset_addtl(ctx); |
| + goto unlock; |
| + } |
| + ctx->addtl_len = len; |
| + |
| +unlock: |
| + release_sock(sock->sk); |
| + return err ? err : len; |
| +} |
| + |
| static struct proto_ops algif_rng_ops = { |
| .family = PF_ALG, |
| |
| @@ -113,14 +182,53 @@ static struct proto_ops algif_rng_ops = { |
| .recvmsg = rng_recvmsg, |
| }; |
| |
| +static struct proto_ops __maybe_unused algif_rng_test_ops = { |
| + .family = PF_ALG, |
| + |
| + .connect = sock_no_connect, |
| + .socketpair = sock_no_socketpair, |
| + .getname = sock_no_getname, |
| + .ioctl = sock_no_ioctl, |
| + .listen = sock_no_listen, |
| + .shutdown = sock_no_shutdown, |
| + .mmap = sock_no_mmap, |
| + .bind = sock_no_bind, |
| + .accept = sock_no_accept, |
| + .sendpage = sock_no_sendpage, |
| + |
| + .release = af_alg_release, |
| + .recvmsg = rng_test_recvmsg, |
| + .sendmsg = rng_test_sendmsg, |
| +}; |
| + |
| static void *rng_bind(const char *name, u32 type, u32 mask) |
| { |
| - return crypto_alloc_rng(name, type, mask); |
| + struct rng_parent_ctx *pctx; |
| + struct crypto_rng *rng; |
| + |
| + pctx = kzalloc(sizeof(*pctx), GFP_KERNEL); |
| + if (!pctx) |
| + return ERR_PTR(-ENOMEM); |
| + |
| + rng = crypto_alloc_rng(name, type, mask); |
| + if (IS_ERR(rng)) { |
| + kfree(pctx); |
| + return ERR_CAST(rng); |
| + } |
| + |
| + pctx->drng = rng; |
| + return pctx; |
| } |
| |
| static void rng_release(void *private) |
| { |
| - crypto_free_rng(private); |
| + struct rng_parent_ctx *pctx = private; |
| + if (unlikely(!pctx)) |
| + return; |
| + crypto_free_rng(pctx->drng); |
| + kzfree(pctx->entropy); |
| + kzfree(pctx); |
| + return; |
| } |
| |
| static void rng_sock_destruct(struct sock *sk) |
| @@ -128,6 +236,7 @@ static void rng_sock_destruct(struct sock *sk) |
| struct alg_sock *ask = alg_sk(sk); |
| struct rng_ctx *ctx = ask->private; |
| |
| + rng_reset_addtl(ctx); |
| sock_kfree_s(sk, ctx, ctx->len); |
| af_alg_release_parent(sk); |
| } |
| @@ -135,6 +244,7 @@ static void rng_sock_destruct(struct sock *sk) |
| static int rng_accept_parent(void *private, struct sock *sk) |
| { |
| struct rng_ctx *ctx; |
| + struct rng_parent_ctx *pctx = private; |
| struct alg_sock *ask = alg_sk(sk); |
| unsigned int len = sizeof(*ctx); |
| |
| @@ -143,6 +253,8 @@ static int rng_accept_parent(void *private, struct sock *sk) |
| return -ENOMEM; |
| |
| ctx->len = len; |
| + ctx->addtl = NULL; |
| + ctx->addtl_len = 0; |
| |
| /* |
| * No seeding done at that point -- if multiple accepts are |
| @@ -150,20 +262,59 @@ static int rng_accept_parent(void *private, struct sock *sk) |
| * state of the RNG. |
| */ |
| |
| - ctx->drng = private; |
| + ctx->drng = pctx->drng; |
| ask->private = ctx; |
| sk->sk_destruct = rng_sock_destruct; |
| |
| + /* |
| + * Non NULL pctx->entropy means that CAVP test has been initiated on |
| + * this socket, replace proto_ops algif_rng_ops with algif_rng_test_ops. |
| + */ |
| + if (IS_ENABLED(CONFIG_CRYPTO_USER_API_RNG_CAVP) && pctx->entropy) |
| + sk->sk_socket->ops = &algif_rng_test_ops; |
| return 0; |
| } |
| |
| static int rng_setkey(void *private, const u8 *seed, unsigned int seedlen) |
| { |
| + struct rng_parent_ctx *pctx = private; |
| /* |
| * Check whether seedlen is of sufficient size is done in RNG |
| * implementations. |
| */ |
| - return crypto_rng_reset(private, seed, seedlen); |
| + return crypto_rng_reset(pctx->drng, seed, seedlen); |
| +} |
| + |
| +static int __maybe_unused rng_setentropy(void *private, const u8 *entropy, |
| + unsigned int len) |
| +{ |
| + struct rng_parent_ctx *pctx = private; |
| + u8 *kentropy = NULL; |
| + int i; |
| + |
| + if (!capable(CAP_SYS_ADMIN)) |
| + return -EACCES; |
| + |
| + if (pctx->entropy) |
| + return -EINVAL; |
| + |
| + if (len > MAXSIZE) |
| + return -EMSGSIZE; |
| + |
| + if (len) { |
| + kentropy = (u8*)kmalloc(len, GFP_KERNEL); |
| + memcpy(kentropy, entropy, len); |
| + if (IS_ERR(kentropy)) |
| + return PTR_ERR(kentropy); |
| + } |
| + |
| + crypto_rng_alg(pctx->drng)->set_ent(pctx->drng, kentropy, len); |
| + /* |
| + * Since rng doesn't perform any memory management for the entropy |
| + * buffer, save kentropy pointer to pctx now to free it after use. |
| + */ |
| + pctx->entropy = kentropy; |
| + return 0; |
| } |
| |
| static const struct af_alg_type algif_type_rng = { |
| @@ -173,7 +324,10 @@ static const struct af_alg_type algif_type_rng = { |
| .setkey = rng_setkey, |
| .ops = &algif_rng_ops, |
| .name = "rng", |
| - .owner = THIS_MODULE |
| + .owner = THIS_MODULE, |
| +#ifdef CONFIG_CRYPTO_USER_API_RNG_CAVP |
| + .setentropy = rng_setentropy, |
| +#endif |
| }; |
| |
| static int __init rng_init(void) |
| diff --git a/include/crypto/if_alg.h b/include/crypto/if_alg.h |
| index c1a8d4a41..ca06787f7 100644 |
| --- a/include/crypto/if_alg.h |
| +++ b/include/crypto/if_alg.h |
| @@ -46,6 +46,7 @@ struct af_alg_type { |
| void *(*bind)(const char *name, u32 type, u32 mask); |
| void (*release)(void *private); |
| int (*setkey)(void *private, const u8 *key, unsigned int keylen); |
| + int (*setentropy)(void *private, const u8 *key, unsigned int keylen); |
| int (*accept)(void *private, struct sock *sk); |
| int (*accept_nokey)(void *private, struct sock *sk); |
| int (*setauthsize)(void *private, unsigned int authsize); |
| diff --git a/include/uapi/linux/if_alg.h b/include/uapi/linux/if_alg.h |
| index 769050771..dc52a11ba 100644 |
| --- a/include/uapi/linux/if_alg.h |
| +++ b/include/uapi/linux/if_alg.h |
| @@ -51,6 +51,7 @@ struct af_alg_iv { |
| #define ALG_SET_OP 3 |
| #define ALG_SET_AEAD_ASSOCLEN 4 |
| #define ALG_SET_AEAD_AUTHSIZE 5 |
| +#define ALG_SET_DRBG_ENTROPY 6 |
| |
| /* Operations */ |
| #define ALG_OP_DECRYPT 0 |