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