1 /* $OpenBSD: crypto.c,v 1.46 2004/12/21 10:07:34 mpf 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/proc.h>
27 #include <sys/pool.h>
28 #include <crypto/cryptodev.h>
29
30 struct cryptocap *crypto_drivers = NULL;
31 int crypto_drivers_num = 0;
32
33 struct pool cryptop_pool;
34 struct pool cryptodesc_pool;
35 int crypto_pool_initialized = 0;
36
37 struct cryptop *crp_req_queue = NULL;
38 struct cryptop **crp_req_queue_tail = NULL;
39
40 struct cryptkop *krp_req_queue = NULL;
41 struct cryptkop **krp_req_queue_tail = NULL;
42
43 /*
44 * Create a new session.
45 */
46 int
crypto_newsession(u_int64_t * sid,struct cryptoini * cri,int hard)47 crypto_newsession(u_int64_t *sid, struct cryptoini *cri, int hard)
48 {
49 u_int32_t hid, lid, hid2 = -1;
50 struct cryptocap *cpc;
51 struct cryptoini *cr;
52 int err, s, turn = 0;
53
54 if (crypto_drivers == NULL)
55 return EINVAL;
56
57 s = splimp();
58
59 /*
60 * The algorithm we use here is pretty stupid; just use the
61 * first driver that supports all the algorithms we need. Do
62 * a double-pass over all the drivers, ignoring software ones
63 * at first, to deal with cases of drivers that register after
64 * the software one(s) --- e.g., PCMCIA crypto cards.
65 *
66 * XXX We need more smarts here (in real life too, but that's
67 * XXX another story altogether).
68 */
69 do {
70 for (hid = 0; hid < crypto_drivers_num; hid++) {
71 cpc = &crypto_drivers[hid];
72
73 /*
74 * If it's not initialized or has remaining sessions
75 * referencing it, skip.
76 */
77 if (cpc->cc_newsession == NULL ||
78 (cpc->cc_flags & CRYPTOCAP_F_CLEANUP))
79 continue;
80
81 if (cpc->cc_flags & CRYPTOCAP_F_SOFTWARE) {
82 /*
83 * First round of search, ignore
84 * software drivers.
85 */
86 if (turn == 0)
87 continue;
88 } else { /* !CRYPTOCAP_F_SOFTWARE */
89 /* Second round of search, only software. */
90 if (turn == 1)
91 continue;
92 }
93
94 /* See if all the algorithms are supported. */
95 for (cr = cri; cr; cr = cr->cri_next) {
96 if (cpc->cc_alg[cr->cri_alg] == 0)
97 break;
98 }
99
100 /*
101 * If even one algorithm is not supported,
102 * keep searching.
103 */
104 if (cr != NULL)
105 continue;
106
107 /*
108 * If we had a previous match, see how it compares
109 * to this one. Keep "remembering" whichever is
110 * the best of the two.
111 */
112 if (hid2 != -1) {
113 /*
114 * Compare session numbers, pick the one
115 * with the lowest.
116 * XXX Need better metrics, this will
117 * XXX just do un-weighted round-robin.
118 */
119 if (crypto_drivers[hid].cc_sessions <=
120 crypto_drivers[hid2].cc_sessions)
121 hid2 = hid;
122 } else {
123 /*
124 * Remember this one, for future
125 * comparisons.
126 */
127 hid2 = hid;
128 }
129 }
130
131 /*
132 * If we found something worth remembering, leave. The
133 * side-effect is that we will always prefer a hardware
134 * driver over the software one.
135 */
136 if (hid2 != -1)
137 break;
138
139 turn++;
140
141 /* If we only want hardware drivers, don't do second pass. */
142 } while (turn <= 2 && hard == 0);
143
144 hid = hid2;
145
146 /*
147 * Can't do everything in one session.
148 *
149 * XXX Fix this. We need to inject a "virtual" session
150 * XXX layer right about here.
151 */
152
153 if (hid == -1) {
154 splx(s);
155 return EINVAL;
156 }
157
158 /* Call the driver initialization routine. */
159 lid = hid; /* Pass the driver ID. */
160 err = crypto_drivers[hid].cc_newsession(&lid, cri);
161 if (err == 0) {
162 (*sid) = hid;
163 (*sid) <<= 32;
164 (*sid) |= (lid & 0xffffffff);
165 crypto_drivers[hid].cc_sessions++;
166 }
167
168 splx(s);
169 return err;
170 }
171
172 /*
173 * Delete an existing session (or a reserved session on an unregistered
174 * driver).
175 */
176 int
crypto_freesession(u_int64_t sid)177 crypto_freesession(u_int64_t sid)
178 {
179 int err = 0, s;
180 u_int32_t hid;
181
182 if (crypto_drivers == NULL)
183 return EINVAL;
184
185 /* Determine two IDs. */
186 hid = (sid >> 32) & 0xffffffff;
187
188 if (hid >= crypto_drivers_num)
189 return ENOENT;
190
191 s = splimp();
192
193 if (crypto_drivers[hid].cc_sessions)
194 crypto_drivers[hid].cc_sessions--;
195
196 /* Call the driver cleanup routine, if available. */
197 if (crypto_drivers[hid].cc_freesession)
198 err = crypto_drivers[hid].cc_freesession(sid);
199
200 /*
201 * If this was the last session of a driver marked as invalid,
202 * make the entry available for reuse.
203 */
204 if ((crypto_drivers[hid].cc_flags & CRYPTOCAP_F_CLEANUP) &&
205 crypto_drivers[hid].cc_sessions == 0)
206 bzero(&crypto_drivers[hid], sizeof(struct cryptocap));
207
208 splx(s);
209 return err;
210 }
211
212 /*
213 * Find an empty slot.
214 */
215 int32_t
crypto_get_driverid(u_int8_t flags)216 crypto_get_driverid(u_int8_t flags)
217 {
218 struct cryptocap *newdrv;
219 int i, s = splimp();
220
221 if (crypto_drivers_num == 0) {
222 crypto_drivers_num = CRYPTO_DRIVERS_INITIAL;
223 crypto_drivers = malloc(crypto_drivers_num *
224 sizeof(struct cryptocap), M_CRYPTO_DATA, M_NOWAIT);
225 if (crypto_drivers == NULL) {
226 splx(s);
227 crypto_drivers_num = 0;
228 return -1;
229 }
230
231 bzero(crypto_drivers, crypto_drivers_num *
232 sizeof(struct cryptocap));
233 }
234
235 for (i = 0; i < crypto_drivers_num; i++) {
236 if (crypto_drivers[i].cc_process == NULL &&
237 !(crypto_drivers[i].cc_flags & CRYPTOCAP_F_CLEANUP) &&
238 crypto_drivers[i].cc_sessions == 0) {
239 crypto_drivers[i].cc_sessions = 1; /* Mark */
240 crypto_drivers[i].cc_flags = flags;
241 splx(s);
242 return i;
243 }
244 }
245
246 /* Out of entries, allocate some more. */
247 if (i == crypto_drivers_num) {
248 /* Be careful about wrap-around. */
249 if (2 * crypto_drivers_num <= crypto_drivers_num) {
250 splx(s);
251 return -1;
252 }
253
254 newdrv = malloc(2 * crypto_drivers_num *
255 sizeof(struct cryptocap), M_CRYPTO_DATA, M_NOWAIT);
256 if (newdrv == NULL) {
257 splx(s);
258 return -1;
259 }
260
261 bcopy(crypto_drivers, newdrv,
262 crypto_drivers_num * sizeof(struct cryptocap));
263 bzero(&newdrv[crypto_drivers_num],
264 crypto_drivers_num * sizeof(struct cryptocap));
265
266 newdrv[i].cc_sessions = 1; /* Mark */
267 newdrv[i].cc_flags = flags;
268 crypto_drivers_num *= 2;
269
270 free(crypto_drivers, M_CRYPTO_DATA);
271 crypto_drivers = newdrv;
272 splx(s);
273 return i;
274 }
275
276 /* Shouldn't really get here... */
277 splx(s);
278 return -1;
279 }
280
281 /*
282 * Register a crypto driver. It should be called once for each algorithm
283 * supported by the driver.
284 */
285 int
crypto_kregister(u_int32_t driverid,int * kalg,int (* kprocess)(struct cryptkop *))286 crypto_kregister(u_int32_t driverid, int *kalg,
287 int (*kprocess)(struct cryptkop *))
288 {
289 int s, i;
290
291 if (driverid >= crypto_drivers_num || kalg == NULL ||
292 crypto_drivers == NULL)
293 return EINVAL;
294
295 s = splimp();
296
297 for (i = 0; i < CRK_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 crypto_drivers[driverid].cc_kalg[i] = kalg[i];
305 }
306
307 crypto_drivers[driverid].cc_kprocess = kprocess;
308
309 splx(s);
310 return 0;
311 }
312
313 /* Register a crypto driver. */
314 int
crypto_register(u_int32_t driverid,int * alg,int (* newses)(u_int32_t *,struct cryptoini *),int (* freeses)(u_int64_t),int (* process)(struct cryptop *))315 crypto_register(u_int32_t driverid, int *alg,
316 int (*newses)(u_int32_t *, struct cryptoini *),
317 int (*freeses)(u_int64_t), int (*process)(struct cryptop *))
318 {
319 int s, i;
320
321
322 if (driverid >= crypto_drivers_num || alg == NULL ||
323 crypto_drivers == NULL)
324 return EINVAL;
325
326 s = splimp();
327
328 for (i = 0; i < CRYPTO_ALGORITHM_ALL; i++) {
329 /*
330 * XXX Do some performance testing to determine
331 * placing. We probably need an auxiliary data
332 * structure that describes relative performances.
333 */
334
335 crypto_drivers[driverid].cc_alg[i] = alg[i];
336 }
337
338
339 crypto_drivers[driverid].cc_newsession = newses;
340 crypto_drivers[driverid].cc_process = process;
341 crypto_drivers[driverid].cc_freesession = freeses;
342 crypto_drivers[driverid].cc_sessions = 0; /* Unmark */
343
344 splx(s);
345
346 return 0;
347 }
348
349 /*
350 * Unregister a crypto driver. If there are pending sessions using it,
351 * leave enough information around so that subsequent calls using those
352 * sessions will correctly detect the driver being unregistered and reroute
353 * the request.
354 */
355 int
crypto_unregister(u_int32_t driverid,int alg)356 crypto_unregister(u_int32_t driverid, int alg)
357 {
358 int i = CRYPTO_ALGORITHM_MAX + 1, s = splimp();
359 u_int32_t ses;
360
361 /* Sanity checks. */
362 if (driverid >= crypto_drivers_num || crypto_drivers == NULL ||
363 ((alg <= 0 || alg > CRYPTO_ALGORITHM_MAX) &&
364 alg != CRYPTO_ALGORITHM_ALL) ||
365 crypto_drivers[driverid].cc_alg[alg] == 0) {
366 splx(s);
367 return EINVAL;
368 }
369
370 if (alg != CRYPTO_ALGORITHM_ALL) {
371 crypto_drivers[driverid].cc_alg[alg] = 0;
372
373 /* Was this the last algorithm ? */
374 for (i = 1; i <= CRYPTO_ALGORITHM_MAX; i++)
375 if (crypto_drivers[driverid].cc_alg[i] != 0)
376 break;
377 }
378
379 /*
380 * If a driver unregistered its last algorithm or all of them
381 * (alg == CRYPTO_ALGORITHM_ALL), cleanup its entry.
382 */
383 if (i == CRYPTO_ALGORITHM_MAX + 1 || alg == CRYPTO_ALGORITHM_ALL) {
384 ses = crypto_drivers[driverid].cc_sessions;
385 bzero(&crypto_drivers[driverid], sizeof(struct cryptocap));
386 if (ses != 0) {
387 /*
388 * If there are pending sessions, just mark as invalid.
389 */
390 crypto_drivers[driverid].cc_flags |= CRYPTOCAP_F_CLEANUP;
391 crypto_drivers[driverid].cc_sessions = ses;
392 }
393 }
394 splx(s);
395 return 0;
396 }
397
398 /*
399 * Add crypto request to a queue, to be processed by a kernel thread.
400 */
401 int
crypto_dispatch(struct cryptop * crp)402 crypto_dispatch(struct cryptop *crp)
403 {
404 int s = splimp();
405 u_int32_t hid;
406
407 /*
408 * Keep track of ops per driver, for coallescing purposes. If
409 * we have been given an invalid hid, we'll deal with in the
410 * crypto_invoke(), through session migration.
411 */
412 hid = (crp->crp_sid >> 32) & 0xffffffff;
413 if (hid < crypto_drivers_num)
414 crypto_drivers[hid].cc_queued++;
415
416 crp->crp_next = NULL;
417 if (crp_req_queue == NULL) {
418 crp_req_queue = crp;
419 crp_req_queue_tail = &(crp->crp_next);
420 splx(s);
421 wakeup((caddr_t) &crp_req_queue); /* Shared wait channel. */
422 } else {
423 *crp_req_queue_tail = crp;
424 crp_req_queue_tail = &(crp->crp_next);
425 splx(s);
426 }
427 return 0;
428 }
429
430 int
crypto_kdispatch(struct cryptkop * krp)431 crypto_kdispatch(struct cryptkop *krp)
432 {
433 int s = splimp();
434
435 krp->krp_next = NULL;
436 if (krp_req_queue == NULL) {
437 krp_req_queue = krp;
438 krp_req_queue_tail = &(krp->krp_next);
439 splx(s);
440 wakeup((caddr_t) &crp_req_queue); /* Shared wait channel. */
441 } else {
442 *krp_req_queue_tail = krp;
443 krp_req_queue_tail = &(krp->krp_next);
444 splx(s);
445 }
446 return 0;
447 }
448
449 /*
450 * Dispatch an asymmetric crypto request to the appropriate crypto devices.
451 */
452 int
crypto_kinvoke(struct cryptkop * krp)453 crypto_kinvoke(struct cryptkop *krp)
454 {
455 extern int cryptodevallowsoft;
456 u_int32_t hid;
457 int error;
458
459 /* Sanity checks. */
460 if (krp == NULL || krp->krp_callback == NULL)
461 return (EINVAL);
462
463 for (hid = 0; hid < crypto_drivers_num; hid++) {
464 if ((crypto_drivers[hid].cc_flags & CRYPTOCAP_F_SOFTWARE) &&
465 cryptodevallowsoft == 0)
466 continue;
467 if (crypto_drivers[hid].cc_kprocess == NULL)
468 continue;
469 if ((crypto_drivers[hid].cc_kalg[krp->krp_op] &
470 CRYPTO_ALG_FLAG_SUPPORTED) == 0)
471 continue;
472 break;
473 }
474
475 if (hid == crypto_drivers_num) {
476 krp->krp_status = ENODEV;
477 crypto_kdone(krp);
478 return (0);
479 }
480
481 krp->krp_hid = hid;
482
483 crypto_drivers[hid].cc_koperations++;
484
485 error = crypto_drivers[hid].cc_kprocess(krp);
486 if (error) {
487 krp->krp_status = error;
488 crypto_kdone(krp);
489 }
490 return (0);
491 }
492
493 /*
494 * Dispatch a crypto request to the appropriate crypto devices.
495 */
496 int
crypto_invoke(struct cryptop * crp)497 crypto_invoke(struct cryptop *crp)
498 {
499 struct cryptodesc *crd;
500 u_int64_t nid;
501 u_int32_t hid;
502 int error;
503
504 /* Sanity checks. */
505 if (crp == NULL || crp->crp_callback == NULL)
506 return EINVAL;
507
508 if (crp->crp_desc == NULL || crypto_drivers == NULL) {
509 crp->crp_etype = EINVAL;
510 crypto_done(crp);
511 return 0;
512 }
513
514 hid = (crp->crp_sid >> 32) & 0xffffffff;
515 if (hid >= crypto_drivers_num)
516 goto migrate;
517
518 crypto_drivers[hid].cc_queued--;
519
520 if (crypto_drivers[hid].cc_flags & CRYPTOCAP_F_CLEANUP) {
521 crypto_freesession(crp->crp_sid);
522 goto migrate;
523 }
524
525 if (crypto_drivers[hid].cc_process == NULL)
526 goto migrate;
527
528 crypto_drivers[hid].cc_operations++;
529 crypto_drivers[hid].cc_bytes += crp->crp_ilen;
530
531 error = crypto_drivers[hid].cc_process(crp);
532 if (error) {
533 if (error == ERESTART) {
534 /* Unregister driver and migrate session. */
535 crypto_unregister(hid, CRYPTO_ALGORITHM_ALL);
536 goto migrate;
537 } else {
538 crp->crp_etype = error;
539 crypto_done(crp);
540 }
541 }
542
543 return 0;
544
545 migrate:
546 /* Migrate session. */
547 for (crd = crp->crp_desc; crd->crd_next; crd = crd->crd_next)
548 crd->CRD_INI.cri_next = &(crd->crd_next->CRD_INI);
549
550 if (crypto_newsession(&nid, &(crp->crp_desc->CRD_INI), 0) == 0)
551 crp->crp_sid = nid;
552
553 crp->crp_etype = EAGAIN;
554 crypto_done(crp);
555 return 0;
556 }
557
558 /*
559 * Release a set of crypto descriptors.
560 */
561 void
crypto_freereq(struct cryptop * crp)562 crypto_freereq(struct cryptop *crp)
563 {
564 struct cryptodesc *crd;
565 int s;
566
567 if (crp == NULL)
568 return;
569
570 s = splimp();
571
572 while ((crd = crp->crp_desc) != NULL) {
573 crp->crp_desc = crd->crd_next;
574 pool_put(&cryptodesc_pool, crd);
575 }
576
577 pool_put(&cryptop_pool, crp);
578 splx(s);
579 }
580
581 /*
582 * Acquire a set of crypto descriptors.
583 */
584 struct cryptop *
crypto_getreq(int num)585 crypto_getreq(int num)
586 {
587 struct cryptodesc *crd;
588 struct cryptop *crp;
589 int s = splimp();
590
591 if (crypto_pool_initialized == 0) {
592 pool_init(&cryptop_pool, sizeof(struct cryptop), 0, 0,
593 0, "cryptop", NULL);
594 pool_init(&cryptodesc_pool, sizeof(struct cryptodesc), 0, 0,
595 0, "cryptodesc", NULL);
596 crypto_pool_initialized = 1;
597 }
598
599 crp = pool_get(&cryptop_pool, 0);
600 if (crp == NULL) {
601 splx(s);
602 return NULL;
603 }
604 bzero(crp, sizeof(struct cryptop));
605
606 while (num--) {
607 crd = pool_get(&cryptodesc_pool, 0);
608 if (crd == NULL) {
609 splx(s);
610 crypto_freereq(crp);
611 return NULL;
612 }
613
614 bzero(crd, sizeof(struct cryptodesc));
615 crd->crd_next = crp->crp_desc;
616 crp->crp_desc = crd;
617 }
618
619 splx(s);
620 return crp;
621 }
622
623 /*
624 * Crypto thread, runs as a kernel thread to process crypto requests.
625 */
626 void
crypto_thread(void)627 crypto_thread(void)
628 {
629 struct cryptop *crp;
630 struct cryptkop *krp;
631 int s;
632
633 s = splimp();
634
635 for (;;) {
636 crp = crp_req_queue;
637 krp = krp_req_queue;
638 if (crp == NULL && krp == NULL) {
639 (void) tsleep(&crp_req_queue, PLOCK, "crypto_wait", 0);
640 continue;
641 }
642
643 if (crp) {
644 /* Remove from the queue. */
645 crp_req_queue = crp->crp_next;
646 crypto_invoke(crp);
647 }
648 if (krp) {
649 /* Remove from the queue. */
650 krp_req_queue = krp->krp_next;
651 crypto_kinvoke(krp);
652 }
653 }
654 }
655
656 /*
657 * Invoke the callback on behalf of the driver.
658 */
659 void
crypto_done(struct cryptop * crp)660 crypto_done(struct cryptop *crp)
661 {
662 crp->crp_flags |= CRYPTO_F_DONE;
663 crp->crp_callback(crp);
664 }
665
666 /*
667 * Invoke the callback on behalf of the driver.
668 */
669 void
crypto_kdone(struct cryptkop * krp)670 crypto_kdone(struct cryptkop *krp)
671 {
672 krp->krp_callback(krp);
673 }
674
675 int
crypto_getfeat(int * featp)676 crypto_getfeat(int *featp)
677 {
678 extern int cryptodevallowsoft, userasymcrypto;
679 int hid, kalg, feat = 0;
680
681 if (userasymcrypto == 0)
682 goto out;
683 for (hid = 0; hid < crypto_drivers_num; hid++) {
684 if ((crypto_drivers[hid].cc_flags & CRYPTOCAP_F_SOFTWARE) &&
685 cryptodevallowsoft == 0) {
686 continue;
687 }
688 if (crypto_drivers[hid].cc_kprocess == NULL)
689 continue;
690 for (kalg = 0; kalg < CRK_ALGORITHM_MAX; kalg++)
691 if ((crypto_drivers[hid].cc_kalg[kalg] &
692 CRYPTO_ALG_FLAG_SUPPORTED) != 0)
693 feat |= 1 << kalg;
694 }
695 out:
696 *featp = feat;
697 return (0);
698 }
699