Blame view

crypto/algif_aead.c 18.8 KB
400c40cf7   Stephan Mueller   crypto: algif - a...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  /*
   * algif_aead: User-space interface for AEAD algorithms
   *
   * Copyright (C) 2014, Stephan Mueller <smueller@chronox.de>
   *
   * This file provides the user-space API for AEAD ciphers.
   *
   * This file is derived from algif_skcipher.c.
   *
   * 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.
   */
83094e5e9   Tadeusz Struk   crypto: af_alg - ...
15
  #include <crypto/internal/aead.h>
400c40cf7   Stephan Mueller   crypto: algif - a...
16
17
18
19
20
21
22
23
24
25
26
27
28
29
  #include <crypto/scatterwalk.h>
  #include <crypto/if_alg.h>
  #include <linux/init.h>
  #include <linux/list.h>
  #include <linux/kernel.h>
  #include <linux/mm.h>
  #include <linux/module.h>
  #include <linux/net.h>
  #include <net/sock.h>
  
  struct aead_sg_list {
  	unsigned int cur;
  	struct scatterlist sg[ALG_MAX_PAGES];
  };
83094e5e9   Tadeusz Struk   crypto: af_alg - ...
30
31
32
33
34
35
36
37
38
39
40
41
42
  struct aead_async_rsgl {
  	struct af_alg_sgl sgl;
  	struct list_head list;
  };
  
  struct aead_async_req {
  	struct scatterlist *tsgl;
  	struct aead_async_rsgl first_rsgl;
  	struct list_head list;
  	struct kiocb *iocb;
  	unsigned int tsgls;
  	char iv[];
  };
400c40cf7   Stephan Mueller   crypto: algif - a...
43
44
  struct aead_ctx {
  	struct aead_sg_list tsgl;
83094e5e9   Tadeusz Struk   crypto: af_alg - ...
45
46
  	struct aead_async_rsgl first_rsgl;
  	struct list_head list;
400c40cf7   Stephan Mueller   crypto: algif - a...
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
77
78
79
  
  	void *iv;
  
  	struct af_alg_completion completion;
  
  	unsigned long used;
  
  	unsigned int len;
  	bool more;
  	bool merge;
  	bool enc;
  
  	size_t aead_assoclen;
  	struct aead_request aead_req;
  };
  
  static inline int aead_sndbuf(struct sock *sk)
  {
  	struct alg_sock *ask = alg_sk(sk);
  	struct aead_ctx *ctx = ask->private;
  
  	return max_t(int, max_t(int, sk->sk_sndbuf & PAGE_MASK, PAGE_SIZE) -
  			  ctx->used, 0);
  }
  
  static inline bool aead_writable(struct sock *sk)
  {
  	return PAGE_SIZE <= aead_sndbuf(sk);
  }
  
  static inline bool aead_sufficient_data(struct aead_ctx *ctx)
  {
  	unsigned as = crypto_aead_authsize(crypto_aead_reqtfm(&ctx->aead_req));
19fa77522   Herbert Xu   crypto: algif_aea...
80
  	return ctx->used >= ctx->aead_assoclen + as;
400c40cf7   Stephan Mueller   crypto: algif - a...
81
  }
83094e5e9   Tadeusz Struk   crypto: af_alg - ...
82
83
84
85
86
87
88
89
90
91
  static void aead_reset_ctx(struct aead_ctx *ctx)
  {
  	struct aead_sg_list *sgl = &ctx->tsgl;
  
  	sg_init_table(sgl->sg, ALG_MAX_PAGES);
  	sgl->cur = 0;
  	ctx->used = 0;
  	ctx->more = 0;
  	ctx->merge = 0;
  }
400c40cf7   Stephan Mueller   crypto: algif - a...
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
  static void aead_put_sgl(struct sock *sk)
  {
  	struct alg_sock *ask = alg_sk(sk);
  	struct aead_ctx *ctx = ask->private;
  	struct aead_sg_list *sgl = &ctx->tsgl;
  	struct scatterlist *sg = sgl->sg;
  	unsigned int i;
  
  	for (i = 0; i < sgl->cur; i++) {
  		if (!sg_page(sg + i))
  			continue;
  
  		put_page(sg_page(sg + i));
  		sg_assign_page(sg + i, NULL);
  	}
83094e5e9   Tadeusz Struk   crypto: af_alg - ...
107
  	aead_reset_ctx(ctx);
400c40cf7   Stephan Mueller   crypto: algif - a...
108
109
110
111
112
113
114
115
116
117
118
  }
  
  static void aead_wmem_wakeup(struct sock *sk)
  {
  	struct socket_wq *wq;
  
  	if (!aead_writable(sk))
  		return;
  
  	rcu_read_lock();
  	wq = rcu_dereference(sk->sk_wq);
1ce0bf50a   Herbert Xu   net: Generalise w...
119
  	if (skwq_has_sleeper(wq))
400c40cf7   Stephan Mueller   crypto: algif - a...
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
  		wake_up_interruptible_sync_poll(&wq->wait, POLLIN |
  							   POLLRDNORM |
  							   POLLRDBAND);
  	sk_wake_async(sk, SOCK_WAKE_WAITD, POLL_IN);
  	rcu_read_unlock();
  }
  
  static int aead_wait_for_data(struct sock *sk, unsigned flags)
  {
  	struct alg_sock *ask = alg_sk(sk);
  	struct aead_ctx *ctx = ask->private;
  	long timeout;
  	DEFINE_WAIT(wait);
  	int err = -ERESTARTSYS;
  
  	if (flags & MSG_DONTWAIT)
  		return -EAGAIN;
9cd3e072b   Eric Dumazet   net: rename SOCK_...
137
  	sk_set_bit(SOCKWQ_ASYNC_WAITDATA, sk);
400c40cf7   Stephan Mueller   crypto: algif - a...
138
139
140
141
142
143
144
145
146
147
148
149
  
  	for (;;) {
  		if (signal_pending(current))
  			break;
  		prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
  		timeout = MAX_SCHEDULE_TIMEOUT;
  		if (sk_wait_event(sk, &timeout, !ctx->more)) {
  			err = 0;
  			break;
  		}
  	}
  	finish_wait(sk_sleep(sk), &wait);
9cd3e072b   Eric Dumazet   net: rename SOCK_...
150
  	sk_clear_bit(SOCKWQ_ASYNC_WAITDATA, sk);
400c40cf7   Stephan Mueller   crypto: algif - a...
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
  
  	return err;
  }
  
  static void aead_data_wakeup(struct sock *sk)
  {
  	struct alg_sock *ask = alg_sk(sk);
  	struct aead_ctx *ctx = ask->private;
  	struct socket_wq *wq;
  
  	if (ctx->more)
  		return;
  	if (!ctx->used)
  		return;
  
  	rcu_read_lock();
  	wq = rcu_dereference(sk->sk_wq);
1ce0bf50a   Herbert Xu   net: Generalise w...
168
  	if (skwq_has_sleeper(wq))
400c40cf7   Stephan Mueller   crypto: algif - a...
169
170
171
172
173
174
  		wake_up_interruptible_sync_poll(&wq->wait, POLLOUT |
  							   POLLRDNORM |
  							   POLLRDBAND);
  	sk_wake_async(sk, SOCK_WAKE_SPACE, POLL_OUT);
  	rcu_read_unlock();
  }
eccd02f32   Linus Torvalds   crypto: fix mis-m...
175
  static int aead_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
400c40cf7   Stephan Mueller   crypto: algif - a...
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
  {
  	struct sock *sk = sock->sk;
  	struct alg_sock *ask = alg_sk(sk);
  	struct aead_ctx *ctx = ask->private;
  	unsigned ivsize =
  		crypto_aead_ivsize(crypto_aead_reqtfm(&ctx->aead_req));
  	struct aead_sg_list *sgl = &ctx->tsgl;
  	struct af_alg_control con = {};
  	long copied = 0;
  	bool enc = 0;
  	bool init = 0;
  	int err = -EINVAL;
  
  	if (msg->msg_controllen) {
  		err = af_alg_cmsg_send(msg, &con);
  		if (err)
  			return err;
  
  		init = 1;
  		switch (con.op) {
  		case ALG_OP_ENCRYPT:
  			enc = 1;
  			break;
  		case ALG_OP_DECRYPT:
  			enc = 0;
  			break;
  		default:
  			return -EINVAL;
  		}
  
  		if (con.iv && con.iv->ivlen != ivsize)
  			return -EINVAL;
  	}
  
  	lock_sock(sk);
  	if (!ctx->more && ctx->used)
  		goto unlock;
  
  	if (init) {
  		ctx->enc = enc;
  		if (con.iv)
  			memcpy(ctx->iv, con.iv->iv, ivsize);
  
  		ctx->aead_assoclen = con.aead_assoclen;
  	}
  
  	while (size) {
652d5b8a8   LABBE Corentin   crypto: algif - C...
223
  		size_t len = size;
400c40cf7   Stephan Mueller   crypto: algif - a...
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
  		struct scatterlist *sg = NULL;
  
  		/* use the existing memory in an allocated page */
  		if (ctx->merge) {
  			sg = sgl->sg + sgl->cur - 1;
  			len = min_t(unsigned long, len,
  				    PAGE_SIZE - sg->offset - sg->length);
  			err = memcpy_from_msg(page_address(sg_page(sg)) +
  					      sg->offset + sg->length,
  					      msg, len);
  			if (err)
  				goto unlock;
  
  			sg->length += len;
  			ctx->merge = (sg->offset + sg->length) &
  				     (PAGE_SIZE - 1);
  
  			ctx->used += len;
  			copied += len;
  			size -= len;
  			continue;
  		}
  
  		if (!aead_writable(sk)) {
  			/* user space sent too much data */
  			aead_put_sgl(sk);
  			err = -EMSGSIZE;
  			goto unlock;
  		}
  
  		/* allocate a new page */
  		len = min_t(unsigned long, size, aead_sndbuf(sk));
  		while (len) {
652d5b8a8   LABBE Corentin   crypto: algif - C...
257
  			size_t plen = 0;
400c40cf7   Stephan Mueller   crypto: algif - a...
258
259
260
261
262
263
264
265
  
  			if (sgl->cur >= ALG_MAX_PAGES) {
  				aead_put_sgl(sk);
  				err = -E2BIG;
  				goto unlock;
  			}
  
  			sg = sgl->sg + sgl->cur;
652d5b8a8   LABBE Corentin   crypto: algif - C...
266
  			plen = min_t(size_t, len, PAGE_SIZE);
400c40cf7   Stephan Mueller   crypto: algif - a...
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
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
  
  			sg_assign_page(sg, alloc_page(GFP_KERNEL));
  			err = -ENOMEM;
  			if (!sg_page(sg))
  				goto unlock;
  
  			err = memcpy_from_msg(page_address(sg_page(sg)),
  					      msg, plen);
  			if (err) {
  				__free_page(sg_page(sg));
  				sg_assign_page(sg, NULL);
  				goto unlock;
  			}
  
  			sg->offset = 0;
  			sg->length = plen;
  			len -= plen;
  			ctx->used += plen;
  			copied += plen;
  			sgl->cur++;
  			size -= plen;
  			ctx->merge = plen & (PAGE_SIZE - 1);
  		}
  	}
  
  	err = 0;
  
  	ctx->more = msg->msg_flags & MSG_MORE;
  	if (!ctx->more && !aead_sufficient_data(ctx)) {
  		aead_put_sgl(sk);
  		err = -EMSGSIZE;
  	}
  
  unlock:
  	aead_data_wakeup(sk);
  	release_sock(sk);
  
  	return err ?: copied;
  }
  
  static ssize_t aead_sendpage(struct socket *sock, struct page *page,
  			     int offset, size_t size, int flags)
  {
  	struct sock *sk = sock->sk;
  	struct alg_sock *ask = alg_sk(sk);
  	struct aead_ctx *ctx = ask->private;
  	struct aead_sg_list *sgl = &ctx->tsgl;
  	int err = -EINVAL;
  
  	if (flags & MSG_SENDPAGE_NOTLAST)
  		flags |= MSG_MORE;
  
  	if (sgl->cur >= ALG_MAX_PAGES)
  		return -E2BIG;
  
  	lock_sock(sk);
  	if (!ctx->more && ctx->used)
  		goto unlock;
  
  	if (!size)
  		goto done;
  
  	if (!aead_writable(sk)) {
  		/* user space sent too much data */
  		aead_put_sgl(sk);
  		err = -EMSGSIZE;
  		goto unlock;
  	}
  
  	ctx->merge = 0;
  
  	get_page(page);
  	sg_set_page(sgl->sg + sgl->cur, page, size, offset);
  	sgl->cur++;
  	ctx->used += size;
  
  	err = 0;
  
  done:
  	ctx->more = flags & MSG_MORE;
  	if (!ctx->more && !aead_sufficient_data(ctx)) {
  		aead_put_sgl(sk);
  		err = -EMSGSIZE;
  	}
  
  unlock:
  	aead_data_wakeup(sk);
  	release_sock(sk);
  
  	return err ?: size;
  }
83094e5e9   Tadeusz Struk   crypto: af_alg - ...
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
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
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
  #define GET_ASYM_REQ(req, tfm) (struct aead_async_req *) \
  		((char *)req + sizeof(struct aead_request) + \
  		 crypto_aead_reqsize(tfm))
  
   #define GET_REQ_SIZE(tfm) sizeof(struct aead_async_req) + \
  	crypto_aead_reqsize(tfm) + crypto_aead_ivsize(tfm) + \
  	sizeof(struct aead_request)
  
  static void aead_async_cb(struct crypto_async_request *_req, int err)
  {
  	struct sock *sk = _req->data;
  	struct alg_sock *ask = alg_sk(sk);
  	struct aead_ctx *ctx = ask->private;
  	struct crypto_aead *tfm = crypto_aead_reqtfm(&ctx->aead_req);
  	struct aead_request *req = aead_request_cast(_req);
  	struct aead_async_req *areq = GET_ASYM_REQ(req, tfm);
  	struct scatterlist *sg = areq->tsgl;
  	struct aead_async_rsgl *rsgl;
  	struct kiocb *iocb = areq->iocb;
  	unsigned int i, reqlen = GET_REQ_SIZE(tfm);
  
  	list_for_each_entry(rsgl, &areq->list, list) {
  		af_alg_free_sg(&rsgl->sgl);
  		if (rsgl != &areq->first_rsgl)
  			sock_kfree_s(sk, rsgl, sizeof(*rsgl));
  	}
  
  	for (i = 0; i < areq->tsgls; i++)
  		put_page(sg_page(sg + i));
  
  	sock_kfree_s(sk, areq->tsgl, sizeof(*areq->tsgl) * areq->tsgls);
  	sock_kfree_s(sk, req, reqlen);
  	__sock_put(sk);
  	iocb->ki_complete(iocb, err, err);
  }
  
  static int aead_recvmsg_async(struct socket *sock, struct msghdr *msg,
  			      int flags)
  {
  	struct sock *sk = sock->sk;
  	struct alg_sock *ask = alg_sk(sk);
  	struct aead_ctx *ctx = ask->private;
  	struct crypto_aead *tfm = crypto_aead_reqtfm(&ctx->aead_req);
  	struct aead_async_req *areq;
  	struct aead_request *req = NULL;
  	struct aead_sg_list *sgl = &ctx->tsgl;
  	struct aead_async_rsgl *last_rsgl = NULL, *rsgl;
  	unsigned int as = crypto_aead_authsize(tfm);
  	unsigned int i, reqlen = GET_REQ_SIZE(tfm);
  	int err = -ENOMEM;
  	unsigned long used;
  	size_t outlen;
  	size_t usedpages = 0;
  
  	lock_sock(sk);
  	if (ctx->more) {
  		err = aead_wait_for_data(sk, flags);
  		if (err)
  			goto unlock;
  	}
  
  	used = ctx->used;
  	outlen = used;
  
  	if (!aead_sufficient_data(ctx))
  		goto unlock;
  
  	req = sock_kmalloc(sk, reqlen, GFP_KERNEL);
  	if (unlikely(!req))
  		goto unlock;
  
  	areq = GET_ASYM_REQ(req, tfm);
  	memset(&areq->first_rsgl, '\0', sizeof(areq->first_rsgl));
  	INIT_LIST_HEAD(&areq->list);
  	areq->iocb = msg->msg_iocb;
  	memcpy(areq->iv, ctx->iv, crypto_aead_ivsize(tfm));
  	aead_request_set_tfm(req, tfm);
  	aead_request_set_ad(req, ctx->aead_assoclen);
  	aead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
  				  aead_async_cb, sk);
  	used -= ctx->aead_assoclen + (ctx->enc ? as : 0);
  
  	/* take over all tx sgls from ctx */
  	areq->tsgl = sock_kmalloc(sk, sizeof(*areq->tsgl) * sgl->cur,
  				  GFP_KERNEL);
  	if (unlikely(!areq->tsgl))
  		goto free;
  
  	sg_init_table(areq->tsgl, sgl->cur);
  	for (i = 0; i < sgl->cur; i++)
  		sg_set_page(&areq->tsgl[i], sg_page(&sgl->sg[i]),
  			    sgl->sg[i].length, sgl->sg[i].offset);
  
  	areq->tsgls = sgl->cur;
  
  	/* create rx sgls */
  	while (iov_iter_count(&msg->msg_iter)) {
  		size_t seglen = min_t(size_t, iov_iter_count(&msg->msg_iter),
  				      (outlen - usedpages));
  
  		if (list_empty(&areq->list)) {
  			rsgl = &areq->first_rsgl;
  
  		} else {
  			rsgl = sock_kmalloc(sk, sizeof(*rsgl), GFP_KERNEL);
  			if (unlikely(!rsgl)) {
  				err = -ENOMEM;
  				goto free;
  			}
  		}
  		rsgl->sgl.npages = 0;
  		list_add_tail(&rsgl->list, &areq->list);
  
  		/* make one iovec available as scatterlist */
  		err = af_alg_make_sg(&rsgl->sgl, &msg->msg_iter, seglen);
  		if (err < 0)
  			goto free;
  
  		usedpages += err;
  
  		/* chain the new scatterlist with previous one */
  		if (last_rsgl)
  			af_alg_link_sg(&last_rsgl->sgl, &rsgl->sgl);
  
  		last_rsgl = rsgl;
  
  		/* we do not need more iovecs as we have sufficient memory */
  		if (outlen <= usedpages)
  			break;
  
  		iov_iter_advance(&msg->msg_iter, err);
  	}
  	err = -EINVAL;
  	/* ensure output buffer is sufficiently large */
  	if (usedpages < outlen)
  		goto free;
  
  	aead_request_set_crypt(req, areq->tsgl, areq->first_rsgl.sgl.sg, used,
  			       areq->iv);
  	err = ctx->enc ? crypto_aead_encrypt(req) : crypto_aead_decrypt(req);
  	if (err) {
  		if (err == -EINPROGRESS) {
  			sock_hold(sk);
  			err = -EIOCBQUEUED;
  			aead_reset_ctx(ctx);
  			goto unlock;
  		} else if (err == -EBADMSG) {
  			aead_put_sgl(sk);
  		}
  		goto free;
  	}
  	aead_put_sgl(sk);
  
  free:
  	list_for_each_entry(rsgl, &areq->list, list) {
  		af_alg_free_sg(&rsgl->sgl);
  		if (rsgl != &areq->first_rsgl)
  			sock_kfree_s(sk, rsgl, sizeof(*rsgl));
  	}
  	if (areq->tsgl)
  		sock_kfree_s(sk, areq->tsgl, sizeof(*areq->tsgl) * areq->tsgls);
  	if (req)
  		sock_kfree_s(sk, req, reqlen);
  unlock:
  	aead_wmem_wakeup(sk);
  	release_sock(sk);
  	return err ? err : outlen;
  }
  
  static int aead_recvmsg_sync(struct socket *sock, struct msghdr *msg, int flags)
400c40cf7   Stephan Mueller   crypto: algif - a...
528
529
530
531
  {
  	struct sock *sk = sock->sk;
  	struct alg_sock *ask = alg_sk(sk);
  	struct aead_ctx *ctx = ask->private;
400c40cf7   Stephan Mueller   crypto: algif - a...
532
533
  	unsigned as = crypto_aead_authsize(crypto_aead_reqtfm(&ctx->aead_req));
  	struct aead_sg_list *sgl = &ctx->tsgl;
83094e5e9   Tadeusz Struk   crypto: af_alg - ...
534
535
  	struct aead_async_rsgl *last_rsgl = NULL;
  	struct aead_async_rsgl *rsgl, *tmp;
400c40cf7   Stephan Mueller   crypto: algif - a...
536
537
538
539
  	int err = -EINVAL;
  	unsigned long used = 0;
  	size_t outlen = 0;
  	size_t usedpages = 0;
400c40cf7   Stephan Mueller   crypto: algif - a...
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
  
  	lock_sock(sk);
  
  	/*
  	 * AEAD memory structure: For encryption, the tag is appended to the
  	 * ciphertext which implies that the memory allocated for the ciphertext
  	 * must be increased by the tag length. For decryption, the tag
  	 * is expected to be concatenated to the ciphertext. The plaintext
  	 * therefore has a memory size of the ciphertext minus the tag length.
  	 *
  	 * The memory structure for cipher operation has the following
  	 * structure:
  	 *	AEAD encryption input:  assoc data || plaintext
  	 *	AEAD encryption output: cipherntext || auth tag
  	 *	AEAD decryption input:  assoc data || ciphertext || auth tag
  	 *	AEAD decryption output: plaintext
  	 */
  
  	if (ctx->more) {
  		err = aead_wait_for_data(sk, flags);
  		if (err)
  			goto unlock;
  	}
  
  	used = ctx->used;
  
  	/*
  	 * Make sure sufficient data is present -- note, the same check is
  	 * is also present in sendmsg/sendpage. The checks in sendpage/sendmsg
  	 * shall provide an information to the data sender that something is
  	 * wrong, but they are irrelevant to maintain the kernel integrity.
  	 * We need this check here too in case user space decides to not honor
  	 * the error message in sendmsg/sendpage and still call recvmsg. This
  	 * check here protects the kernel integrity.
  	 */
  	if (!aead_sufficient_data(ctx))
  		goto unlock;
19fa77522   Herbert Xu   crypto: algif_aea...
577
  	outlen = used;
400c40cf7   Stephan Mueller   crypto: algif - a...
578
579
580
581
  	/*
  	 * The cipher operation input data is reduced by the associated data
  	 * length as this data is processed separately later on.
  	 */
19fa77522   Herbert Xu   crypto: algif_aea...
582
  	used -= ctx->aead_assoclen + (ctx->enc ? as : 0);
400c40cf7   Stephan Mueller   crypto: algif - a...
583
584
585
586
587
  
  	/* convert iovecs of output buffers into scatterlists */
  	while (iov_iter_count(&msg->msg_iter)) {
  		size_t seglen = min_t(size_t, iov_iter_count(&msg->msg_iter),
  				      (outlen - usedpages));
83094e5e9   Tadeusz Struk   crypto: af_alg - ...
588
589
590
591
592
593
594
595
596
597
598
  		if (list_empty(&ctx->list)) {
  			rsgl = &ctx->first_rsgl;
  		} else {
  			rsgl = sock_kmalloc(sk, sizeof(*rsgl), GFP_KERNEL);
  			if (unlikely(!rsgl)) {
  				err = -ENOMEM;
  				goto unlock;
  			}
  		}
  		rsgl->sgl.npages = 0;
  		list_add_tail(&rsgl->list, &ctx->list);
400c40cf7   Stephan Mueller   crypto: algif - a...
599
  		/* make one iovec available as scatterlist */
83094e5e9   Tadeusz Struk   crypto: af_alg - ...
600
  		err = af_alg_make_sg(&rsgl->sgl, &msg->msg_iter, seglen);
400c40cf7   Stephan Mueller   crypto: algif - a...
601
602
603
  		if (err < 0)
  			goto unlock;
  		usedpages += err;
7b2a18e05   Tadeusz Struk   crypto: algif_aea...
604
  		/* chain the new scatterlist with previous one */
83094e5e9   Tadeusz Struk   crypto: af_alg - ...
605
606
607
608
  		if (last_rsgl)
  			af_alg_link_sg(&last_rsgl->sgl, &rsgl->sgl);
  
  		last_rsgl = rsgl;
7b2a18e05   Tadeusz Struk   crypto: algif_aea...
609

400c40cf7   Stephan Mueller   crypto: algif - a...
610
611
612
613
  		/* we do not need more iovecs as we have sufficient memory */
  		if (outlen <= usedpages)
  			break;
  		iov_iter_advance(&msg->msg_iter, err);
400c40cf7   Stephan Mueller   crypto: algif - a...
614
615
616
617
618
619
  	}
  
  	err = -EINVAL;
  	/* ensure output buffer is sufficiently large */
  	if (usedpages < outlen)
  		goto unlock;
19fa77522   Herbert Xu   crypto: algif_aea...
620
  	sg_mark_end(sgl->sg + sgl->cur - 1);
83094e5e9   Tadeusz Struk   crypto: af_alg - ...
621
  	aead_request_set_crypt(&ctx->aead_req, sgl->sg, ctx->first_rsgl.sgl.sg,
19fa77522   Herbert Xu   crypto: algif_aea...
622
623
  			       used, ctx->iv);
  	aead_request_set_ad(&ctx->aead_req, ctx->aead_assoclen);
400c40cf7   Stephan Mueller   crypto: algif - a...
624
625
626
627
628
629
630
631
632
633
  
  	err = af_alg_wait_for_completion(ctx->enc ?
  					 crypto_aead_encrypt(&ctx->aead_req) :
  					 crypto_aead_decrypt(&ctx->aead_req),
  					 &ctx->completion);
  
  	if (err) {
  		/* EBADMSG implies a valid cipher operation took place */
  		if (err == -EBADMSG)
  			aead_put_sgl(sk);
83094e5e9   Tadeusz Struk   crypto: af_alg - ...
634

400c40cf7   Stephan Mueller   crypto: algif - a...
635
636
637
638
  		goto unlock;
  	}
  
  	aead_put_sgl(sk);
400c40cf7   Stephan Mueller   crypto: algif - a...
639
640
641
  	err = 0;
  
  unlock:
83094e5e9   Tadeusz Struk   crypto: af_alg - ...
642
643
644
645
646
647
648
  	list_for_each_entry_safe(rsgl, tmp, &ctx->list, list) {
  		af_alg_free_sg(&rsgl->sgl);
  		if (rsgl != &ctx->first_rsgl)
  			sock_kfree_s(sk, rsgl, sizeof(*rsgl));
  		list_del(&rsgl->list);
  	}
  	INIT_LIST_HEAD(&ctx->list);
400c40cf7   Stephan Mueller   crypto: algif - a...
649
650
651
652
653
  	aead_wmem_wakeup(sk);
  	release_sock(sk);
  
  	return err ? err : outlen;
  }
83094e5e9   Tadeusz Struk   crypto: af_alg - ...
654
655
656
657
658
659
660
  static int aead_recvmsg(struct socket *sock, struct msghdr *msg, size_t ignored,
  			int flags)
  {
  	return (msg->msg_iocb && !is_sync_kiocb(msg->msg_iocb)) ?
  		aead_recvmsg_async(sock, msg, flags) :
  		aead_recvmsg_sync(sock, msg, flags);
  }
400c40cf7   Stephan Mueller   crypto: algif - a...
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
  static unsigned int aead_poll(struct file *file, struct socket *sock,
  			      poll_table *wait)
  {
  	struct sock *sk = sock->sk;
  	struct alg_sock *ask = alg_sk(sk);
  	struct aead_ctx *ctx = ask->private;
  	unsigned int mask;
  
  	sock_poll_wait(file, sk_sleep(sk), wait);
  	mask = 0;
  
  	if (!ctx->more)
  		mask |= POLLIN | POLLRDNORM;
  
  	if (aead_writable(sk))
  		mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
  
  	return mask;
  }
  
  static struct proto_ops algif_aead_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,
  	.getsockopt	=	sock_no_getsockopt,
  	.mmap		=	sock_no_mmap,
  	.bind		=	sock_no_bind,
  	.accept		=	sock_no_accept,
  	.setsockopt	=	sock_no_setsockopt,
  
  	.release	=	af_alg_release,
  	.sendmsg	=	aead_sendmsg,
  	.sendpage	=	aead_sendpage,
  	.recvmsg	=	aead_recvmsg,
  	.poll		=	aead_poll,
  };
  
  static void *aead_bind(const char *name, u32 type, u32 mask)
  {
5e4b8c1fc   Herbert Xu   crypto: aead - Re...
705
  	return crypto_alloc_aead(name, type, mask);
400c40cf7   Stephan Mueller   crypto: algif - a...
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
  }
  
  static void aead_release(void *private)
  {
  	crypto_free_aead(private);
  }
  
  static int aead_setauthsize(void *private, unsigned int authsize)
  {
  	return crypto_aead_setauthsize(private, authsize);
  }
  
  static int aead_setkey(void *private, const u8 *key, unsigned int keylen)
  {
  	return crypto_aead_setkey(private, key, keylen);
  }
  
  static void aead_sock_destruct(struct sock *sk)
  {
  	struct alg_sock *ask = alg_sk(sk);
  	struct aead_ctx *ctx = ask->private;
  	unsigned int ivlen = crypto_aead_ivsize(
  				crypto_aead_reqtfm(&ctx->aead_req));
83094e5e9   Tadeusz Struk   crypto: af_alg - ...
729
  	WARN_ON(atomic_read(&sk->sk_refcnt) != 0);
400c40cf7   Stephan Mueller   crypto: algif - a...
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
  	aead_put_sgl(sk);
  	sock_kzfree_s(sk, ctx->iv, ivlen);
  	sock_kfree_s(sk, ctx, ctx->len);
  	af_alg_release_parent(sk);
  }
  
  static int aead_accept_parent(void *private, struct sock *sk)
  {
  	struct aead_ctx *ctx;
  	struct alg_sock *ask = alg_sk(sk);
  	unsigned int len = sizeof(*ctx) + crypto_aead_reqsize(private);
  	unsigned int ivlen = crypto_aead_ivsize(private);
  
  	ctx = sock_kmalloc(sk, len, GFP_KERNEL);
  	if (!ctx)
  		return -ENOMEM;
  	memset(ctx, 0, len);
  
  	ctx->iv = sock_kmalloc(sk, ivlen, GFP_KERNEL);
  	if (!ctx->iv) {
  		sock_kfree_s(sk, ctx, len);
  		return -ENOMEM;
  	}
  	memset(ctx->iv, 0, ivlen);
  
  	ctx->len = len;
  	ctx->used = 0;
  	ctx->more = 0;
  	ctx->merge = 0;
  	ctx->enc = 0;
  	ctx->tsgl.cur = 0;
  	ctx->aead_assoclen = 0;
  	af_alg_init_completion(&ctx->completion);
  	sg_init_table(ctx->tsgl.sg, ALG_MAX_PAGES);
83094e5e9   Tadeusz Struk   crypto: af_alg - ...
764
  	INIT_LIST_HEAD(&ctx->list);
400c40cf7   Stephan Mueller   crypto: algif - a...
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
  
  	ask->private = ctx;
  
  	aead_request_set_tfm(&ctx->aead_req, private);
  	aead_request_set_callback(&ctx->aead_req, CRYPTO_TFM_REQ_MAY_BACKLOG,
  				  af_alg_complete, &ctx->completion);
  
  	sk->sk_destruct = aead_sock_destruct;
  
  	return 0;
  }
  
  static const struct af_alg_type algif_type_aead = {
  	.bind		=	aead_bind,
  	.release	=	aead_release,
  	.setkey		=	aead_setkey,
  	.setauthsize	=	aead_setauthsize,
  	.accept		=	aead_accept_parent,
  	.ops		=	&algif_aead_ops,
  	.name		=	"aead",
  	.owner		=	THIS_MODULE
  };
  
  static int __init algif_aead_init(void)
  {
  	return af_alg_register_type(&algif_type_aead);
  }
  
  static void __exit algif_aead_exit(void)
  {
  	int err = af_alg_unregister_type(&algif_type_aead);
  	BUG_ON(err);
  }
  
  module_init(algif_aead_init);
  module_exit(algif_aead_exit);
  MODULE_LICENSE("GPL");
  MODULE_AUTHOR("Stephan Mueller <smueller@chronox.de>");
  MODULE_DESCRIPTION("AEAD kernel crypto API user space interface");