| /* | 
 |  * algif_rng: User-space interface for random number generators | 
 |  * | 
 |  * This file provides the user-space API for random number generators. | 
 |  * | 
 |  * Copyright (C) 2014, Stephan Mueller <smueller@chronox.de> | 
 |  * | 
 |  * Redistribution and use in source and binary forms, with or without | 
 |  * modification, are permitted provided that the following conditions | 
 |  * are met: | 
 |  * 1. Redistributions of source code must retain the above copyright | 
 |  *    notice, and the entire permission notice in its entirety, | 
 |  *    including the disclaimer of warranties. | 
 |  * 2. Redistributions in binary form must reproduce the above copyright | 
 |  *    notice, this list of conditions and the following disclaimer in the | 
 |  *    documentation and/or other materials provided with the distribution. | 
 |  * 3. The name of the author may not be used to endorse or promote | 
 |  *    products derived from this software without specific prior | 
 |  *    written permission. | 
 |  * | 
 |  * ALTERNATIVELY, this product may be distributed under the terms of | 
 |  * the GNU General Public License, in which case the provisions of the GPL2 | 
 |  * are required INSTEAD OF the above restrictions.  (This clause is | 
 |  * necessary due to a potential bad interaction between the GPL and | 
 |  * the restrictions contained in a BSD-style copyright.) | 
 |  * | 
 |  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | 
 |  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | 
 |  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF | 
 |  * WHICH ARE HEREBY DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE | 
 |  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | 
 |  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT | 
 |  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR | 
 |  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | 
 |  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 
 |  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE | 
 |  * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH | 
 |  * DAMAGE. | 
 |  */ | 
 |  | 
 | #include <linux/capability.h> | 
 | #include <linux/module.h> | 
 | #include <crypto/rng.h> | 
 | #include <linux/random.h> | 
 | #include <crypto/if_alg.h> | 
 | #include <linux/net.h> | 
 | #include <net/sock.h> | 
 |  | 
 | MODULE_LICENSE("GPL"); | 
 | MODULE_AUTHOR("Stephan Mueller <smueller@chronox.de>"); | 
 | MODULE_DESCRIPTION("User-space interface for random number generators"); | 
 |  | 
 | struct rng_ctx { | 
 | #define MAXSIZE 128 | 
 | 	unsigned int len; | 
 | 	struct crypto_rng *drng; | 
 | 	u8 *addtl; | 
 | 	size_t addtl_len; | 
 | }; | 
 |  | 
 | struct rng_parent_ctx { | 
 | 	struct crypto_rng *drng; | 
 | 	u8 *entropy; | 
 | }; | 
 |  | 
 | static void rng_reset_addtl(struct rng_ctx *ctx) | 
 | { | 
 | 	kfree_sensitive(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]; | 
 |  | 
 | 	if (len == 0) | 
 | 		return 0; | 
 | 	if (len > MAXSIZE) | 
 | 		len = MAXSIZE; | 
 |  | 
 | 	/* | 
 | 	 * although not strictly needed, this is a precaution against coding | 
 | 	 * errors | 
 | 	 */ | 
 | 	memset(result, 0, len); | 
 |  | 
 | 	/* | 
 | 	 * The enforcement of a proper seeding of an RNG is done within an | 
 | 	 * RNG implementation. Some RNGs (DRBG, krng) do not need specific | 
 | 	 * seeding as they automatically seed. The X9.31 DRNG will return | 
 | 	 * an error if it was not seeded properly. | 
 | 	 */ | 
 | 	genlen = crypto_rng_generate(drng, addtl, addtl_len, result, len); | 
 | 	if (genlen < 0) | 
 | 		return genlen; | 
 |  | 
 | 	err = memcpy_to_msg(msg, result, len); | 
 | 	memzero_explicit(result, 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, | 
 |  | 
 | 	.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, | 
 | 	.sendmsg	=	sock_no_sendmsg, | 
 | 	.sendpage	=	sock_no_sendpage, | 
 |  | 
 | 	.release	=	af_alg_release, | 
 | 	.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) | 
 | { | 
 | 	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) | 
 | { | 
 | 	struct rng_parent_ctx *pctx = private; | 
 |  | 
 | 	if (unlikely(!pctx)) | 
 | 		return; | 
 | 	crypto_free_rng(pctx->drng); | 
 | 	kfree_sensitive(pctx->entropy); | 
 | 	kfree_sensitive(pctx); | 
 | } | 
 |  | 
 | 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); | 
 | } | 
 |  | 
 | 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); | 
 |  | 
 | 	ctx = sock_kmalloc(sk, len, GFP_KERNEL); | 
 | 	if (!ctx) | 
 | 		return -ENOMEM; | 
 |  | 
 | 	ctx->len = len; | 
 | 	ctx->addtl = NULL; | 
 | 	ctx->addtl_len = 0; | 
 |  | 
 | 	/* | 
 | 	 * No seeding done at that point -- if multiple accepts are | 
 | 	 * done on one RNG instance, each resulting FD points to the same | 
 | 	 * state of the RNG. | 
 | 	 */ | 
 |  | 
 | 	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(pctx->drng, seed, seedlen); | 
 | } | 
 |  | 
 | static int __maybe_unused rng_setentropy(void *private, sockptr_t entropy, | 
 | 					 unsigned int len) | 
 | { | 
 | 	struct rng_parent_ctx *pctx = private; | 
 | 	u8 *kentropy = NULL; | 
 |  | 
 | 	if (!capable(CAP_SYS_ADMIN)) | 
 | 		return -EACCES; | 
 |  | 
 | 	if (pctx->entropy) | 
 | 		return -EINVAL; | 
 |  | 
 | 	if (len > MAXSIZE) | 
 | 		return -EMSGSIZE; | 
 |  | 
 | 	if (len) { | 
 | 		kentropy = memdup_sockptr(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 = { | 
 | 	.bind		=	rng_bind, | 
 | 	.release	=	rng_release, | 
 | 	.accept		=	rng_accept_parent, | 
 | 	.setkey		=	rng_setkey, | 
 | #ifdef CONFIG_CRYPTO_USER_API_RNG_CAVP | 
 | 	.setentropy	=	rng_setentropy, | 
 | #endif | 
 | 	.ops		=	&algif_rng_ops, | 
 | 	.name		=	"rng", | 
 | 	.owner		=	THIS_MODULE | 
 | }; | 
 |  | 
 | static int __init rng_init(void) | 
 | { | 
 | 	return af_alg_register_type(&algif_type_rng); | 
 | } | 
 |  | 
 | static void __exit rng_exit(void) | 
 | { | 
 | 	int err = af_alg_unregister_type(&algif_type_rng); | 
 | 	BUG_ON(err); | 
 | } | 
 |  | 
 | module_init(rng_init); | 
 | module_exit(rng_exit); |