Blame view
crypto/algif_skcipher.c
9.87 KB
2874c5fd2
|
1 |
// SPDX-License-Identifier: GPL-2.0-or-later |
8ff590903
|
2 3 4 5 6 7 8 |
/* * algif_skcipher: User-space interface for skcipher algorithms * * This file provides the user-space API for symmetric key ciphers. * * Copyright (c) 2010 Herbert Xu <herbert@gondor.apana.org.au> * |
e870456d8
|
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
* The following concept of the memory management is used: * * The kernel maintains two SGLs, the TX SGL and the RX SGL. The TX SGL is * filled by user space with the data submitted via sendpage/sendmsg. Filling * up the TX SGL does not cause a crypto operation -- the data will only be * tracked by the kernel. Upon receipt of one recvmsg call, the caller must * provide a buffer which is tracked with the RX SGL. * * During the processing of the recvmsg operation, the cipher request is * allocated and prepared. As part of the recvmsg operation, the processed * TX buffers are extracted from the TX SGL into a separate SGL. * * After the completion of the crypto operation, the RX SGL and the cipher * request is released. The extracted TX SGL parts are released together with * the RX SGL release. |
8ff590903
|
24 25 26 27 28 29 30 31 32 33 34 35 |
*/ #include <crypto/scatterwalk.h> #include <crypto/skcipher.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> |
1b7841404
|
36 37 |
static int skcipher_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) |
8ff590903
|
38 39 40 |
{ struct sock *sk = sock->sk; struct alg_sock *ask = alg_sk(sk); |
6454c2b83
|
41 42 |
struct sock *psk = ask->parent; struct alg_sock *pask = alg_sk(psk); |
f8d33fac8
|
43 |
struct crypto_skcipher *tfm = pask->private; |
0d96e4bab
|
44 |
unsigned ivsize = crypto_skcipher_ivsize(tfm); |
8ff590903
|
45 |
|
2d97591ef
|
46 |
return af_alg_sendmsg(sock, msg, size, ivsize); |
a596999b7
|
47 |
} |
e870456d8
|
48 49 |
static int _skcipher_recvmsg(struct socket *sock, struct msghdr *msg, size_t ignored, int flags) |
a596999b7
|
50 51 52 |
{ struct sock *sk = sock->sk; struct alg_sock *ask = alg_sk(sk); |
ec69bbfb9
|
53 54 |
struct sock *psk = ask->parent; struct alg_sock *pask = alg_sk(psk); |
2d97591ef
|
55 |
struct af_alg_ctx *ctx = ask->private; |
f8d33fac8
|
56 |
struct crypto_skcipher *tfm = pask->private; |
e870456d8
|
57 |
unsigned int bs = crypto_skcipher_blocksize(tfm); |
2d97591ef
|
58 |
struct af_alg_async_req *areq; |
e870456d8
|
59 60 |
int err = 0; size_t len = 0; |
ec69bbfb9
|
61 |
|
11edb5559
|
62 63 64 65 66 |
if (!ctx->used) { err = af_alg_wait_for_data(sk, flags); if (err) return err; } |
e870456d8
|
67 |
/* Allocate cipher request for current operation. */ |
2d97591ef
|
68 69 70 71 |
areq = af_alg_alloc_areq(sk, sizeof(struct af_alg_async_req) + crypto_skcipher_reqsize(tfm)); if (IS_ERR(areq)) return PTR_ERR(areq); |
a596999b7
|
72 |
|
e870456d8
|
73 |
/* convert iovecs of output buffers into RX SGL */ |
2d97591ef
|
74 75 76 |
err = af_alg_get_rsgl(sk, msg, flags, areq, -1, &len); if (err) goto free; |
a596999b7
|
77 |
|
e870456d8
|
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
/* Process only as much RX buffers for which we have TX data */ if (len > ctx->used) len = ctx->used; /* * If more buffers are to be expected to be processed, process only * full block size buffers. */ if (ctx->more || len < ctx->used) len -= len % bs; /* * Create a per request TX SGL for this request which tracks the * SG entries from the global TX SGL. */ |
2d97591ef
|
93 |
areq->tsgl_entries = af_alg_count_tsgl(sk, len, 0); |
e870456d8
|
94 95 |
if (!areq->tsgl_entries) areq->tsgl_entries = 1; |
76e43e37a
|
96 97 |
areq->tsgl = sock_kmalloc(sk, array_size(sizeof(*areq->tsgl), areq->tsgl_entries), |
e870456d8
|
98 99 100 101 102 103 |
GFP_KERNEL); if (!areq->tsgl) { err = -ENOMEM; goto free; } sg_init_table(areq->tsgl, areq->tsgl_entries); |
2d97591ef
|
104 |
af_alg_pull_tsgl(sk, len, areq->tsgl, 0); |
e870456d8
|
105 106 |
/* Initialize the crypto operation */ |
2d97591ef
|
107 108 109 |
skcipher_request_set_tfm(&areq->cra_u.skcipher_req, tfm); skcipher_request_set_crypt(&areq->cra_u.skcipher_req, areq->tsgl, areq->first_rsgl.sgl.sg, len, ctx->iv); |
e870456d8
|
110 111 112 |
if (msg->msg_iocb && !is_sync_kiocb(msg->msg_iocb)) { /* AIO operation */ |
7d2c3f54e
|
113 |
sock_hold(sk); |
e870456d8
|
114 |
areq->iocb = msg->msg_iocb; |
d53c51357
|
115 116 117 |
/* Remember output size that will be generated. */ areq->outlen = len; |
2d97591ef
|
118 |
skcipher_request_set_callback(&areq->cra_u.skcipher_req, |
e870456d8
|
119 |
CRYPTO_TFM_REQ_MAY_SLEEP, |
2d97591ef
|
120 121 122 123 |
af_alg_async_cb, areq); err = ctx->enc ? crypto_skcipher_encrypt(&areq->cra_u.skcipher_req) : crypto_skcipher_decrypt(&areq->cra_u.skcipher_req); |
7d2c3f54e
|
124 125 |
/* AIO operation in progress */ |
d53c51357
|
126 |
if (err == -EINPROGRESS || err == -EBUSY) |
7d2c3f54e
|
127 |
return -EIOCBQUEUED; |
7d2c3f54e
|
128 129 |
sock_put(sk); |
e870456d8
|
130 131 |
} else { /* Synchronous operation */ |
2d97591ef
|
132 |
skcipher_request_set_callback(&areq->cra_u.skcipher_req, |
e870456d8
|
133 134 |
CRYPTO_TFM_REQ_MAY_SLEEP | CRYPTO_TFM_REQ_MAY_BACKLOG, |
2c3f8b162
|
135 136 |
crypto_req_done, &ctx->wait); err = crypto_wait_req(ctx->enc ? |
2d97591ef
|
137 138 |
crypto_skcipher_encrypt(&areq->cra_u.skcipher_req) : crypto_skcipher_decrypt(&areq->cra_u.skcipher_req), |
2c3f8b162
|
139 |
&ctx->wait); |
e870456d8
|
140 |
} |
033f46b3c
|
141 |
|
e870456d8
|
142 |
|
a596999b7
|
143 |
free: |
7d2c3f54e
|
144 |
af_alg_free_resources(areq); |
e870456d8
|
145 146 |
return err ? err : len; |
a596999b7
|
147 |
} |
e870456d8
|
148 149 |
static int skcipher_recvmsg(struct socket *sock, struct msghdr *msg, size_t ignored, int flags) |
8ff590903
|
150 151 |
{ struct sock *sk = sock->sk; |
e870456d8
|
152 |
int ret = 0; |
8ff590903
|
153 154 |
lock_sock(sk); |
01e97e651
|
155 |
while (msg_data_left(msg)) { |
e870456d8
|
156 157 158 159 160 161 162 |
int err = _skcipher_recvmsg(sock, msg, ignored, flags); /* * This error covers -EIOCBQUEUED which implies that we can * only handle one AIO request. If the caller wants to have * multiple AIO requests in parallel, he must make multiple * separate AIO calls. |
5703c826b
|
163 164 |
* * Also return the error if no data has been processed so far. |
e870456d8
|
165 166 |
*/ if (err <= 0) { |
5703c826b
|
167 |
if (err == -EIOCBQUEUED || !ret) |
e870456d8
|
168 169 |
ret = err; goto out; |
1d10eb2f1
|
170 |
} |
e870456d8
|
171 |
ret += err; |
8ff590903
|
172 |
} |
e870456d8
|
173 |
out: |
2d97591ef
|
174 |
af_alg_wmem_wakeup(sk); |
8ff590903
|
175 |
release_sock(sk); |
e870456d8
|
176 |
return ret; |
a596999b7
|
177 |
} |
8ff590903
|
178 |
|
8ff590903
|
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 |
static struct proto_ops algif_skcipher_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 = skcipher_sendmsg, |
2d97591ef
|
196 |
.sendpage = af_alg_sendpage, |
8ff590903
|
197 |
.recvmsg = skcipher_recvmsg, |
a11e1d432
|
198 |
.poll = af_alg_poll, |
8ff590903
|
199 |
}; |
a0fa2d037
|
200 201 |
static int skcipher_check_key(struct socket *sock) { |
1822793a5
|
202 |
int err = 0; |
a0fa2d037
|
203 204 |
struct sock *psk; struct alg_sock *pask; |
f8d33fac8
|
205 |
struct crypto_skcipher *tfm; |
a0fa2d037
|
206 207 |
struct sock *sk = sock->sk; struct alg_sock *ask = alg_sk(sk); |
1822793a5
|
208 |
lock_sock(sk); |
a0fa2d037
|
209 |
if (ask->refcnt) |
1822793a5
|
210 |
goto unlock_child; |
a0fa2d037
|
211 212 213 214 215 216 |
psk = ask->parent; pask = alg_sk(ask->parent); tfm = pask->private; err = -ENOKEY; |
1822793a5
|
217 |
lock_sock_nested(psk, SINGLE_DEPTH_NESTING); |
f8d33fac8
|
218 |
if (crypto_skcipher_get_flags(tfm) & CRYPTO_TFM_NEED_KEY) |
a0fa2d037
|
219 220 221 222 223 224 225 226 227 228 229 230 |
goto unlock; if (!pask->refcnt++) sock_hold(psk); ask->refcnt = 1; sock_put(psk); err = 0; unlock: release_sock(psk); |
1822793a5
|
231 232 |
unlock_child: release_sock(sk); |
a0fa2d037
|
233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 |
return err; } static int skcipher_sendmsg_nokey(struct socket *sock, struct msghdr *msg, size_t size) { int err; err = skcipher_check_key(sock); if (err) return err; return skcipher_sendmsg(sock, msg, size); } static ssize_t skcipher_sendpage_nokey(struct socket *sock, struct page *page, int offset, size_t size, int flags) { int err; err = skcipher_check_key(sock); if (err) return err; |
2d97591ef
|
257 |
return af_alg_sendpage(sock, page, offset, size, flags); |
a0fa2d037
|
258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 |
} static int skcipher_recvmsg_nokey(struct socket *sock, struct msghdr *msg, size_t ignored, int flags) { int err; err = skcipher_check_key(sock); if (err) return err; return skcipher_recvmsg(sock, msg, ignored, flags); } static struct proto_ops algif_skcipher_ops_nokey = { .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 = skcipher_sendmsg_nokey, .sendpage = skcipher_sendpage_nokey, .recvmsg = skcipher_recvmsg_nokey, |
a11e1d432
|
291 |
.poll = af_alg_poll, |
a0fa2d037
|
292 |
}; |
8ff590903
|
293 294 |
static void *skcipher_bind(const char *name, u32 type, u32 mask) { |
f8d33fac8
|
295 |
return crypto_alloc_skcipher(name, type, mask); |
8ff590903
|
296 297 298 299 |
} static void skcipher_release(void *private) { |
f8d33fac8
|
300 |
crypto_free_skcipher(private); |
8ff590903
|
301 302 303 304 |
} static int skcipher_setkey(void *private, const u8 *key, unsigned int keylen) { |
f8d33fac8
|
305 |
return crypto_skcipher_setkey(private, key, keylen); |
8ff590903
|
306 307 308 309 310 |
} static void skcipher_sock_destruct(struct sock *sk) { struct alg_sock *ask = alg_sk(sk); |
2d97591ef
|
311 |
struct af_alg_ctx *ctx = ask->private; |
e870456d8
|
312 313 |
struct sock *psk = ask->parent; struct alg_sock *pask = alg_sk(psk); |
f8d33fac8
|
314 |
struct crypto_skcipher *tfm = pask->private; |
a596999b7
|
315 |
|
2d97591ef
|
316 |
af_alg_pull_tsgl(sk, ctx->used, NULL, 0); |
0d96e4bab
|
317 |
sock_kzfree_s(sk, ctx->iv, crypto_skcipher_ivsize(tfm)); |
8ff590903
|
318 319 320 |
sock_kfree_s(sk, ctx, ctx->len); af_alg_release_parent(sk); } |
d7b65aee1
|
321 |
static int skcipher_accept_parent_nokey(void *private, struct sock *sk) |
8ff590903
|
322 |
{ |
2d97591ef
|
323 |
struct af_alg_ctx *ctx; |
8ff590903
|
324 |
struct alg_sock *ask = alg_sk(sk); |
f8d33fac8
|
325 |
struct crypto_skcipher *tfm = private; |
e870456d8
|
326 |
unsigned int len = sizeof(*ctx); |
8ff590903
|
327 328 329 330 |
ctx = sock_kmalloc(sk, len, GFP_KERNEL); if (!ctx) return -ENOMEM; |
f8d33fac8
|
331 |
ctx->iv = sock_kmalloc(sk, crypto_skcipher_ivsize(tfm), |
8ff590903
|
332 333 334 335 336 |
GFP_KERNEL); if (!ctx->iv) { sock_kfree_s(sk, ctx, len); return -ENOMEM; } |
f8d33fac8
|
337 |
memset(ctx->iv, 0, crypto_skcipher_ivsize(tfm)); |
8ff590903
|
338 |
|
e870456d8
|
339 |
INIT_LIST_HEAD(&ctx->tsgl_list); |
8ff590903
|
340 341 |
ctx->len = len; ctx->used = 0; |
af955bf15
|
342 |
atomic_set(&ctx->rcvused, 0); |
8ff590903
|
343 344 345 |
ctx->more = 0; ctx->merge = 0; ctx->enc = 0; |
2c3f8b162
|
346 |
crypto_init_wait(&ctx->wait); |
8ff590903
|
347 348 |
ask->private = ctx; |
8ff590903
|
349 350 351 352 |
sk->sk_destruct = skcipher_sock_destruct; return 0; } |
a0fa2d037
|
353 354 |
static int skcipher_accept_parent(void *private, struct sock *sk) { |
f8d33fac8
|
355 |
struct crypto_skcipher *tfm = private; |
a0fa2d037
|
356 |
|
f8d33fac8
|
357 |
if (crypto_skcipher_get_flags(tfm) & CRYPTO_TFM_NEED_KEY) |
a0fa2d037
|
358 |
return -ENOKEY; |
d7b65aee1
|
359 |
return skcipher_accept_parent_nokey(private, sk); |
a0fa2d037
|
360 |
} |
8ff590903
|
361 362 363 364 365 |
static const struct af_alg_type algif_type_skcipher = { .bind = skcipher_bind, .release = skcipher_release, .setkey = skcipher_setkey, .accept = skcipher_accept_parent, |
a0fa2d037
|
366 |
.accept_nokey = skcipher_accept_parent_nokey, |
8ff590903
|
367 |
.ops = &algif_skcipher_ops, |
a0fa2d037
|
368 |
.ops_nokey = &algif_skcipher_ops_nokey, |
8ff590903
|
369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 |
.name = "skcipher", .owner = THIS_MODULE }; static int __init algif_skcipher_init(void) { return af_alg_register_type(&algif_type_skcipher); } static void __exit algif_skcipher_exit(void) { int err = af_alg_unregister_type(&algif_type_skcipher); BUG_ON(err); } module_init(algif_skcipher_init); module_exit(algif_skcipher_exit); MODULE_LICENSE("GPL"); |