| Herbert Xu | 03c8efc | 2010-10-19 21:12:39 +0800 | [diff] [blame] | 1 | /* | 
|  | 2 | * af_alg: User-space algorithm interface | 
|  | 3 | * | 
|  | 4 | * This file provides the user-space API for 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 |  | 
| Arun Sharma | 60063497 | 2011-07-26 16:09:06 -0700 | [diff] [blame] | 15 | #include <linux/atomic.h> | 
| Herbert Xu | 03c8efc | 2010-10-19 21:12:39 +0800 | [diff] [blame] | 16 | #include <crypto/if_alg.h> | 
|  | 17 | #include <linux/crypto.h> | 
|  | 18 | #include <linux/init.h> | 
|  | 19 | #include <linux/kernel.h> | 
|  | 20 | #include <linux/list.h> | 
|  | 21 | #include <linux/module.h> | 
|  | 22 | #include <linux/net.h> | 
|  | 23 | #include <linux/rwsem.h> | 
|  | 24 |  | 
|  | 25 | struct alg_type_list { | 
|  | 26 | const struct af_alg_type *type; | 
|  | 27 | struct list_head list; | 
|  | 28 | }; | 
|  | 29 |  | 
| Randy Dunlap | 0686952 | 2010-12-21 22:22:40 +1100 | [diff] [blame] | 30 | static atomic_long_t alg_memory_allocated; | 
| Herbert Xu | 03c8efc | 2010-10-19 21:12:39 +0800 | [diff] [blame] | 31 |  | 
|  | 32 | static struct proto alg_proto = { | 
|  | 33 | .name			= "ALG", | 
|  | 34 | .owner			= THIS_MODULE, | 
|  | 35 | .memory_allocated	= &alg_memory_allocated, | 
|  | 36 | .obj_size		= sizeof(struct alg_sock), | 
|  | 37 | }; | 
|  | 38 |  | 
|  | 39 | static LIST_HEAD(alg_types); | 
|  | 40 | static DECLARE_RWSEM(alg_types_sem); | 
|  | 41 |  | 
|  | 42 | static const struct af_alg_type *alg_get_type(const char *name) | 
|  | 43 | { | 
|  | 44 | const struct af_alg_type *type = ERR_PTR(-ENOENT); | 
|  | 45 | struct alg_type_list *node; | 
|  | 46 |  | 
|  | 47 | down_read(&alg_types_sem); | 
|  | 48 | list_for_each_entry(node, &alg_types, list) { | 
|  | 49 | if (strcmp(node->type->name, name)) | 
|  | 50 | continue; | 
|  | 51 |  | 
|  | 52 | if (try_module_get(node->type->owner)) | 
|  | 53 | type = node->type; | 
|  | 54 | break; | 
|  | 55 | } | 
|  | 56 | up_read(&alg_types_sem); | 
|  | 57 |  | 
|  | 58 | return type; | 
|  | 59 | } | 
|  | 60 |  | 
|  | 61 | int af_alg_register_type(const struct af_alg_type *type) | 
|  | 62 | { | 
|  | 63 | struct alg_type_list *node; | 
|  | 64 | int err = -EEXIST; | 
|  | 65 |  | 
|  | 66 | down_write(&alg_types_sem); | 
|  | 67 | list_for_each_entry(node, &alg_types, list) { | 
|  | 68 | if (!strcmp(node->type->name, type->name)) | 
|  | 69 | goto unlock; | 
|  | 70 | } | 
|  | 71 |  | 
|  | 72 | node = kmalloc(sizeof(*node), GFP_KERNEL); | 
|  | 73 | err = -ENOMEM; | 
|  | 74 | if (!node) | 
|  | 75 | goto unlock; | 
|  | 76 |  | 
|  | 77 | type->ops->owner = THIS_MODULE; | 
|  | 78 | node->type = type; | 
|  | 79 | list_add(&node->list, &alg_types); | 
|  | 80 | err = 0; | 
|  | 81 |  | 
|  | 82 | unlock: | 
|  | 83 | up_write(&alg_types_sem); | 
|  | 84 |  | 
|  | 85 | return err; | 
|  | 86 | } | 
|  | 87 | EXPORT_SYMBOL_GPL(af_alg_register_type); | 
|  | 88 |  | 
|  | 89 | int af_alg_unregister_type(const struct af_alg_type *type) | 
|  | 90 | { | 
|  | 91 | struct alg_type_list *node; | 
|  | 92 | int err = -ENOENT; | 
|  | 93 |  | 
|  | 94 | down_write(&alg_types_sem); | 
|  | 95 | list_for_each_entry(node, &alg_types, list) { | 
|  | 96 | if (strcmp(node->type->name, type->name)) | 
|  | 97 | continue; | 
|  | 98 |  | 
|  | 99 | list_del(&node->list); | 
|  | 100 | kfree(node); | 
|  | 101 | err = 0; | 
|  | 102 | break; | 
|  | 103 | } | 
|  | 104 | up_write(&alg_types_sem); | 
|  | 105 |  | 
|  | 106 | return err; | 
|  | 107 | } | 
|  | 108 | EXPORT_SYMBOL_GPL(af_alg_unregister_type); | 
|  | 109 |  | 
|  | 110 | static void alg_do_release(const struct af_alg_type *type, void *private) | 
|  | 111 | { | 
|  | 112 | if (!type) | 
|  | 113 | return; | 
|  | 114 |  | 
|  | 115 | type->release(private); | 
|  | 116 | module_put(type->owner); | 
|  | 117 | } | 
|  | 118 |  | 
|  | 119 | int af_alg_release(struct socket *sock) | 
|  | 120 | { | 
|  | 121 | if (sock->sk) | 
|  | 122 | sock_put(sock->sk); | 
|  | 123 | return 0; | 
|  | 124 | } | 
|  | 125 | EXPORT_SYMBOL_GPL(af_alg_release); | 
|  | 126 |  | 
|  | 127 | static int alg_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) | 
|  | 128 | { | 
|  | 129 | struct sock *sk = sock->sk; | 
|  | 130 | struct alg_sock *ask = alg_sk(sk); | 
|  | 131 | struct sockaddr_alg *sa = (void *)uaddr; | 
|  | 132 | const struct af_alg_type *type; | 
|  | 133 | void *private; | 
|  | 134 |  | 
|  | 135 | if (sock->state == SS_CONNECTED) | 
|  | 136 | return -EINVAL; | 
|  | 137 |  | 
|  | 138 | if (addr_len != sizeof(*sa)) | 
|  | 139 | return -EINVAL; | 
|  | 140 |  | 
|  | 141 | sa->salg_type[sizeof(sa->salg_type) - 1] = 0; | 
|  | 142 | sa->salg_name[sizeof(sa->salg_name) - 1] = 0; | 
|  | 143 |  | 
|  | 144 | type = alg_get_type(sa->salg_type); | 
|  | 145 | if (IS_ERR(type) && PTR_ERR(type) == -ENOENT) { | 
|  | 146 | request_module("algif-%s", sa->salg_type); | 
|  | 147 | type = alg_get_type(sa->salg_type); | 
|  | 148 | } | 
|  | 149 |  | 
|  | 150 | if (IS_ERR(type)) | 
|  | 151 | return PTR_ERR(type); | 
|  | 152 |  | 
|  | 153 | private = type->bind(sa->salg_name, sa->salg_feat, sa->salg_mask); | 
|  | 154 | if (IS_ERR(private)) { | 
|  | 155 | module_put(type->owner); | 
|  | 156 | return PTR_ERR(private); | 
|  | 157 | } | 
|  | 158 |  | 
|  | 159 | lock_sock(sk); | 
|  | 160 |  | 
|  | 161 | swap(ask->type, type); | 
|  | 162 | swap(ask->private, private); | 
|  | 163 |  | 
|  | 164 | release_sock(sk); | 
|  | 165 |  | 
|  | 166 | alg_do_release(type, private); | 
|  | 167 |  | 
|  | 168 | return 0; | 
|  | 169 | } | 
|  | 170 |  | 
|  | 171 | static int alg_setkey(struct sock *sk, char __user *ukey, | 
|  | 172 | unsigned int keylen) | 
|  | 173 | { | 
|  | 174 | struct alg_sock *ask = alg_sk(sk); | 
|  | 175 | const struct af_alg_type *type = ask->type; | 
|  | 176 | u8 *key; | 
|  | 177 | int err; | 
|  | 178 |  | 
|  | 179 | key = sock_kmalloc(sk, keylen, GFP_KERNEL); | 
|  | 180 | if (!key) | 
|  | 181 | return -ENOMEM; | 
|  | 182 |  | 
|  | 183 | err = -EFAULT; | 
|  | 184 | if (copy_from_user(key, ukey, keylen)) | 
|  | 185 | goto out; | 
|  | 186 |  | 
|  | 187 | err = type->setkey(ask->private, key, keylen); | 
|  | 188 |  | 
|  | 189 | out: | 
|  | 190 | sock_kfree_s(sk, key, keylen); | 
|  | 191 |  | 
|  | 192 | return err; | 
|  | 193 | } | 
|  | 194 |  | 
|  | 195 | static int alg_setsockopt(struct socket *sock, int level, int optname, | 
|  | 196 | char __user *optval, unsigned int optlen) | 
|  | 197 | { | 
|  | 198 | struct sock *sk = sock->sk; | 
|  | 199 | struct alg_sock *ask = alg_sk(sk); | 
|  | 200 | const struct af_alg_type *type; | 
|  | 201 | int err = -ENOPROTOOPT; | 
|  | 202 |  | 
|  | 203 | lock_sock(sk); | 
|  | 204 | type = ask->type; | 
|  | 205 |  | 
|  | 206 | if (level != SOL_ALG || !type) | 
|  | 207 | goto unlock; | 
|  | 208 |  | 
|  | 209 | switch (optname) { | 
|  | 210 | case ALG_SET_KEY: | 
|  | 211 | if (sock->state == SS_CONNECTED) | 
|  | 212 | goto unlock; | 
|  | 213 | if (!type->setkey) | 
|  | 214 | goto unlock; | 
|  | 215 |  | 
|  | 216 | err = alg_setkey(sk, optval, optlen); | 
|  | 217 | } | 
|  | 218 |  | 
|  | 219 | unlock: | 
|  | 220 | release_sock(sk); | 
|  | 221 |  | 
|  | 222 | return err; | 
|  | 223 | } | 
|  | 224 |  | 
|  | 225 | int af_alg_accept(struct sock *sk, struct socket *newsock) | 
|  | 226 | { | 
|  | 227 | struct alg_sock *ask = alg_sk(sk); | 
|  | 228 | const struct af_alg_type *type; | 
|  | 229 | struct sock *sk2; | 
|  | 230 | int err; | 
|  | 231 |  | 
|  | 232 | lock_sock(sk); | 
|  | 233 | type = ask->type; | 
|  | 234 |  | 
|  | 235 | err = -EINVAL; | 
|  | 236 | if (!type) | 
|  | 237 | goto unlock; | 
|  | 238 |  | 
|  | 239 | sk2 = sk_alloc(sock_net(sk), PF_ALG, GFP_KERNEL, &alg_proto); | 
|  | 240 | err = -ENOMEM; | 
|  | 241 | if (!sk2) | 
|  | 242 | goto unlock; | 
|  | 243 |  | 
|  | 244 | sock_init_data(newsock, sk2); | 
| Miloslav Trmač | 507cad3 | 2010-12-08 14:36:19 +0800 | [diff] [blame] | 245 | sock_graft(sk2, newsock); | 
| Herbert Xu | 03c8efc | 2010-10-19 21:12:39 +0800 | [diff] [blame] | 246 |  | 
|  | 247 | err = type->accept(ask->private, sk2); | 
|  | 248 | if (err) { | 
|  | 249 | sk_free(sk2); | 
|  | 250 | goto unlock; | 
|  | 251 | } | 
|  | 252 |  | 
|  | 253 | sk2->sk_family = PF_ALG; | 
|  | 254 |  | 
|  | 255 | sock_hold(sk); | 
|  | 256 | alg_sk(sk2)->parent = sk; | 
|  | 257 | alg_sk(sk2)->type = type; | 
|  | 258 |  | 
|  | 259 | newsock->ops = type->ops; | 
|  | 260 | newsock->state = SS_CONNECTED; | 
|  | 261 |  | 
|  | 262 | err = 0; | 
|  | 263 |  | 
|  | 264 | unlock: | 
|  | 265 | release_sock(sk); | 
|  | 266 |  | 
|  | 267 | return err; | 
|  | 268 | } | 
|  | 269 | EXPORT_SYMBOL_GPL(af_alg_accept); | 
|  | 270 |  | 
|  | 271 | static int alg_accept(struct socket *sock, struct socket *newsock, int flags) | 
|  | 272 | { | 
|  | 273 | return af_alg_accept(sock->sk, newsock); | 
|  | 274 | } | 
|  | 275 |  | 
|  | 276 | static const struct proto_ops alg_proto_ops = { | 
|  | 277 | .family		=	PF_ALG, | 
|  | 278 | .owner		=	THIS_MODULE, | 
|  | 279 |  | 
|  | 280 | .connect	=	sock_no_connect, | 
|  | 281 | .socketpair	=	sock_no_socketpair, | 
|  | 282 | .getname	=	sock_no_getname, | 
|  | 283 | .ioctl		=	sock_no_ioctl, | 
|  | 284 | .listen		=	sock_no_listen, | 
|  | 285 | .shutdown	=	sock_no_shutdown, | 
|  | 286 | .getsockopt	=	sock_no_getsockopt, | 
|  | 287 | .mmap		=	sock_no_mmap, | 
|  | 288 | .sendpage	=	sock_no_sendpage, | 
|  | 289 | .sendmsg	=	sock_no_sendmsg, | 
|  | 290 | .recvmsg	=	sock_no_recvmsg, | 
|  | 291 | .poll		=	sock_no_poll, | 
|  | 292 |  | 
|  | 293 | .bind		=	alg_bind, | 
|  | 294 | .release	=	af_alg_release, | 
|  | 295 | .setsockopt	=	alg_setsockopt, | 
|  | 296 | .accept		=	alg_accept, | 
|  | 297 | }; | 
|  | 298 |  | 
|  | 299 | static void alg_sock_destruct(struct sock *sk) | 
|  | 300 | { | 
|  | 301 | struct alg_sock *ask = alg_sk(sk); | 
|  | 302 |  | 
|  | 303 | alg_do_release(ask->type, ask->private); | 
|  | 304 | } | 
|  | 305 |  | 
|  | 306 | static int alg_create(struct net *net, struct socket *sock, int protocol, | 
|  | 307 | int kern) | 
|  | 308 | { | 
|  | 309 | struct sock *sk; | 
|  | 310 | int err; | 
|  | 311 |  | 
|  | 312 | if (sock->type != SOCK_SEQPACKET) | 
|  | 313 | return -ESOCKTNOSUPPORT; | 
|  | 314 | if (protocol != 0) | 
|  | 315 | return -EPROTONOSUPPORT; | 
|  | 316 |  | 
|  | 317 | err = -ENOMEM; | 
|  | 318 | sk = sk_alloc(net, PF_ALG, GFP_KERNEL, &alg_proto); | 
|  | 319 | if (!sk) | 
|  | 320 | goto out; | 
|  | 321 |  | 
|  | 322 | sock->ops = &alg_proto_ops; | 
|  | 323 | sock_init_data(sock, sk); | 
|  | 324 |  | 
|  | 325 | sk->sk_family = PF_ALG; | 
|  | 326 | sk->sk_destruct = alg_sock_destruct; | 
|  | 327 |  | 
|  | 328 | return 0; | 
|  | 329 | out: | 
|  | 330 | return err; | 
|  | 331 | } | 
|  | 332 |  | 
|  | 333 | static const struct net_proto_family alg_family = { | 
|  | 334 | .family	=	PF_ALG, | 
|  | 335 | .create	=	alg_create, | 
|  | 336 | .owner	=	THIS_MODULE, | 
|  | 337 | }; | 
|  | 338 |  | 
|  | 339 | int af_alg_make_sg(struct af_alg_sgl *sgl, void __user *addr, int len, | 
|  | 340 | int write) | 
|  | 341 | { | 
|  | 342 | unsigned long from = (unsigned long)addr; | 
|  | 343 | unsigned long npages; | 
|  | 344 | unsigned off; | 
|  | 345 | int err; | 
|  | 346 | int i; | 
|  | 347 |  | 
|  | 348 | err = -EFAULT; | 
|  | 349 | if (!access_ok(write ? VERIFY_READ : VERIFY_WRITE, addr, len)) | 
|  | 350 | goto out; | 
|  | 351 |  | 
|  | 352 | off = from & ~PAGE_MASK; | 
|  | 353 | npages = (off + len + PAGE_SIZE - 1) >> PAGE_SHIFT; | 
|  | 354 | if (npages > ALG_MAX_PAGES) | 
|  | 355 | npages = ALG_MAX_PAGES; | 
|  | 356 |  | 
|  | 357 | err = get_user_pages_fast(from, npages, write, sgl->pages); | 
|  | 358 | if (err < 0) | 
|  | 359 | goto out; | 
|  | 360 |  | 
|  | 361 | npages = err; | 
|  | 362 | err = -EINVAL; | 
|  | 363 | if (WARN_ON(npages == 0)) | 
|  | 364 | goto out; | 
|  | 365 |  | 
|  | 366 | err = 0; | 
|  | 367 |  | 
|  | 368 | sg_init_table(sgl->sg, npages); | 
|  | 369 |  | 
|  | 370 | for (i = 0; i < npages; i++) { | 
|  | 371 | int plen = min_t(int, len, PAGE_SIZE - off); | 
|  | 372 |  | 
|  | 373 | sg_set_page(sgl->sg + i, sgl->pages[i], plen, off); | 
|  | 374 |  | 
|  | 375 | off = 0; | 
|  | 376 | len -= plen; | 
|  | 377 | err += plen; | 
|  | 378 | } | 
|  | 379 |  | 
|  | 380 | out: | 
|  | 381 | return err; | 
|  | 382 | } | 
|  | 383 | EXPORT_SYMBOL_GPL(af_alg_make_sg); | 
|  | 384 |  | 
|  | 385 | void af_alg_free_sg(struct af_alg_sgl *sgl) | 
|  | 386 | { | 
|  | 387 | int i; | 
|  | 388 |  | 
|  | 389 | i = 0; | 
|  | 390 | do { | 
|  | 391 | put_page(sgl->pages[i]); | 
|  | 392 | } while (!sg_is_last(sgl->sg + (i++))); | 
|  | 393 | } | 
|  | 394 | EXPORT_SYMBOL_GPL(af_alg_free_sg); | 
|  | 395 |  | 
|  | 396 | int af_alg_cmsg_send(struct msghdr *msg, struct af_alg_control *con) | 
|  | 397 | { | 
|  | 398 | struct cmsghdr *cmsg; | 
|  | 399 |  | 
|  | 400 | for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) { | 
|  | 401 | if (!CMSG_OK(msg, cmsg)) | 
|  | 402 | return -EINVAL; | 
|  | 403 | if (cmsg->cmsg_level != SOL_ALG) | 
|  | 404 | continue; | 
|  | 405 |  | 
|  | 406 | switch(cmsg->cmsg_type) { | 
|  | 407 | case ALG_SET_IV: | 
|  | 408 | if (cmsg->cmsg_len < CMSG_LEN(sizeof(*con->iv))) | 
|  | 409 | return -EINVAL; | 
|  | 410 | con->iv = (void *)CMSG_DATA(cmsg); | 
|  | 411 | if (cmsg->cmsg_len < CMSG_LEN(con->iv->ivlen + | 
|  | 412 | sizeof(*con->iv))) | 
|  | 413 | return -EINVAL; | 
|  | 414 | break; | 
|  | 415 |  | 
|  | 416 | case ALG_SET_OP: | 
|  | 417 | if (cmsg->cmsg_len < CMSG_LEN(sizeof(u32))) | 
|  | 418 | return -EINVAL; | 
|  | 419 | con->op = *(u32 *)CMSG_DATA(cmsg); | 
|  | 420 | break; | 
|  | 421 |  | 
|  | 422 | default: | 
|  | 423 | return -EINVAL; | 
|  | 424 | } | 
|  | 425 | } | 
|  | 426 |  | 
|  | 427 | return 0; | 
|  | 428 | } | 
|  | 429 | EXPORT_SYMBOL_GPL(af_alg_cmsg_send); | 
|  | 430 |  | 
|  | 431 | int af_alg_wait_for_completion(int err, struct af_alg_completion *completion) | 
|  | 432 | { | 
|  | 433 | switch (err) { | 
|  | 434 | case -EINPROGRESS: | 
|  | 435 | case -EBUSY: | 
|  | 436 | wait_for_completion(&completion->completion); | 
|  | 437 | INIT_COMPLETION(completion->completion); | 
|  | 438 | err = completion->err; | 
|  | 439 | break; | 
|  | 440 | }; | 
|  | 441 |  | 
|  | 442 | return err; | 
|  | 443 | } | 
|  | 444 | EXPORT_SYMBOL_GPL(af_alg_wait_for_completion); | 
|  | 445 |  | 
|  | 446 | void af_alg_complete(struct crypto_async_request *req, int err) | 
|  | 447 | { | 
|  | 448 | struct af_alg_completion *completion = req->data; | 
|  | 449 |  | 
|  | 450 | completion->err = err; | 
|  | 451 | complete(&completion->completion); | 
|  | 452 | } | 
|  | 453 | EXPORT_SYMBOL_GPL(af_alg_complete); | 
|  | 454 |  | 
|  | 455 | static int __init af_alg_init(void) | 
|  | 456 | { | 
|  | 457 | int err = proto_register(&alg_proto, 0); | 
|  | 458 |  | 
|  | 459 | if (err) | 
|  | 460 | goto out; | 
|  | 461 |  | 
|  | 462 | err = sock_register(&alg_family); | 
|  | 463 | if (err != 0) | 
|  | 464 | goto out_unregister_proto; | 
|  | 465 |  | 
|  | 466 | out: | 
|  | 467 | return err; | 
|  | 468 |  | 
|  | 469 | out_unregister_proto: | 
|  | 470 | proto_unregister(&alg_proto); | 
|  | 471 | goto out; | 
|  | 472 | } | 
|  | 473 |  | 
|  | 474 | static void __exit af_alg_exit(void) | 
|  | 475 | { | 
|  | 476 | sock_unregister(PF_ALG); | 
|  | 477 | proto_unregister(&alg_proto); | 
|  | 478 | } | 
|  | 479 |  | 
|  | 480 | module_init(af_alg_init); | 
|  | 481 | module_exit(af_alg_exit); | 
|  | 482 | MODULE_LICENSE("GPL"); | 
|  | 483 | MODULE_ALIAS_NETPROTO(AF_ALG); |