Line data Source code
1 : /* $OpenBSD: crypto.c,v 1.80 2017/11/30 16:31:12 visa Exp $ */
2 : /*
3 : * The author of this code is Angelos D. Keromytis (angelos@cis.upenn.edu)
4 : *
5 : * This code was written by Angelos D. Keromytis in Athens, Greece, in
6 : * February 2000. Network Security Technologies Inc. (NSTI) kindly
7 : * supported the development of this code.
8 : *
9 : * Copyright (c) 2000, 2001 Angelos D. Keromytis
10 : *
11 : * Permission to use, copy, and modify this software with or without fee
12 : * is hereby granted, provided that this entire notice is included in
13 : * all source code copies of any software which is or includes a copy or
14 : * modification of this software.
15 : *
16 : * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR
17 : * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY
18 : * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE
19 : * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR
20 : * PURPOSE.
21 : */
22 :
23 : #include <sys/param.h>
24 : #include <sys/systm.h>
25 : #include <sys/malloc.h>
26 : #include <sys/pool.h>
27 :
28 : #include <crypto/cryptodev.h>
29 :
30 : void crypto_init(void);
31 :
32 : struct cryptocap *crypto_drivers = NULL;
33 : int crypto_drivers_num = 0;
34 :
35 : struct pool cryptop_pool;
36 : struct pool cryptodesc_pool;
37 :
38 : struct taskq *crypto_taskq;
39 : struct taskq *crypto_taskq_mpsafe;
40 :
41 : /*
42 : * Create a new session.
43 : */
44 : int
45 0 : crypto_newsession(u_int64_t *sid, struct cryptoini *cri, int hard)
46 : {
47 0 : u_int32_t hid, lid, hid2 = -1;
48 : struct cryptocap *cpc;
49 : struct cryptoini *cr;
50 : int err, s, turn = 0;
51 :
52 0 : if (crypto_drivers == NULL)
53 0 : return EINVAL;
54 :
55 0 : s = splvm();
56 :
57 : /*
58 : * The algorithm we use here is pretty stupid; just use the
59 : * first driver that supports all the algorithms we need. Do
60 : * a double-pass over all the drivers, ignoring software ones
61 : * at first, to deal with cases of drivers that register after
62 : * the software one(s) --- e.g., PCMCIA crypto cards.
63 : *
64 : * XXX We need more smarts here (in real life too, but that's
65 : * XXX another story altogether).
66 : */
67 0 : do {
68 0 : for (hid = 0; hid < crypto_drivers_num; hid++) {
69 0 : cpc = &crypto_drivers[hid];
70 :
71 : /*
72 : * If it's not initialized or has remaining sessions
73 : * referencing it, skip.
74 : */
75 0 : if (cpc->cc_newsession == NULL ||
76 0 : (cpc->cc_flags & CRYPTOCAP_F_CLEANUP))
77 : continue;
78 :
79 0 : if (cpc->cc_flags & CRYPTOCAP_F_SOFTWARE) {
80 : /*
81 : * First round of search, ignore
82 : * software drivers.
83 : */
84 0 : if (turn == 0)
85 : continue;
86 : } else { /* !CRYPTOCAP_F_SOFTWARE */
87 : /* Second round of search, only software. */
88 0 : if (turn == 1)
89 : continue;
90 : }
91 :
92 : /* See if all the algorithms are supported. */
93 0 : for (cr = cri; cr; cr = cr->cri_next) {
94 0 : if (cpc->cc_alg[cr->cri_alg] == 0)
95 : break;
96 : }
97 :
98 : /*
99 : * If even one algorithm is not supported,
100 : * keep searching.
101 : */
102 0 : if (cr != NULL)
103 : continue;
104 :
105 : /*
106 : * If we had a previous match, see how it compares
107 : * to this one. Keep "remembering" whichever is
108 : * the best of the two.
109 : */
110 0 : if (hid2 != -1) {
111 : /*
112 : * Compare session numbers, pick the one
113 : * with the lowest.
114 : * XXX Need better metrics, this will
115 : * XXX just do un-weighted round-robin.
116 : */
117 0 : if (crypto_drivers[hid].cc_sessions <=
118 0 : crypto_drivers[hid2].cc_sessions)
119 0 : hid2 = hid;
120 : } else {
121 : /*
122 : * Remember this one, for future
123 : * comparisons.
124 : */
125 : hid2 = hid;
126 : }
127 : }
128 :
129 : /*
130 : * If we found something worth remembering, leave. The
131 : * side-effect is that we will always prefer a hardware
132 : * driver over the software one.
133 : */
134 0 : if (hid2 != -1)
135 : break;
136 :
137 0 : turn++;
138 :
139 : /* If we only want hardware drivers, don't do second pass. */
140 0 : } while (turn <= 2 && hard == 0);
141 :
142 : hid = hid2;
143 :
144 : /*
145 : * Can't do everything in one session.
146 : *
147 : * XXX Fix this. We need to inject a "virtual" session
148 : * XXX layer right about here.
149 : */
150 :
151 0 : if (hid == -1) {
152 0 : splx(s);
153 0 : return EINVAL;
154 : }
155 :
156 : /* Call the driver initialization routine. */
157 0 : lid = hid; /* Pass the driver ID. */
158 0 : err = crypto_drivers[hid].cc_newsession(&lid, cri);
159 0 : if (err == 0) {
160 : (*sid) = hid;
161 0 : (*sid) <<= 32;
162 0 : (*sid) |= (lid & 0xffffffff);
163 0 : crypto_drivers[hid].cc_sessions++;
164 0 : }
165 :
166 0 : splx(s);
167 0 : return err;
168 0 : }
169 :
170 : /*
171 : * Delete an existing session (or a reserved session on an unregistered
172 : * driver).
173 : */
174 : int
175 0 : crypto_freesession(u_int64_t sid)
176 : {
177 : int err = 0, s;
178 : u_int32_t hid;
179 :
180 0 : if (crypto_drivers == NULL)
181 0 : return EINVAL;
182 :
183 : /* Determine two IDs. */
184 0 : hid = (sid >> 32) & 0xffffffff;
185 :
186 0 : if (hid >= crypto_drivers_num)
187 0 : return ENOENT;
188 :
189 0 : s = splvm();
190 :
191 0 : if (crypto_drivers[hid].cc_sessions)
192 0 : crypto_drivers[hid].cc_sessions--;
193 :
194 : /* Call the driver cleanup routine, if available. */
195 0 : if (crypto_drivers[hid].cc_freesession)
196 0 : err = crypto_drivers[hid].cc_freesession(sid);
197 :
198 : /*
199 : * If this was the last session of a driver marked as invalid,
200 : * make the entry available for reuse.
201 : */
202 0 : if ((crypto_drivers[hid].cc_flags & CRYPTOCAP_F_CLEANUP) &&
203 0 : crypto_drivers[hid].cc_sessions == 0)
204 0 : explicit_bzero(&crypto_drivers[hid], sizeof(struct cryptocap));
205 :
206 0 : splx(s);
207 0 : return err;
208 0 : }
209 :
210 : /*
211 : * Find an empty slot.
212 : */
213 : int32_t
214 0 : crypto_get_driverid(u_int8_t flags)
215 : {
216 : struct cryptocap *newdrv;
217 : int i, s;
218 :
219 0 : s = splvm();
220 :
221 0 : if (crypto_drivers_num == 0) {
222 0 : crypto_drivers_num = CRYPTO_DRIVERS_INITIAL;
223 0 : crypto_drivers = mallocarray(crypto_drivers_num,
224 : sizeof(struct cryptocap), M_CRYPTO_DATA, M_NOWAIT | M_ZERO);
225 0 : if (crypto_drivers == NULL) {
226 0 : crypto_drivers_num = 0;
227 0 : splx(s);
228 0 : return -1;
229 : }
230 : }
231 :
232 0 : for (i = 0; i < crypto_drivers_num; i++) {
233 0 : if (crypto_drivers[i].cc_process == NULL &&
234 0 : !(crypto_drivers[i].cc_flags & CRYPTOCAP_F_CLEANUP) &&
235 0 : crypto_drivers[i].cc_sessions == 0) {
236 0 : crypto_drivers[i].cc_sessions = 1; /* Mark */
237 0 : crypto_drivers[i].cc_flags = flags;
238 0 : splx(s);
239 0 : return i;
240 : }
241 : }
242 :
243 : /* Out of entries, allocate some more. */
244 0 : if (i == crypto_drivers_num) {
245 0 : if (crypto_drivers_num >= CRYPTO_DRIVERS_MAX) {
246 0 : splx(s);
247 0 : return -1;
248 : }
249 :
250 0 : newdrv = mallocarray(crypto_drivers_num,
251 : 2 * sizeof(struct cryptocap), M_CRYPTO_DATA, M_NOWAIT);
252 0 : if (newdrv == NULL) {
253 0 : splx(s);
254 0 : return -1;
255 : }
256 :
257 0 : memcpy(newdrv, crypto_drivers,
258 : crypto_drivers_num * sizeof(struct cryptocap));
259 0 : bzero(&newdrv[crypto_drivers_num],
260 : crypto_drivers_num * sizeof(struct cryptocap));
261 :
262 0 : newdrv[i].cc_sessions = 1; /* Mark */
263 0 : newdrv[i].cc_flags = flags;
264 :
265 0 : free(crypto_drivers, M_CRYPTO_DATA,
266 0 : crypto_drivers_num * sizeof(struct cryptocap));
267 :
268 0 : crypto_drivers_num *= 2;
269 0 : crypto_drivers = newdrv;
270 0 : splx(s);
271 0 : return i;
272 : }
273 :
274 : /* Shouldn't really get here... */
275 0 : splx(s);
276 0 : return -1;
277 0 : }
278 :
279 : /*
280 : * Register a crypto driver. It should be called once for each algorithm
281 : * supported by the driver.
282 : */
283 : int
284 0 : crypto_register(u_int32_t driverid, int *alg,
285 : int (*newses)(u_int32_t *, struct cryptoini *),
286 : int (*freeses)(u_int64_t), int (*process)(struct cryptop *))
287 : {
288 : int s, i;
289 :
290 :
291 0 : if (driverid >= crypto_drivers_num || alg == NULL ||
292 0 : crypto_drivers == NULL)
293 0 : return EINVAL;
294 :
295 0 : s = splvm();
296 :
297 0 : for (i = 0; i <= CRYPTO_ALGORITHM_MAX; i++) {
298 : /*
299 : * XXX Do some performance testing to determine
300 : * placing. We probably need an auxiliary data
301 : * structure that describes relative performances.
302 : */
303 :
304 0 : crypto_drivers[driverid].cc_alg[i] = alg[i];
305 : }
306 :
307 :
308 0 : crypto_drivers[driverid].cc_newsession = newses;
309 0 : crypto_drivers[driverid].cc_process = process;
310 0 : crypto_drivers[driverid].cc_freesession = freeses;
311 0 : crypto_drivers[driverid].cc_sessions = 0; /* Unmark */
312 :
313 0 : splx(s);
314 :
315 0 : return 0;
316 0 : }
317 :
318 : /*
319 : * Unregister a crypto driver. If there are pending sessions using it,
320 : * leave enough information around so that subsequent calls using those
321 : * sessions will correctly detect the driver being unregistered and reroute
322 : * the request.
323 : */
324 : int
325 0 : crypto_unregister(u_int32_t driverid, int alg)
326 : {
327 : int i = CRYPTO_ALGORITHM_MAX + 1, s;
328 : u_int32_t ses;
329 :
330 0 : s = splvm();
331 :
332 : /* Sanity checks. */
333 0 : if (driverid >= crypto_drivers_num || crypto_drivers == NULL ||
334 0 : ((alg <= 0 || alg > CRYPTO_ALGORITHM_MAX) &&
335 0 : alg != CRYPTO_ALGORITHM_MAX + 1) ||
336 0 : crypto_drivers[driverid].cc_alg[alg] == 0) {
337 0 : splx(s);
338 0 : return EINVAL;
339 : }
340 :
341 0 : if (alg != CRYPTO_ALGORITHM_MAX + 1) {
342 0 : crypto_drivers[driverid].cc_alg[alg] = 0;
343 :
344 : /* Was this the last algorithm ? */
345 0 : for (i = 1; i <= CRYPTO_ALGORITHM_MAX; i++)
346 0 : if (crypto_drivers[driverid].cc_alg[i] != 0)
347 : break;
348 : }
349 :
350 : /*
351 : * If a driver unregistered its last algorithm or all of them
352 : * (alg == CRYPTO_ALGORITHM_MAX + 1), cleanup its entry.
353 : */
354 0 : if (i == CRYPTO_ALGORITHM_MAX + 1 || alg == CRYPTO_ALGORITHM_MAX + 1) {
355 0 : ses = crypto_drivers[driverid].cc_sessions;
356 0 : bzero(&crypto_drivers[driverid], sizeof(struct cryptocap));
357 0 : if (ses != 0) {
358 : /*
359 : * If there are pending sessions, just mark as invalid.
360 : */
361 0 : crypto_drivers[driverid].cc_flags |= CRYPTOCAP_F_CLEANUP;
362 0 : crypto_drivers[driverid].cc_sessions = ses;
363 0 : }
364 : }
365 0 : splx(s);
366 0 : return 0;
367 0 : }
368 :
369 : /*
370 : * Add crypto request to a queue, to be processed by a kernel thread.
371 : */
372 : int
373 0 : crypto_dispatch(struct cryptop *crp)
374 : {
375 0 : struct taskq *tq = crypto_taskq;
376 : int s;
377 : u_int32_t hid;
378 :
379 0 : s = splvm();
380 0 : hid = (crp->crp_sid >> 32) & 0xffffffff;
381 0 : if (hid < crypto_drivers_num) {
382 0 : if (crypto_drivers[hid].cc_flags & CRYPTOCAP_F_MPSAFE)
383 0 : tq = crypto_taskq_mpsafe;
384 : }
385 0 : splx(s);
386 :
387 0 : if (tq && !(crp->crp_flags & CRYPTO_F_NOQUEUE)) {
388 0 : task_set(&crp->crp_task, (void (*))crypto_invoke, crp);
389 0 : task_add(tq, &crp->crp_task);
390 0 : } else {
391 0 : crypto_invoke(crp);
392 : }
393 :
394 0 : return 0;
395 : }
396 :
397 : /*
398 : * Dispatch a crypto request to the appropriate crypto devices.
399 : */
400 : int
401 0 : crypto_invoke(struct cryptop *crp)
402 : {
403 0 : u_int64_t nid;
404 : u_int32_t hid;
405 : int error;
406 : int s, i;
407 :
408 : /* Sanity checks. */
409 0 : if (crp == NULL || crp->crp_callback == NULL)
410 0 : return EINVAL;
411 :
412 0 : s = splvm();
413 0 : if (crp->crp_ndesc < 1 || crypto_drivers == NULL) {
414 0 : crp->crp_etype = EINVAL;
415 0 : crypto_done(crp);
416 0 : splx(s);
417 0 : return 0;
418 : }
419 :
420 0 : hid = (crp->crp_sid >> 32) & 0xffffffff;
421 0 : if (hid >= crypto_drivers_num)
422 : goto migrate;
423 :
424 0 : if (crypto_drivers[hid].cc_flags & CRYPTOCAP_F_CLEANUP) {
425 0 : crypto_freesession(crp->crp_sid);
426 0 : goto migrate;
427 : }
428 :
429 0 : if (crypto_drivers[hid].cc_process == NULL)
430 : goto migrate;
431 :
432 0 : crypto_drivers[hid].cc_operations++;
433 0 : crypto_drivers[hid].cc_bytes += crp->crp_ilen;
434 :
435 0 : error = crypto_drivers[hid].cc_process(crp);
436 0 : if (error) {
437 0 : if (error == ERESTART) {
438 : /* Unregister driver and migrate session. */
439 0 : crypto_unregister(hid, CRYPTO_ALGORITHM_MAX + 1);
440 0 : goto migrate;
441 : } else {
442 0 : crp->crp_etype = error;
443 : }
444 0 : }
445 :
446 0 : splx(s);
447 0 : return 0;
448 :
449 : migrate:
450 : /* Migrate session. */
451 0 : for (i = 0; i < crp->crp_ndesc - 1; i++)
452 0 : crp->crp_desc[i].CRD_INI.cri_next = &crp->crp_desc[i+1].CRD_INI;
453 0 : crp->crp_desc[crp->crp_ndesc].CRD_INI.cri_next = NULL;
454 :
455 0 : if (crypto_newsession(&nid, &(crp->crp_desc->CRD_INI), 0) == 0)
456 0 : crp->crp_sid = nid;
457 :
458 0 : crp->crp_etype = EAGAIN;
459 0 : crypto_done(crp);
460 0 : splx(s);
461 0 : return 0;
462 0 : }
463 :
464 : /*
465 : * Release a set of crypto descriptors.
466 : */
467 : void
468 0 : crypto_freereq(struct cryptop *crp)
469 : {
470 0 : if (crp == NULL)
471 : return;
472 :
473 0 : if (crp->crp_ndescalloc > 2)
474 0 : free(crp->crp_desc, M_CRYPTO_DATA,
475 0 : crp->crp_ndescalloc * sizeof(struct cryptodesc));
476 0 : pool_put(&cryptop_pool, crp);
477 0 : }
478 :
479 : /*
480 : * Acquire a set of crypto descriptors.
481 : */
482 : struct cryptop *
483 0 : crypto_getreq(int num)
484 : {
485 : struct cryptop *crp;
486 :
487 0 : crp = pool_get(&cryptop_pool, PR_NOWAIT | PR_ZERO);
488 0 : if (crp == NULL)
489 0 : return NULL;
490 :
491 0 : crp->crp_desc = crp->crp_sdesc;
492 0 : crp->crp_ndescalloc = crp->crp_ndesc = num;
493 :
494 0 : if (num > 2) {
495 0 : crp->crp_desc = mallocarray(num, sizeof(struct cryptodesc),
496 : M_CRYPTO_DATA, M_NOWAIT | M_ZERO);
497 0 : if (crp->crp_desc == NULL) {
498 0 : pool_put(&cryptop_pool, crp);
499 0 : return NULL;
500 : }
501 : }
502 :
503 0 : return crp;
504 0 : }
505 :
506 : void
507 0 : crypto_init(void)
508 : {
509 0 : crypto_taskq = taskq_create("crypto", 1, IPL_VM, 0);
510 0 : crypto_taskq_mpsafe = taskq_create("crynlk", 1, IPL_VM, TASKQ_MPSAFE);
511 :
512 0 : pool_init(&cryptop_pool, sizeof(struct cryptop), 0, IPL_VM, 0,
513 : "cryptop", NULL);
514 0 : }
515 :
516 : /*
517 : * Invoke the callback on behalf of the driver.
518 : */
519 : void
520 0 : crypto_done(struct cryptop *crp)
521 : {
522 0 : crp->crp_flags |= CRYPTO_F_DONE;
523 0 : if (crp->crp_flags & CRYPTO_F_NOQUEUE) {
524 : /* not from the crypto queue, wakeup the userland process */
525 0 : crp->crp_callback(crp);
526 0 : } else {
527 0 : task_set(&crp->crp_task, (void (*))crp->crp_callback, crp);
528 0 : task_add(crypto_taskq, &crp->crp_task);
529 : }
530 0 : }
|