xref: /openbsd/sys/crypto/crypto.c (revision 9b7c3dbb)
1 /*	$OpenBSD: crypto.c,v 1.76 2016/04/18 21:05:55 kettenis 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 crypto_newsession(u_int64_t *sid, struct cryptoini *cri, int hard)
46 {
47 	u_int32_t hid, lid, hid2 = -1;
48 	struct cryptocap *cpc;
49 	struct cryptoini *cr;
50 	int err, s, turn = 0;
51 
52 	if (crypto_drivers == NULL)
53 		return EINVAL;
54 
55 	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 	do {
68 		for (hid = 0; hid < crypto_drivers_num; hid++) {
69 			cpc = &crypto_drivers[hid];
70 
71 			/*
72 			 * If it's not initialized or has remaining sessions
73 			 * referencing it, skip.
74 			 */
75 			if (cpc->cc_newsession == NULL ||
76 			    (cpc->cc_flags & CRYPTOCAP_F_CLEANUP))
77 				continue;
78 
79 			if (cpc->cc_flags & CRYPTOCAP_F_SOFTWARE) {
80 				/*
81 				 * First round of search, ignore
82 				 * software drivers.
83 				 */
84 				if (turn == 0)
85 					continue;
86 			} else { /* !CRYPTOCAP_F_SOFTWARE */
87 				/* Second round of search, only software. */
88 				if (turn == 1)
89 					continue;
90 			}
91 
92 			/* See if all the algorithms are supported. */
93 			for (cr = cri; cr; cr = cr->cri_next) {
94 				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 			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 			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 				if (crypto_drivers[hid].cc_sessions <=
118 				    crypto_drivers[hid2].cc_sessions)
119 					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 		if (hid2 != -1)
135 			break;
136 
137 		turn++;
138 
139 		/* If we only want hardware drivers, don't do second pass. */
140 	} 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 	if (hid == -1) {
152 		splx(s);
153 		return EINVAL;
154 	}
155 
156 	/* Call the driver initialization routine. */
157 	lid = hid; /* Pass the driver ID. */
158 	err = crypto_drivers[hid].cc_newsession(&lid, cri);
159 	if (err == 0) {
160 		(*sid) = hid;
161 		(*sid) <<= 32;
162 		(*sid) |= (lid & 0xffffffff);
163 		crypto_drivers[hid].cc_sessions++;
164 	}
165 
166 	splx(s);
167 	return err;
168 }
169 
170 /*
171  * Delete an existing session (or a reserved session on an unregistered
172  * driver).
173  */
174 int
175 crypto_freesession(u_int64_t sid)
176 {
177 	int err = 0, s;
178 	u_int32_t hid;
179 
180 	if (crypto_drivers == NULL)
181 		return EINVAL;
182 
183 	/* Determine two IDs. */
184 	hid = (sid >> 32) & 0xffffffff;
185 
186 	if (hid >= crypto_drivers_num)
187 		return ENOENT;
188 
189 	s = splvm();
190 
191 	if (crypto_drivers[hid].cc_sessions)
192 		crypto_drivers[hid].cc_sessions--;
193 
194 	/* Call the driver cleanup routine, if available. */
195 	if (crypto_drivers[hid].cc_freesession)
196 		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 	if ((crypto_drivers[hid].cc_flags & CRYPTOCAP_F_CLEANUP) &&
203 	    crypto_drivers[hid].cc_sessions == 0)
204 		explicit_bzero(&crypto_drivers[hid], sizeof(struct cryptocap));
205 
206 	splx(s);
207 	return err;
208 }
209 
210 /*
211  * Find an empty slot.
212  */
213 int32_t
214 crypto_get_driverid(u_int8_t flags)
215 {
216 	struct cryptocap *newdrv;
217 	int i, s;
218 
219 	s = splvm();
220 
221 	if (crypto_drivers_num == 0) {
222 		crypto_drivers_num = CRYPTO_DRIVERS_INITIAL;
223 		crypto_drivers = mallocarray(crypto_drivers_num,
224 		    sizeof(struct cryptocap), M_CRYPTO_DATA, M_NOWAIT | M_ZERO);
225 		if (crypto_drivers == NULL) {
226 			crypto_drivers_num = 0;
227 			splx(s);
228 			return -1;
229 		}
230 	}
231 
232 	for (i = 0; i < crypto_drivers_num; i++) {
233 		if (crypto_drivers[i].cc_process == NULL &&
234 		    !(crypto_drivers[i].cc_flags & CRYPTOCAP_F_CLEANUP) &&
235 		    crypto_drivers[i].cc_sessions == 0) {
236 			crypto_drivers[i].cc_sessions = 1; /* Mark */
237 			crypto_drivers[i].cc_flags = flags;
238 			splx(s);
239 			return i;
240 		}
241 	}
242 
243 	/* Out of entries, allocate some more. */
244 	if (i == crypto_drivers_num) {
245 		if (crypto_drivers_num >= CRYPTO_DRIVERS_MAX) {
246 			splx(s);
247 			return -1;
248 		}
249 
250 		newdrv = mallocarray(crypto_drivers_num,
251 		    2 * sizeof(struct cryptocap), M_CRYPTO_DATA, M_NOWAIT);
252 		if (newdrv == NULL) {
253 			splx(s);
254 			return -1;
255 		}
256 
257 		bcopy(crypto_drivers, newdrv,
258 		    crypto_drivers_num * sizeof(struct cryptocap));
259 		bzero(&newdrv[crypto_drivers_num],
260 		    crypto_drivers_num * sizeof(struct cryptocap));
261 
262 		newdrv[i].cc_sessions = 1; /* Mark */
263 		newdrv[i].cc_flags = flags;
264 
265 		free(crypto_drivers, M_CRYPTO_DATA,
266 		    crypto_drivers_num * sizeof(struct cryptocap));
267 
268 		crypto_drivers_num *= 2;
269 		crypto_drivers = newdrv;
270 		splx(s);
271 		return i;
272 	}
273 
274 	/* Shouldn't really get here... */
275 	splx(s);
276 	return -1;
277 }
278 
279 /*
280  * Register a crypto driver. It should be called once for each algorithm
281  * supported by the driver.
282  */
283 int
284 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 	if (driverid >= crypto_drivers_num || alg == NULL ||
292 	    crypto_drivers == NULL)
293 		return EINVAL;
294 
295 	s = splvm();
296 
297 	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 		crypto_drivers[driverid].cc_alg[i] = alg[i];
305 	}
306 
307 
308 	crypto_drivers[driverid].cc_newsession = newses;
309 	crypto_drivers[driverid].cc_process = process;
310 	crypto_drivers[driverid].cc_freesession = freeses;
311 	crypto_drivers[driverid].cc_sessions = 0; /* Unmark */
312 
313 	splx(s);
314 
315 	return 0;
316 }
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 crypto_unregister(u_int32_t driverid, int alg)
326 {
327 	int i = CRYPTO_ALGORITHM_MAX + 1, s;
328 	u_int32_t ses;
329 
330 	s = splvm();
331 
332 	/* Sanity checks. */
333 	if (driverid >= crypto_drivers_num || crypto_drivers == NULL ||
334 	    ((alg <= 0 || alg > CRYPTO_ALGORITHM_MAX) &&
335 		alg != CRYPTO_ALGORITHM_MAX + 1) ||
336 	    crypto_drivers[driverid].cc_alg[alg] == 0) {
337 		splx(s);
338 		return EINVAL;
339 	}
340 
341 	if (alg != CRYPTO_ALGORITHM_MAX + 1) {
342 		crypto_drivers[driverid].cc_alg[alg] = 0;
343 
344 		/* Was this the last algorithm ? */
345 		for (i = 1; i <= CRYPTO_ALGORITHM_MAX; i++)
346 			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 	if (i == CRYPTO_ALGORITHM_MAX + 1 || alg == CRYPTO_ALGORITHM_MAX + 1) {
355 		ses = crypto_drivers[driverid].cc_sessions;
356 		bzero(&crypto_drivers[driverid], sizeof(struct cryptocap));
357 		if (ses != 0) {
358 			/*
359 			 * If there are pending sessions, just mark as invalid.
360 			 */
361 			crypto_drivers[driverid].cc_flags |= CRYPTOCAP_F_CLEANUP;
362 			crypto_drivers[driverid].cc_sessions = ses;
363 		}
364 	}
365 	splx(s);
366 	return 0;
367 }
368 
369 /*
370  * Add crypto request to a queue, to be processed by a kernel thread.
371  */
372 int
373 crypto_dispatch(struct cryptop *crp)
374 {
375 	struct taskq *tq = crypto_taskq;
376 	int s;
377 	u_int32_t hid;
378 
379 	s = splvm();
380 	hid = (crp->crp_sid >> 32) & 0xffffffff;
381 	if (hid < crypto_drivers_num) {
382 		if (crypto_drivers[hid].cc_flags & CRYPTOCAP_F_MPSAFE)
383 			tq = crypto_taskq_mpsafe;
384 	}
385 	splx(s);
386 
387 	if (tq && !(crp->crp_flags & CRYPTO_F_NOQUEUE)) {
388 		task_set(&crp->crp_task, (void (*))crypto_invoke, crp);
389 		task_add(tq, &crp->crp_task);
390 	} else {
391 		crypto_invoke(crp);
392 	}
393 
394 	return 0;
395 }
396 
397 /*
398  * Dispatch a crypto request to the appropriate crypto devices.
399  */
400 int
401 crypto_invoke(struct cryptop *crp)
402 {
403 	struct cryptodesc *crd;
404 	u_int64_t nid;
405 	u_int32_t hid;
406 	int error;
407 	int s;
408 
409 	/* Sanity checks. */
410 	if (crp == NULL || crp->crp_callback == NULL)
411 		return EINVAL;
412 
413 	s = splvm();
414 	if (crp->crp_desc == NULL || crypto_drivers == NULL) {
415 		crp->crp_etype = EINVAL;
416 		crypto_done(crp);
417 		splx(s);
418 		return 0;
419 	}
420 
421 	hid = (crp->crp_sid >> 32) & 0xffffffff;
422 	if (hid >= crypto_drivers_num)
423 		goto migrate;
424 
425 	if (crypto_drivers[hid].cc_flags & CRYPTOCAP_F_CLEANUP) {
426 		crypto_freesession(crp->crp_sid);
427 		goto migrate;
428 	}
429 
430 	if (crypto_drivers[hid].cc_process == NULL)
431 		goto migrate;
432 
433 	crypto_drivers[hid].cc_operations++;
434 	crypto_drivers[hid].cc_bytes += crp->crp_ilen;
435 
436 	error = crypto_drivers[hid].cc_process(crp);
437 	if (error) {
438 		if (error == ERESTART) {
439 			/* Unregister driver and migrate session. */
440 			crypto_unregister(hid, CRYPTO_ALGORITHM_MAX + 1);
441 			goto migrate;
442 		} else {
443 			crp->crp_etype = error;
444 		}
445 	}
446 
447 	splx(s);
448 	return 0;
449 
450  migrate:
451 	/* Migrate session. */
452 	for (crd = crp->crp_desc; crd->crd_next; crd = crd->crd_next)
453 		crd->CRD_INI.cri_next = &(crd->crd_next->CRD_INI);
454 
455 	if (crypto_newsession(&nid, &(crp->crp_desc->CRD_INI), 0) == 0)
456 		crp->crp_sid = nid;
457 
458 	crp->crp_etype = EAGAIN;
459 	crypto_done(crp);
460 	splx(s);
461 	return 0;
462 }
463 
464 /*
465  * Release a set of crypto descriptors.
466  */
467 void
468 crypto_freereq(struct cryptop *crp)
469 {
470 	struct cryptodesc *crd;
471 
472 	if (crp == NULL)
473 		return;
474 
475 	while ((crd = crp->crp_desc) != NULL) {
476 		crp->crp_desc = crd->crd_next;
477 		pool_put(&cryptodesc_pool, crd);
478 	}
479 
480 	pool_put(&cryptop_pool, crp);
481 }
482 
483 /*
484  * Acquire a set of crypto descriptors.
485  */
486 struct cryptop *
487 crypto_getreq(int num)
488 {
489 	struct cryptodesc *crd;
490 	struct cryptop *crp;
491 
492 	crp = pool_get(&cryptop_pool, PR_NOWAIT | PR_ZERO);
493 	if (crp == NULL)
494 		return NULL;
495 
496 	while (num--) {
497 		crd = pool_get(&cryptodesc_pool, PR_NOWAIT | PR_ZERO);
498 		if (crd == NULL) {
499 			crypto_freereq(crp);
500 			return NULL;
501 		}
502 
503 		crd->crd_next = crp->crp_desc;
504 		crp->crp_desc = crd;
505 	}
506 
507 	return crp;
508 }
509 
510 void
511 crypto_init(void)
512 {
513 	crypto_taskq = taskq_create("crypto", 1, IPL_VM, 0);
514 	crypto_taskq_mpsafe = taskq_create("crynlk", 1, IPL_VM|IPL_MPSAFE, 0);
515 
516 	pool_init(&cryptop_pool, sizeof(struct cryptop), 0, 0,
517 	    0, "cryptop", NULL);
518 	pool_setipl(&cryptop_pool, IPL_VM);
519 	pool_init(&cryptodesc_pool, sizeof(struct cryptodesc), 0, 0,
520 	    0, "cryptodesc", NULL);
521 	pool_setipl(&cryptodesc_pool, IPL_VM);
522 }
523 
524 /*
525  * Invoke the callback on behalf of the driver.
526  */
527 void
528 crypto_done(struct cryptop *crp)
529 {
530 	crp->crp_flags |= CRYPTO_F_DONE;
531 	if (crp->crp_flags & CRYPTO_F_NOQUEUE) {
532 		/* not from the crypto queue, wakeup the userland process */
533 		crp->crp_callback(crp);
534 	} else {
535 		task_set(&crp->crp_task, (void (*))crp->crp_callback, crp);
536 		task_add(crypto_taskq, &crp->crp_task);
537 	}
538 }
539