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