1 /* $OpenBSD: crypto.c,v 1.82 2020/03/30 17:48:39 krw 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 memcpy(newdrv, crypto_drivers, 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 + 1)) { 335 splx(s); 336 return EINVAL; 337 } 338 339 if (alg != CRYPTO_ALGORITHM_MAX + 1) { 340 if (crypto_drivers[driverid].cc_alg[alg] == 0) { 341 splx(s); 342 return EINVAL; 343 } 344 crypto_drivers[driverid].cc_alg[alg] = 0; 345 346 /* Was this the last algorithm ? */ 347 for (i = 1; i <= CRYPTO_ALGORITHM_MAX; i++) 348 if (crypto_drivers[driverid].cc_alg[i] != 0) 349 break; 350 } 351 352 /* 353 * If a driver unregistered its last algorithm or all of them 354 * (alg == CRYPTO_ALGORITHM_MAX + 1), cleanup its entry. 355 */ 356 if (i == CRYPTO_ALGORITHM_MAX + 1 || alg == CRYPTO_ALGORITHM_MAX + 1) { 357 ses = crypto_drivers[driverid].cc_sessions; 358 bzero(&crypto_drivers[driverid], sizeof(struct cryptocap)); 359 if (ses != 0) { 360 /* 361 * If there are pending sessions, just mark as invalid. 362 */ 363 crypto_drivers[driverid].cc_flags |= CRYPTOCAP_F_CLEANUP; 364 crypto_drivers[driverid].cc_sessions = ses; 365 } 366 } 367 splx(s); 368 return 0; 369 } 370 371 /* 372 * Add crypto request to a queue, to be processed by a kernel thread. 373 */ 374 int 375 crypto_dispatch(struct cryptop *crp) 376 { 377 struct taskq *tq = crypto_taskq; 378 int s; 379 u_int32_t hid; 380 381 s = splvm(); 382 hid = (crp->crp_sid >> 32) & 0xffffffff; 383 if (hid < crypto_drivers_num) { 384 if (crypto_drivers[hid].cc_flags & CRYPTOCAP_F_MPSAFE) 385 tq = crypto_taskq_mpsafe; 386 } 387 splx(s); 388 389 if (tq && !(crp->crp_flags & CRYPTO_F_NOQUEUE)) { 390 task_set(&crp->crp_task, (void (*))crypto_invoke, crp); 391 task_add(tq, &crp->crp_task); 392 } else { 393 crypto_invoke(crp); 394 } 395 396 return 0; 397 } 398 399 /* 400 * Dispatch a crypto request to the appropriate crypto devices. 401 */ 402 int 403 crypto_invoke(struct cryptop *crp) 404 { 405 u_int64_t nid; 406 u_int32_t hid; 407 int error; 408 int s, i; 409 410 /* Sanity checks. */ 411 if (crp == NULL || crp->crp_callback == NULL) 412 return EINVAL; 413 414 s = splvm(); 415 if (crp->crp_ndesc < 1 || crypto_drivers == NULL) { 416 crp->crp_etype = EINVAL; 417 crypto_done(crp); 418 splx(s); 419 return 0; 420 } 421 422 hid = (crp->crp_sid >> 32) & 0xffffffff; 423 if (hid >= crypto_drivers_num) 424 goto migrate; 425 426 if (crypto_drivers[hid].cc_flags & CRYPTOCAP_F_CLEANUP) { 427 crypto_freesession(crp->crp_sid); 428 goto migrate; 429 } 430 431 if (crypto_drivers[hid].cc_process == NULL) 432 goto migrate; 433 434 crypto_drivers[hid].cc_operations++; 435 crypto_drivers[hid].cc_bytes += crp->crp_ilen; 436 437 error = crypto_drivers[hid].cc_process(crp); 438 if (error) { 439 if (error == ERESTART) { 440 /* Unregister driver and migrate session. */ 441 crypto_unregister(hid, CRYPTO_ALGORITHM_MAX + 1); 442 goto migrate; 443 } else { 444 crp->crp_etype = error; 445 } 446 } 447 448 splx(s); 449 return 0; 450 451 migrate: 452 /* Migrate session. */ 453 for (i = 0; i < crp->crp_ndesc - 1; i++) 454 crp->crp_desc[i].CRD_INI.cri_next = &crp->crp_desc[i+1].CRD_INI; 455 crp->crp_desc[crp->crp_ndesc].CRD_INI.cri_next = NULL; 456 457 if (crypto_newsession(&nid, &(crp->crp_desc->CRD_INI), 0) == 0) 458 crp->crp_sid = nid; 459 460 crp->crp_etype = EAGAIN; 461 crypto_done(crp); 462 splx(s); 463 return 0; 464 } 465 466 /* 467 * Release a set of crypto descriptors. 468 */ 469 void 470 crypto_freereq(struct cryptop *crp) 471 { 472 if (crp == NULL) 473 return; 474 475 if (crp->crp_ndescalloc > 2) 476 free(crp->crp_desc, M_CRYPTO_DATA, 477 crp->crp_ndescalloc * sizeof(struct cryptodesc)); 478 pool_put(&cryptop_pool, crp); 479 } 480 481 /* 482 * Acquire a set of crypto descriptors. 483 */ 484 struct cryptop * 485 crypto_getreq(int num) 486 { 487 struct cryptop *crp; 488 489 crp = pool_get(&cryptop_pool, PR_NOWAIT | PR_ZERO); 490 if (crp == NULL) 491 return NULL; 492 493 crp->crp_desc = crp->crp_sdesc; 494 crp->crp_ndescalloc = crp->crp_ndesc = num; 495 496 if (num > 2) { 497 crp->crp_desc = mallocarray(num, sizeof(struct cryptodesc), 498 M_CRYPTO_DATA, M_NOWAIT | M_ZERO); 499 if (crp->crp_desc == NULL) { 500 pool_put(&cryptop_pool, crp); 501 return NULL; 502 } 503 } 504 505 return crp; 506 } 507 508 void 509 crypto_init(void) 510 { 511 crypto_taskq = taskq_create("crypto", 1, IPL_VM, 0); 512 crypto_taskq_mpsafe = taskq_create("crynlk", 1, IPL_VM, TASKQ_MPSAFE); 513 514 pool_init(&cryptop_pool, sizeof(struct cryptop), 0, IPL_VM, 0, 515 "cryptop", NULL); 516 } 517 518 /* 519 * Invoke the callback on behalf of the driver. 520 */ 521 void 522 crypto_done(struct cryptop *crp) 523 { 524 crp->crp_flags |= CRYPTO_F_DONE; 525 if (crp->crp_flags & CRYPTO_F_NOQUEUE) { 526 /* not from the crypto queue, wakeup the userland process */ 527 crp->crp_callback(crp); 528 } else { 529 task_set(&crp->crp_task, (void (*))crp->crp_callback, crp); 530 task_add(crypto_taskq, &crp->crp_task); 531 } 532 } 533