| Herbert Xu | fe869cd | 2010-10-19 21:23:00 +0800 | [diff] [blame] | 1 | /* | 
 | 2 |  * algif_hash: User-space interface for hash algorithms | 
 | 3 |  * | 
 | 4 |  * This file provides the user-space API for hash algorithms. | 
 | 5 |  * | 
 | 6 |  * Copyright (c) 2010 Herbert Xu <herbert@gondor.apana.org.au> | 
 | 7 |  * | 
 | 8 |  * This program is free software; you can redistribute it and/or modify it | 
 | 9 |  * under the terms of the GNU General Public License as published by the Free | 
 | 10 |  * Software Foundation; either version 2 of the License, or (at your option) | 
 | 11 |  * any later version. | 
 | 12 |  * | 
 | 13 |  */ | 
 | 14 |  | 
 | 15 | #include <crypto/hash.h> | 
 | 16 | #include <crypto/if_alg.h> | 
 | 17 | #include <linux/init.h> | 
 | 18 | #include <linux/kernel.h> | 
 | 19 | #include <linux/mm.h> | 
 | 20 | #include <linux/module.h> | 
 | 21 | #include <linux/net.h> | 
 | 22 | #include <net/sock.h> | 
 | 23 |  | 
 | 24 | struct hash_ctx { | 
 | 25 | 	struct af_alg_sgl sgl; | 
 | 26 |  | 
 | 27 | 	u8 *result; | 
 | 28 |  | 
 | 29 | 	struct af_alg_completion completion; | 
 | 30 |  | 
 | 31 | 	unsigned int len; | 
 | 32 | 	bool more; | 
 | 33 |  | 
 | 34 | 	struct ahash_request req; | 
 | 35 | }; | 
 | 36 |  | 
 | 37 | static int hash_sendmsg(struct kiocb *unused, struct socket *sock, | 
 | 38 | 			struct msghdr *msg, size_t ignored) | 
 | 39 | { | 
 | 40 | 	int limit = ALG_MAX_PAGES * PAGE_SIZE; | 
 | 41 | 	struct sock *sk = sock->sk; | 
 | 42 | 	struct alg_sock *ask = alg_sk(sk); | 
 | 43 | 	struct hash_ctx *ctx = ask->private; | 
 | 44 | 	unsigned long iovlen; | 
 | 45 | 	struct iovec *iov; | 
 | 46 | 	long copied = 0; | 
 | 47 | 	int err; | 
 | 48 |  | 
 | 49 | 	if (limit > sk->sk_sndbuf) | 
 | 50 | 		limit = sk->sk_sndbuf; | 
 | 51 |  | 
 | 52 | 	lock_sock(sk); | 
 | 53 | 	if (!ctx->more) { | 
 | 54 | 		err = crypto_ahash_init(&ctx->req); | 
 | 55 | 		if (err) | 
 | 56 | 			goto unlock; | 
 | 57 | 	} | 
 | 58 |  | 
 | 59 | 	ctx->more = 0; | 
 | 60 |  | 
 | 61 | 	for (iov = msg->msg_iov, iovlen = msg->msg_iovlen; iovlen > 0; | 
 | 62 | 	     iovlen--, iov++) { | 
 | 63 | 		unsigned long seglen = iov->iov_len; | 
 | 64 | 		char __user *from = iov->iov_base; | 
 | 65 |  | 
 | 66 | 		while (seglen) { | 
 | 67 | 			int len = min_t(unsigned long, seglen, limit); | 
 | 68 | 			int newlen; | 
 | 69 |  | 
 | 70 | 			newlen = af_alg_make_sg(&ctx->sgl, from, len, 0); | 
 | 71 | 			if (newlen < 0) | 
 | 72 | 				goto unlock; | 
 | 73 |  | 
 | 74 | 			ahash_request_set_crypt(&ctx->req, ctx->sgl.sg, NULL, | 
 | 75 | 						newlen); | 
 | 76 |  | 
 | 77 | 			err = af_alg_wait_for_completion( | 
 | 78 | 				crypto_ahash_update(&ctx->req), | 
 | 79 | 				&ctx->completion); | 
 | 80 |  | 
 | 81 | 			af_alg_free_sg(&ctx->sgl); | 
 | 82 |  | 
 | 83 | 			if (err) | 
 | 84 | 				goto unlock; | 
 | 85 |  | 
 | 86 | 			seglen -= newlen; | 
 | 87 | 			from += newlen; | 
 | 88 | 			copied += newlen; | 
 | 89 | 		} | 
 | 90 | 	} | 
 | 91 |  | 
 | 92 | 	err = 0; | 
 | 93 |  | 
 | 94 | 	ctx->more = msg->msg_flags & MSG_MORE; | 
 | 95 | 	if (!ctx->more) { | 
 | 96 | 		ahash_request_set_crypt(&ctx->req, NULL, ctx->result, 0); | 
 | 97 | 		err = af_alg_wait_for_completion(crypto_ahash_final(&ctx->req), | 
 | 98 | 						 &ctx->completion); | 
 | 99 | 	} | 
 | 100 |  | 
 | 101 | unlock: | 
 | 102 | 	release_sock(sk); | 
 | 103 |  | 
 | 104 | 	return err ?: copied; | 
 | 105 | } | 
 | 106 |  | 
 | 107 | static ssize_t hash_sendpage(struct socket *sock, struct page *page, | 
 | 108 | 			     int offset, size_t size, int flags) | 
 | 109 | { | 
 | 110 | 	struct sock *sk = sock->sk; | 
 | 111 | 	struct alg_sock *ask = alg_sk(sk); | 
 | 112 | 	struct hash_ctx *ctx = ask->private; | 
 | 113 | 	int err; | 
 | 114 |  | 
 | 115 | 	lock_sock(sk); | 
 | 116 | 	sg_init_table(ctx->sgl.sg, 1); | 
 | 117 | 	sg_set_page(ctx->sgl.sg, page, size, offset); | 
 | 118 |  | 
 | 119 | 	ahash_request_set_crypt(&ctx->req, ctx->sgl.sg, ctx->result, size); | 
 | 120 |  | 
 | 121 | 	if (!(flags & MSG_MORE)) { | 
 | 122 | 		if (ctx->more) | 
 | 123 | 			err = crypto_ahash_finup(&ctx->req); | 
 | 124 | 		else | 
 | 125 | 			err = crypto_ahash_digest(&ctx->req); | 
 | 126 | 	} else { | 
 | 127 | 		if (!ctx->more) { | 
 | 128 | 			err = crypto_ahash_init(&ctx->req); | 
 | 129 | 			if (err) | 
 | 130 | 				goto unlock; | 
 | 131 | 		} | 
 | 132 |  | 
 | 133 | 		err = crypto_ahash_update(&ctx->req); | 
 | 134 | 	} | 
 | 135 |  | 
 | 136 | 	err = af_alg_wait_for_completion(err, &ctx->completion); | 
 | 137 | 	if (err) | 
 | 138 | 		goto unlock; | 
 | 139 |  | 
 | 140 | 	ctx->more = flags & MSG_MORE; | 
 | 141 |  | 
 | 142 | unlock: | 
 | 143 | 	release_sock(sk); | 
 | 144 |  | 
 | 145 | 	return err ?: size; | 
 | 146 | } | 
 | 147 |  | 
 | 148 | static int hash_recvmsg(struct kiocb *unused, struct socket *sock, | 
 | 149 | 			struct msghdr *msg, size_t len, int flags) | 
 | 150 | { | 
 | 151 | 	struct sock *sk = sock->sk; | 
 | 152 | 	struct alg_sock *ask = alg_sk(sk); | 
 | 153 | 	struct hash_ctx *ctx = ask->private; | 
 | 154 | 	unsigned ds = crypto_ahash_digestsize(crypto_ahash_reqtfm(&ctx->req)); | 
 | 155 | 	int err; | 
 | 156 |  | 
 | 157 | 	if (len > ds) | 
 | 158 | 		len = ds; | 
 | 159 | 	else if (len < ds) | 
 | 160 | 		msg->msg_flags |= MSG_TRUNC; | 
 | 161 |  | 
 | 162 | 	lock_sock(sk); | 
 | 163 | 	if (ctx->more) { | 
 | 164 | 		ctx->more = 0; | 
 | 165 | 		ahash_request_set_crypt(&ctx->req, NULL, ctx->result, 0); | 
 | 166 | 		err = af_alg_wait_for_completion(crypto_ahash_final(&ctx->req), | 
 | 167 | 						 &ctx->completion); | 
 | 168 | 		if (err) | 
 | 169 | 			goto unlock; | 
 | 170 | 	} | 
 | 171 |  | 
 | 172 | 	err = memcpy_toiovec(msg->msg_iov, ctx->result, len); | 
 | 173 |  | 
 | 174 | unlock: | 
 | 175 | 	release_sock(sk); | 
 | 176 |  | 
 | 177 | 	return err ?: len; | 
 | 178 | } | 
 | 179 |  | 
 | 180 | static int hash_accept(struct socket *sock, struct socket *newsock, int flags) | 
 | 181 | { | 
 | 182 | 	struct sock *sk = sock->sk; | 
 | 183 | 	struct alg_sock *ask = alg_sk(sk); | 
 | 184 | 	struct hash_ctx *ctx = ask->private; | 
 | 185 | 	struct ahash_request *req = &ctx->req; | 
 | 186 | 	char state[crypto_ahash_statesize(crypto_ahash_reqtfm(req))]; | 
 | 187 | 	struct sock *sk2; | 
 | 188 | 	struct alg_sock *ask2; | 
 | 189 | 	struct hash_ctx *ctx2; | 
 | 190 | 	int err; | 
 | 191 |  | 
 | 192 | 	err = crypto_ahash_export(req, state); | 
 | 193 | 	if (err) | 
 | 194 | 		return err; | 
 | 195 |  | 
 | 196 | 	err = af_alg_accept(ask->parent, newsock); | 
 | 197 | 	if (err) | 
 | 198 | 		return err; | 
 | 199 |  | 
 | 200 | 	sk2 = newsock->sk; | 
 | 201 | 	ask2 = alg_sk(sk2); | 
 | 202 | 	ctx2 = ask2->private; | 
 | 203 | 	ctx2->more = 1; | 
 | 204 |  | 
 | 205 | 	err = crypto_ahash_import(&ctx2->req, state); | 
 | 206 | 	if (err) { | 
 | 207 | 		sock_orphan(sk2); | 
 | 208 | 		sock_put(sk2); | 
 | 209 | 	} | 
 | 210 |  | 
 | 211 | 	return err; | 
 | 212 | } | 
 | 213 |  | 
 | 214 | static struct proto_ops algif_hash_ops = { | 
 | 215 | 	.family		=	PF_ALG, | 
 | 216 |  | 
 | 217 | 	.connect	=	sock_no_connect, | 
 | 218 | 	.socketpair	=	sock_no_socketpair, | 
 | 219 | 	.getname	=	sock_no_getname, | 
 | 220 | 	.ioctl		=	sock_no_ioctl, | 
 | 221 | 	.listen		=	sock_no_listen, | 
 | 222 | 	.shutdown	=	sock_no_shutdown, | 
 | 223 | 	.getsockopt	=	sock_no_getsockopt, | 
 | 224 | 	.mmap		=	sock_no_mmap, | 
 | 225 | 	.bind		=	sock_no_bind, | 
 | 226 | 	.setsockopt	=	sock_no_setsockopt, | 
 | 227 | 	.poll		=	sock_no_poll, | 
 | 228 |  | 
 | 229 | 	.release	=	af_alg_release, | 
 | 230 | 	.sendmsg	=	hash_sendmsg, | 
 | 231 | 	.sendpage	=	hash_sendpage, | 
 | 232 | 	.recvmsg	=	hash_recvmsg, | 
 | 233 | 	.accept		=	hash_accept, | 
 | 234 | }; | 
 | 235 |  | 
 | 236 | static void *hash_bind(const char *name, u32 type, u32 mask) | 
 | 237 | { | 
 | 238 | 	return crypto_alloc_ahash(name, type, mask); | 
 | 239 | } | 
 | 240 |  | 
 | 241 | static void hash_release(void *private) | 
 | 242 | { | 
 | 243 | 	crypto_free_ahash(private); | 
 | 244 | } | 
 | 245 |  | 
 | 246 | static int hash_setkey(void *private, const u8 *key, unsigned int keylen) | 
 | 247 | { | 
 | 248 | 	return crypto_ahash_setkey(private, key, keylen); | 
 | 249 | } | 
 | 250 |  | 
 | 251 | static void hash_sock_destruct(struct sock *sk) | 
 | 252 | { | 
 | 253 | 	struct alg_sock *ask = alg_sk(sk); | 
 | 254 | 	struct hash_ctx *ctx = ask->private; | 
 | 255 |  | 
 | 256 | 	sock_kfree_s(sk, ctx->result, | 
 | 257 | 		     crypto_ahash_digestsize(crypto_ahash_reqtfm(&ctx->req))); | 
 | 258 | 	sock_kfree_s(sk, ctx, ctx->len); | 
 | 259 | 	af_alg_release_parent(sk); | 
 | 260 | } | 
 | 261 |  | 
 | 262 | static int hash_accept_parent(void *private, struct sock *sk) | 
 | 263 | { | 
 | 264 | 	struct hash_ctx *ctx; | 
 | 265 | 	struct alg_sock *ask = alg_sk(sk); | 
 | 266 | 	unsigned len = sizeof(*ctx) + crypto_ahash_reqsize(private); | 
 | 267 | 	unsigned ds = crypto_ahash_digestsize(private); | 
 | 268 |  | 
 | 269 | 	ctx = sock_kmalloc(sk, len, GFP_KERNEL); | 
 | 270 | 	if (!ctx) | 
 | 271 | 		return -ENOMEM; | 
 | 272 |  | 
 | 273 | 	ctx->result = sock_kmalloc(sk, ds, GFP_KERNEL); | 
 | 274 | 	if (!ctx->result) { | 
 | 275 | 		sock_kfree_s(sk, ctx, len); | 
 | 276 | 		return -ENOMEM; | 
 | 277 | 	} | 
 | 278 |  | 
 | 279 | 	memset(ctx->result, 0, ds); | 
 | 280 |  | 
 | 281 | 	ctx->len = len; | 
 | 282 | 	ctx->more = 0; | 
 | 283 | 	af_alg_init_completion(&ctx->completion); | 
 | 284 |  | 
 | 285 | 	ask->private = ctx; | 
 | 286 |  | 
 | 287 | 	ahash_request_set_tfm(&ctx->req, private); | 
 | 288 | 	ahash_request_set_callback(&ctx->req, CRYPTO_TFM_REQ_MAY_BACKLOG, | 
 | 289 | 				   af_alg_complete, &ctx->completion); | 
 | 290 |  | 
 | 291 | 	sk->sk_destruct = hash_sock_destruct; | 
 | 292 |  | 
 | 293 | 	return 0; | 
 | 294 | } | 
 | 295 |  | 
 | 296 | static const struct af_alg_type algif_type_hash = { | 
 | 297 | 	.bind		=	hash_bind, | 
 | 298 | 	.release	=	hash_release, | 
 | 299 | 	.setkey		=	hash_setkey, | 
 | 300 | 	.accept		=	hash_accept_parent, | 
 | 301 | 	.ops		=	&algif_hash_ops, | 
 | 302 | 	.name		=	"hash", | 
 | 303 | 	.owner		=	THIS_MODULE | 
 | 304 | }; | 
 | 305 |  | 
 | 306 | static int __init algif_hash_init(void) | 
 | 307 | { | 
 | 308 | 	return af_alg_register_type(&algif_type_hash); | 
 | 309 | } | 
 | 310 |  | 
 | 311 | static void __exit algif_hash_exit(void) | 
 | 312 | { | 
 | 313 | 	int err = af_alg_unregister_type(&algif_type_hash); | 
 | 314 | 	BUG_ON(err); | 
 | 315 | } | 
 | 316 |  | 
 | 317 | module_init(algif_hash_init); | 
 | 318 | module_exit(algif_hash_exit); | 
 | 319 | MODULE_LICENSE("GPL"); |