Blame view

crypto/af_alg.c 10.6 KB
03c8efc1f   Herbert Xu   crypto: af_alg - ...
1
2
3
4
5
6
7
8
9
10
11
12
13
  /*
   * af_alg: User-space algorithm interface
   *
   * This file provides the user-space API for algorithms.
   *
   * Copyright (c) 2010 Herbert Xu <herbert@gondor.apana.org.au>
   *
   * This program is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License as published by the Free
   * Software Foundation; either version 2 of the License, or (at your option)
   * any later version.
   *
   */
60063497a   Arun Sharma   atomic: use <linu...
14
  #include <linux/atomic.h>
03c8efc1f   Herbert Xu   crypto: af_alg - ...
15
16
17
18
19
20
21
22
  #include <crypto/if_alg.h>
  #include <linux/crypto.h>
  #include <linux/init.h>
  #include <linux/kernel.h>
  #include <linux/list.h>
  #include <linux/module.h>
  #include <linux/net.h>
  #include <linux/rwsem.h>
4c63f83c2   Milan Broz   crypto: af_alg - ...
23
  #include <linux/security.h>
03c8efc1f   Herbert Xu   crypto: af_alg - ...
24
25
26
27
28
  
  struct alg_type_list {
  	const struct af_alg_type *type;
  	struct list_head list;
  };
068695245   Randy Dunlap   crypto: af_alg - ...
29
  static atomic_long_t alg_memory_allocated;
03c8efc1f   Herbert Xu   crypto: af_alg - ...
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
  
  static struct proto alg_proto = {
  	.name			= "ALG",
  	.owner			= THIS_MODULE,
  	.memory_allocated	= &alg_memory_allocated,
  	.obj_size		= sizeof(struct alg_sock),
  };
  
  static LIST_HEAD(alg_types);
  static DECLARE_RWSEM(alg_types_sem);
  
  static const struct af_alg_type *alg_get_type(const char *name)
  {
  	const struct af_alg_type *type = ERR_PTR(-ENOENT);
  	struct alg_type_list *node;
  
  	down_read(&alg_types_sem);
  	list_for_each_entry(node, &alg_types, list) {
  		if (strcmp(node->type->name, name))
  			continue;
  
  		if (try_module_get(node->type->owner))
  			type = node->type;
  		break;
  	}
  	up_read(&alg_types_sem);
  
  	return type;
  }
  
  int af_alg_register_type(const struct af_alg_type *type)
  {
  	struct alg_type_list *node;
  	int err = -EEXIST;
  
  	down_write(&alg_types_sem);
  	list_for_each_entry(node, &alg_types, list) {
  		if (!strcmp(node->type->name, type->name))
  			goto unlock;
  	}
  
  	node = kmalloc(sizeof(*node), GFP_KERNEL);
  	err = -ENOMEM;
  	if (!node)
  		goto unlock;
  
  	type->ops->owner = THIS_MODULE;
37766586c   Herbert Xu   crypto: af_alg - ...
77
78
  	if (type->ops_nokey)
  		type->ops_nokey->owner = THIS_MODULE;
03c8efc1f   Herbert Xu   crypto: af_alg - ...
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
  	node->type = type;
  	list_add(&node->list, &alg_types);
  	err = 0;
  
  unlock:
  	up_write(&alg_types_sem);
  
  	return err;
  }
  EXPORT_SYMBOL_GPL(af_alg_register_type);
  
  int af_alg_unregister_type(const struct af_alg_type *type)
  {
  	struct alg_type_list *node;
  	int err = -ENOENT;
  
  	down_write(&alg_types_sem);
  	list_for_each_entry(node, &alg_types, list) {
  		if (strcmp(node->type->name, type->name))
  			continue;
  
  		list_del(&node->list);
  		kfree(node);
  		err = 0;
  		break;
  	}
  	up_write(&alg_types_sem);
  
  	return err;
  }
  EXPORT_SYMBOL_GPL(af_alg_unregister_type);
  
  static void alg_do_release(const struct af_alg_type *type, void *private)
  {
  	if (!type)
  		return;
  
  	type->release(private);
  	module_put(type->owner);
  }
  
  int af_alg_release(struct socket *sock)
  {
  	if (sock->sk)
  		sock_put(sock->sk);
  	return 0;
  }
  EXPORT_SYMBOL_GPL(af_alg_release);
c840ac6af   Herbert Xu   crypto: af_alg - ...
127
128
129
  void af_alg_release_parent(struct sock *sk)
  {
  	struct alg_sock *ask = alg_sk(sk);
a6a48c565   Herbert Xu   crypto: af_alg - ...
130
131
  	unsigned int nokey = ask->nokey_refcnt;
  	bool last = nokey && !ask->refcnt;
c840ac6af   Herbert Xu   crypto: af_alg - ...
132
133
134
135
136
  
  	sk = ask->parent;
  	ask = alg_sk(sk);
  
  	lock_sock(sk);
a6a48c565   Herbert Xu   crypto: af_alg - ...
137
138
139
  	ask->nokey_refcnt -= nokey;
  	if (!last)
  		last = !--ask->refcnt;
c840ac6af   Herbert Xu   crypto: af_alg - ...
140
141
142
143
144
145
  	release_sock(sk);
  
  	if (last)
  		sock_put(sk);
  }
  EXPORT_SYMBOL_GPL(af_alg_release_parent);
03c8efc1f   Herbert Xu   crypto: af_alg - ...
146
147
  static int alg_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
  {
15539de5c   Herbert Xu   crypto: af_alg - ...
148
  	const u32 forbidden = CRYPTO_ALG_INTERNAL;
03c8efc1f   Herbert Xu   crypto: af_alg - ...
149
150
151
152
153
  	struct sock *sk = sock->sk;
  	struct alg_sock *ask = alg_sk(sk);
  	struct sockaddr_alg *sa = (void *)uaddr;
  	const struct af_alg_type *type;
  	void *private;
c840ac6af   Herbert Xu   crypto: af_alg - ...
154
  	int err;
03c8efc1f   Herbert Xu   crypto: af_alg - ...
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
  
  	if (sock->state == SS_CONNECTED)
  		return -EINVAL;
  
  	if (addr_len != sizeof(*sa))
  		return -EINVAL;
  
  	sa->salg_type[sizeof(sa->salg_type) - 1] = 0;
  	sa->salg_name[sizeof(sa->salg_name) - 1] = 0;
  
  	type = alg_get_type(sa->salg_type);
  	if (IS_ERR(type) && PTR_ERR(type) == -ENOENT) {
  		request_module("algif-%s", sa->salg_type);
  		type = alg_get_type(sa->salg_type);
  	}
  
  	if (IS_ERR(type))
  		return PTR_ERR(type);
15539de5c   Herbert Xu   crypto: af_alg - ...
173
174
175
  	private = type->bind(sa->salg_name,
  			     sa->salg_feat & ~forbidden,
  			     sa->salg_mask & ~forbidden);
03c8efc1f   Herbert Xu   crypto: af_alg - ...
176
177
178
179
  	if (IS_ERR(private)) {
  		module_put(type->owner);
  		return PTR_ERR(private);
  	}
c840ac6af   Herbert Xu   crypto: af_alg - ...
180
  	err = -EBUSY;
03c8efc1f   Herbert Xu   crypto: af_alg - ...
181
  	lock_sock(sk);
a6a48c565   Herbert Xu   crypto: af_alg - ...
182
  	if (ask->refcnt | ask->nokey_refcnt)
c840ac6af   Herbert Xu   crypto: af_alg - ...
183
  		goto unlock;
03c8efc1f   Herbert Xu   crypto: af_alg - ...
184
185
186
  
  	swap(ask->type, type);
  	swap(ask->private, private);
c840ac6af   Herbert Xu   crypto: af_alg - ...
187
188
189
  	err = 0;
  
  unlock:
03c8efc1f   Herbert Xu   crypto: af_alg - ...
190
191
192
  	release_sock(sk);
  
  	alg_do_release(type, private);
c840ac6af   Herbert Xu   crypto: af_alg - ...
193
  	return err;
03c8efc1f   Herbert Xu   crypto: af_alg - ...
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
  }
  
  static int alg_setkey(struct sock *sk, char __user *ukey,
  		      unsigned int keylen)
  {
  	struct alg_sock *ask = alg_sk(sk);
  	const struct af_alg_type *type = ask->type;
  	u8 *key;
  	int err;
  
  	key = sock_kmalloc(sk, keylen, GFP_KERNEL);
  	if (!key)
  		return -ENOMEM;
  
  	err = -EFAULT;
  	if (copy_from_user(key, ukey, keylen))
  		goto out;
  
  	err = type->setkey(ask->private, key, keylen);
  
  out:
ad202c8c1   Stephan Mueller   crypto: af_alg - ...
215
  	sock_kzfree_s(sk, key, keylen);
03c8efc1f   Herbert Xu   crypto: af_alg - ...
216
217
218
219
220
221
222
223
224
225
  
  	return err;
  }
  
  static int alg_setsockopt(struct socket *sock, int level, int optname,
  			  char __user *optval, unsigned int optlen)
  {
  	struct sock *sk = sock->sk;
  	struct alg_sock *ask = alg_sk(sk);
  	const struct af_alg_type *type;
c840ac6af   Herbert Xu   crypto: af_alg - ...
226
  	int err = -EBUSY;
03c8efc1f   Herbert Xu   crypto: af_alg - ...
227
228
  
  	lock_sock(sk);
c840ac6af   Herbert Xu   crypto: af_alg - ...
229
230
  	if (ask->refcnt)
  		goto unlock;
03c8efc1f   Herbert Xu   crypto: af_alg - ...
231
  	type = ask->type;
c840ac6af   Herbert Xu   crypto: af_alg - ...
232
  	err = -ENOPROTOOPT;
03c8efc1f   Herbert Xu   crypto: af_alg - ...
233
234
235
236
237
238
239
240
241
242
243
  	if (level != SOL_ALG || !type)
  		goto unlock;
  
  	switch (optname) {
  	case ALG_SET_KEY:
  		if (sock->state == SS_CONNECTED)
  			goto unlock;
  		if (!type->setkey)
  			goto unlock;
  
  		err = alg_setkey(sk, optval, optlen);
25fb8638e   Stephan Mueller   crypto: af_alg - ...
244
245
246
247
248
249
250
  		break;
  	case ALG_SET_AEAD_AUTHSIZE:
  		if (sock->state == SS_CONNECTED)
  			goto unlock;
  		if (!type->setauthsize)
  			goto unlock;
  		err = type->setauthsize(ask->private, optlen);
03c8efc1f   Herbert Xu   crypto: af_alg - ...
251
252
253
254
255
256
257
258
259
260
261
262
263
  	}
  
  unlock:
  	release_sock(sk);
  
  	return err;
  }
  
  int af_alg_accept(struct sock *sk, struct socket *newsock)
  {
  	struct alg_sock *ask = alg_sk(sk);
  	const struct af_alg_type *type;
  	struct sock *sk2;
6a935170a   Herbert Xu   crypto: af_alg - ...
264
  	unsigned int nokey;
03c8efc1f   Herbert Xu   crypto: af_alg - ...
265
266
267
268
269
270
271
272
  	int err;
  
  	lock_sock(sk);
  	type = ask->type;
  
  	err = -EINVAL;
  	if (!type)
  		goto unlock;
11aa9c28b   Eric W. Biederman   net: Pass kern fr...
273
  	sk2 = sk_alloc(sock_net(sk), PF_ALG, GFP_KERNEL, &alg_proto, 0);
03c8efc1f   Herbert Xu   crypto: af_alg - ...
274
275
276
277
278
  	err = -ENOMEM;
  	if (!sk2)
  		goto unlock;
  
  	sock_init_data(newsock, sk2);
507cad355   Miloslav Trmač   crypto: af_alg - ...
279
  	sock_graft(sk2, newsock);
4c63f83c2   Milan Broz   crypto: af_alg - ...
280
  	security_sk_clone(sk, sk2);
03c8efc1f   Herbert Xu   crypto: af_alg - ...
281
282
  
  	err = type->accept(ask->private, sk2);
37766586c   Herbert Xu   crypto: af_alg - ...
283
284
285
286
  
  	nokey = err == -ENOKEY;
  	if (nokey && type->accept_nokey)
  		err = type->accept_nokey(ask->private, sk2);
a383292c8   Herbert Xu   crypto: af_alg - ...
287
  	if (err)
03c8efc1f   Herbert Xu   crypto: af_alg - ...
288
  		goto unlock;
03c8efc1f   Herbert Xu   crypto: af_alg - ...
289
290
  
  	sk2->sk_family = PF_ALG;
37766586c   Herbert Xu   crypto: af_alg - ...
291
  	if (nokey || !ask->refcnt++)
c840ac6af   Herbert Xu   crypto: af_alg - ...
292
  		sock_hold(sk);
a6a48c565   Herbert Xu   crypto: af_alg - ...
293
  	ask->nokey_refcnt += nokey;
03c8efc1f   Herbert Xu   crypto: af_alg - ...
294
295
  	alg_sk(sk2)->parent = sk;
  	alg_sk(sk2)->type = type;
6a935170a   Herbert Xu   crypto: af_alg - ...
296
  	alg_sk(sk2)->nokey_refcnt = nokey;
03c8efc1f   Herbert Xu   crypto: af_alg - ...
297
298
299
  
  	newsock->ops = type->ops;
  	newsock->state = SS_CONNECTED;
37766586c   Herbert Xu   crypto: af_alg - ...
300
301
  	if (nokey)
  		newsock->ops = type->ops_nokey;
03c8efc1f   Herbert Xu   crypto: af_alg - ...
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
  	err = 0;
  
  unlock:
  	release_sock(sk);
  
  	return err;
  }
  EXPORT_SYMBOL_GPL(af_alg_accept);
  
  static int alg_accept(struct socket *sock, struct socket *newsock, int flags)
  {
  	return af_alg_accept(sock->sk, newsock);
  }
  
  static const struct proto_ops alg_proto_ops = {
  	.family		=	PF_ALG,
  	.owner		=	THIS_MODULE,
  
  	.connect	=	sock_no_connect,
  	.socketpair	=	sock_no_socketpair,
  	.getname	=	sock_no_getname,
  	.ioctl		=	sock_no_ioctl,
  	.listen		=	sock_no_listen,
  	.shutdown	=	sock_no_shutdown,
  	.getsockopt	=	sock_no_getsockopt,
  	.mmap		=	sock_no_mmap,
  	.sendpage	=	sock_no_sendpage,
  	.sendmsg	=	sock_no_sendmsg,
  	.recvmsg	=	sock_no_recvmsg,
  	.poll		=	sock_no_poll,
  
  	.bind		=	alg_bind,
  	.release	=	af_alg_release,
  	.setsockopt	=	alg_setsockopt,
  	.accept		=	alg_accept,
  };
  
  static void alg_sock_destruct(struct sock *sk)
  {
  	struct alg_sock *ask = alg_sk(sk);
  
  	alg_do_release(ask->type, ask->private);
  }
  
  static int alg_create(struct net *net, struct socket *sock, int protocol,
  		      int kern)
  {
  	struct sock *sk;
  	int err;
  
  	if (sock->type != SOCK_SEQPACKET)
  		return -ESOCKTNOSUPPORT;
  	if (protocol != 0)
  		return -EPROTONOSUPPORT;
  
  	err = -ENOMEM;
11aa9c28b   Eric W. Biederman   net: Pass kern fr...
358
  	sk = sk_alloc(net, PF_ALG, GFP_KERNEL, &alg_proto, kern);
03c8efc1f   Herbert Xu   crypto: af_alg - ...
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
  	if (!sk)
  		goto out;
  
  	sock->ops = &alg_proto_ops;
  	sock_init_data(sock, sk);
  
  	sk->sk_family = PF_ALG;
  	sk->sk_destruct = alg_sock_destruct;
  
  	return 0;
  out:
  	return err;
  }
  
  static const struct net_proto_family alg_family = {
  	.family	=	PF_ALG,
  	.create	=	alg_create,
  	.owner	=	THIS_MODULE,
  };
1d10eb2f1   Al Viro   crypto: switch af...
378
  int af_alg_make_sg(struct af_alg_sgl *sgl, struct iov_iter *iter, int len)
03c8efc1f   Herbert Xu   crypto: af_alg - ...
379
  {
1d10eb2f1   Al Viro   crypto: switch af...
380
381
382
  	size_t off;
  	ssize_t n;
  	int npages, i;
03c8efc1f   Herbert Xu   crypto: af_alg - ...
383

1d10eb2f1   Al Viro   crypto: switch af...
384
385
386
  	n = iov_iter_get_pages(iter, sgl->pages, len, ALG_MAX_PAGES, &off);
  	if (n < 0)
  		return n;
03c8efc1f   Herbert Xu   crypto: af_alg - ...
387

9399f0c51   Linus Torvalds   crypto: fix af_al...
388
  	npages = (off + n + PAGE_SIZE - 1) >> PAGE_SHIFT;
03c8efc1f   Herbert Xu   crypto: af_alg - ...
389
  	if (WARN_ON(npages == 0))
1d10eb2f1   Al Viro   crypto: switch af...
390
  		return -EINVAL;
66db37391   Tadeusz Struk   crypto: af_alg - ...
391
392
  	/* Add one extra for linking */
  	sg_init_table(sgl->sg, npages + 1);
03c8efc1f   Herbert Xu   crypto: af_alg - ...
393

1d10eb2f1   Al Viro   crypto: switch af...
394
  	for (i = 0, len = n; i < npages; i++) {
03c8efc1f   Herbert Xu   crypto: af_alg - ...
395
396
397
398
399
400
  		int plen = min_t(int, len, PAGE_SIZE - off);
  
  		sg_set_page(sgl->sg + i, sgl->pages[i], plen, off);
  
  		off = 0;
  		len -= plen;
03c8efc1f   Herbert Xu   crypto: af_alg - ...
401
  	}
66db37391   Tadeusz Struk   crypto: af_alg - ...
402
403
  	sg_mark_end(sgl->sg + npages - 1);
  	sgl->npages = npages;
1d10eb2f1   Al Viro   crypto: switch af...
404
  	return n;
03c8efc1f   Herbert Xu   crypto: af_alg - ...
405
406
  }
  EXPORT_SYMBOL_GPL(af_alg_make_sg);
66db37391   Tadeusz Struk   crypto: af_alg - ...
407
408
409
410
411
  void af_alg_link_sg(struct af_alg_sgl *sgl_prev, struct af_alg_sgl *sgl_new)
  {
  	sg_unmark_end(sgl_prev->sg + sgl_prev->npages - 1);
  	sg_chain(sgl_prev->sg, sgl_prev->npages + 1, sgl_new->sg);
  }
bd5075203   Tadeusz Struk   crypto: af_alg - ...
412
  EXPORT_SYMBOL_GPL(af_alg_link_sg);
66db37391   Tadeusz Struk   crypto: af_alg - ...
413

03c8efc1f   Herbert Xu   crypto: af_alg - ...
414
415
416
  void af_alg_free_sg(struct af_alg_sgl *sgl)
  {
  	int i;
66db37391   Tadeusz Struk   crypto: af_alg - ...
417
  	for (i = 0; i < sgl->npages; i++)
03c8efc1f   Herbert Xu   crypto: af_alg - ...
418
  		put_page(sgl->pages[i]);
03c8efc1f   Herbert Xu   crypto: af_alg - ...
419
420
421
422
423
424
  }
  EXPORT_SYMBOL_GPL(af_alg_free_sg);
  
  int af_alg_cmsg_send(struct msghdr *msg, struct af_alg_control *con)
  {
  	struct cmsghdr *cmsg;
f95b414ed   Gu Zheng   net: introduce he...
425
  	for_each_cmsghdr(cmsg, msg) {
03c8efc1f   Herbert Xu   crypto: af_alg - ...
426
427
428
429
  		if (!CMSG_OK(msg, cmsg))
  			return -EINVAL;
  		if (cmsg->cmsg_level != SOL_ALG)
  			continue;
267c4221f   Joshua I. James   crypto: af_alg - ...
430
  		switch (cmsg->cmsg_type) {
03c8efc1f   Herbert Xu   crypto: af_alg - ...
431
432
433
434
435
436
437
438
439
440
441
442
443
444
  		case ALG_SET_IV:
  			if (cmsg->cmsg_len < CMSG_LEN(sizeof(*con->iv)))
  				return -EINVAL;
  			con->iv = (void *)CMSG_DATA(cmsg);
  			if (cmsg->cmsg_len < CMSG_LEN(con->iv->ivlen +
  						      sizeof(*con->iv)))
  				return -EINVAL;
  			break;
  
  		case ALG_SET_OP:
  			if (cmsg->cmsg_len < CMSG_LEN(sizeof(u32)))
  				return -EINVAL;
  			con->op = *(u32 *)CMSG_DATA(cmsg);
  			break;
af8e80731   Stephan Mueller   crypto: af_alg - ...
445
446
447
448
449
  		case ALG_SET_AEAD_ASSOCLEN:
  			if (cmsg->cmsg_len < CMSG_LEN(sizeof(u32)))
  				return -EINVAL;
  			con->aead_assoclen = *(u32 *)CMSG_DATA(cmsg);
  			break;
03c8efc1f   Herbert Xu   crypto: af_alg - ...
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
  		default:
  			return -EINVAL;
  		}
  	}
  
  	return 0;
  }
  EXPORT_SYMBOL_GPL(af_alg_cmsg_send);
  
  int af_alg_wait_for_completion(int err, struct af_alg_completion *completion)
  {
  	switch (err) {
  	case -EINPROGRESS:
  	case -EBUSY:
  		wait_for_completion(&completion->completion);
16735d022   Wolfram Sang   tree-wide: use re...
465
  		reinit_completion(&completion->completion);
03c8efc1f   Herbert Xu   crypto: af_alg - ...
466
467
468
469
470
471
472
473
474
475
476
  		err = completion->err;
  		break;
  	};
  
  	return err;
  }
  EXPORT_SYMBOL_GPL(af_alg_wait_for_completion);
  
  void af_alg_complete(struct crypto_async_request *req, int err)
  {
  	struct af_alg_completion *completion = req->data;
7e77bdebf   Rabin Vincent   crypto: af_alg - ...
477
478
  	if (err == -EINPROGRESS)
  		return;
03c8efc1f   Herbert Xu   crypto: af_alg - ...
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
  	completion->err = err;
  	complete(&completion->completion);
  }
  EXPORT_SYMBOL_GPL(af_alg_complete);
  
  static int __init af_alg_init(void)
  {
  	int err = proto_register(&alg_proto, 0);
  
  	if (err)
  		goto out;
  
  	err = sock_register(&alg_family);
  	if (err != 0)
  		goto out_unregister_proto;
  
  out:
  	return err;
  
  out_unregister_proto:
  	proto_unregister(&alg_proto);
  	goto out;
  }
  
  static void __exit af_alg_exit(void)
  {
  	sock_unregister(PF_ALG);
  	proto_unregister(&alg_proto);
  }
  
  module_init(af_alg_init);
  module_exit(af_alg_exit);
  MODULE_LICENSE("GPL");
  MODULE_ALIAS_NETPROTO(AF_ALG);