1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * Mechanism Manager - centralized knowledge of mechanisms. 31 * 32 * The core of the mechmanager is the "mechlist" data structure. It contains 33 * information about all mechanisms available from providers that have been 34 * exposed to the application. 35 * 36 * Each element in the array represents a particular mechanism type. The 37 * array is sorted by type, so that searching by mechanism can be done 38 * quickly. Each element also contains the mechanism data for each slot. 39 * 40 * The mechlist is constructed on an as-needed basis, entries are not added 41 * until the application triggers an action that requires an entry to be 42 * added (or updated). 43 * 44 */ 45 46 #include <string.h> 47 #include <strings.h> 48 #include "pkcs11Conf.h" 49 #include "metaGlobal.h" 50 51 52 /* Global data... */ 53 54 #define INITIAL_MECHLIST_SIZE 256 55 56 typedef struct mechliststruct { 57 CK_MECHANISM_TYPE type; 58 mechinfo_t *slots; 59 } mechlist_t; 60 61 static pthread_rwlock_t mechlist_lock = PTHREAD_RWLOCK_INITIALIZER; 62 static mechlist_t *mechlist; 63 static unsigned long num_mechs; 64 static unsigned long true_mechlist_size; 65 66 67 /* Prototypes... */ 68 static CK_RV meta_mechManager_update_mech(CK_MECHANISM_TYPE, boolean_t); 69 static CK_RV meta_mechManager_update_slot(CK_ULONG); 70 static CK_RV update_slotmech(CK_MECHANISM_TYPE, CK_ULONG, unsigned long); 71 static CK_RV meta_mechManager_allocmechs(CK_MECHANISM_TYPE *, unsigned long, 72 unsigned long *); 73 static boolean_t find_mech_index(CK_MECHANISM_TYPE, unsigned long *); 74 static int qsort_mechtypes(const void *, const void *); 75 76 77 /* 78 * meta_mechManager_initialize 79 * 80 * Called from C_Initialize. Allocates and initializes storage needed 81 * by the slot manager. 82 */ 83 CK_RV 84 meta_mechManager_initialize() 85 { 86 /* The mechlist can dynamically grow, but let's preallocate space. */ 87 mechlist = calloc(INITIAL_MECHLIST_SIZE, sizeof (mechlist_t)); 88 if (mechlist == NULL) 89 return (CKR_HOST_MEMORY); 90 91 true_mechlist_size = INITIAL_MECHLIST_SIZE; 92 num_mechs = 0; 93 94 return (CKR_OK); 95 } 96 97 98 /* 99 * meta_mechManager_finalize 100 * 101 * Called from C_Finalize. Deallocates any storage held by the slot manager. 102 */ 103 void 104 meta_mechManager_finalize() 105 { 106 int i; 107 108 /* No need to lock list, we assume all sessions are closed. */ 109 for (i = 0; i < num_mechs; i++) { 110 free(mechlist[i].slots); 111 } 112 113 free(mechlist); 114 mechlist = NULL; 115 num_mechs = 0; 116 true_mechlist_size = 0; 117 } 118 119 120 /* 121 * meta_mechManager_get_mechs 122 * 123 * Get list of all available mechanisms. 124 * 125 * Follows PKCS#11 semantics, where list may be NULL to only request a 126 * count of available mechanisms. 127 */ 128 CK_RV 129 meta_mechManager_get_mechs(CK_MECHANISM_TYPE *list, CK_ULONG *listsize) 130 { 131 CK_RV rv = CKR_OK; 132 CK_ULONG num_found = 0; 133 CK_ULONG slotnum, num_slots; 134 unsigned long i; 135 136 /* get number of slots */ 137 num_slots = meta_slotManager_get_slotcount(); 138 139 /* 140 * Update slot info. Ignore any errors. 141 * 142 * NOTE: Due to the PKCS#11 convention of calling C_GetMechanismList 143 * twice (once to get the count, again to get the actual list), this 144 * is somewhat inefficient... However, I don't see an easy way to fix 145 * that without impacting other cases (eg, when the first call contains 146 * an "optimistic" pre-allocated buffer). 147 */ 148 for (slotnum = 0; slotnum < num_slots; slotnum++) { 149 (void) meta_mechManager_update_slot(slotnum); 150 } 151 152 153 /* 154 * Count the number of mechanisms. We can't just use num_mechs, 155 * because some mechs may not currently be supported on any slot. 156 * Also, it may not be allowed based on the mechanism policy. 157 */ 158 159 (void) pthread_rwlock_rdlock(&mechlist_lock); 160 for (i = 0; i < num_mechs; i++) { 161 CK_ULONG j; 162 boolean_t supported; 163 164 if (pkcs11_is_dismech(METASLOT_FRAMEWORK_ID, 165 mechlist[i].type)) { 166 /* skip mechs disabled by policy */ 167 continue; 168 } 169 170 supported = FALSE; 171 for (j = 0; j < num_slots; j++) { 172 if (!mechlist[i].slots[j].initialized) 173 continue; 174 175 if (mechlist[i].slots[j].supported) { 176 supported = B_TRUE; 177 break; 178 } 179 } 180 181 if (supported) { 182 num_found++; 183 184 if (list && *listsize >= num_found) { 185 list[num_found - 1] = mechlist[i].type; 186 } 187 } 188 } 189 (void) pthread_rwlock_unlock(&mechlist_lock); 190 191 if (num_found > *listsize) 192 rv = CKR_BUFFER_TOO_SMALL; 193 194 *listsize = num_found; 195 196 return (rv); 197 } 198 199 200 /* 201 * meta_mechManager_get_slots 202 * 203 * Get list of all slots supporting the specified mechanism. 204 * 205 * The "mech_support_info" argument should have allocated enough 206 * space to accomodate the list of slots that supports the 207 * specified mechanism. The "num_supporting_slots" field 208 * in the "mech_support_info" structure will indicate how 209 * many slots are found to support the mechanism. 210 * 211 * If any error occurred in getting the list, info in 212 * mech_support_info argument is not updated. 213 * 214 */ 215 CK_RV 216 meta_mechManager_get_slots(mech_support_info_t *mech_support_info, 217 boolean_t force_update) 218 { 219 CK_RV rv; 220 boolean_t found; 221 CK_ULONG i, num_slots; 222 unsigned long index, num_found = 0; 223 224 rv = meta_mechManager_update_mech(mech_support_info->mech, 225 force_update); 226 if (rv != CKR_OK) { 227 return (rv); 228 } 229 230 (void) pthread_rwlock_rdlock(&mechlist_lock); 231 232 found = find_mech_index(mech_support_info->mech, &index); 233 if (!found) { 234 goto finish; 235 } 236 237 num_slots = meta_slotManager_get_slotcount(); 238 for (i = 0; i < num_slots; i++) { 239 if (!mechlist[index].slots[i].initialized || 240 !mechlist[index].slots[i].supported) 241 continue; 242 243 num_found++; 244 (mech_support_info->supporting_slots)[num_found - 1] 245 = &mechlist[index].slots[i]; 246 } 247 248 finish: 249 (void) pthread_rwlock_unlock(&mechlist_lock); 250 251 if (num_found == 0) { 252 rv = CKR_MECHANISM_INVALID; 253 } else { 254 mech_support_info->num_supporting_slots = num_found; 255 } 256 257 return (rv); 258 } 259 260 261 /* 262 * meta_mechManager_update_mech 263 * 264 * Updates a mechanism in the mechlist. If the mechanism is not 265 * listed, all providers will be queried. If the mechanism 266 * is present, but not initialized for some providers, those providers 267 * will be queried. Existing entries will not be updated unless the 268 * force_refresh flag is set. 269 * 270 * The force_refresh flag is used by C_GetMechanismInfo, to force an 271 * update. Updates are not forced during the common usage by operations 272 * [eg C_EncryptInit] to avoid poor performance. 273 */ 274 static CK_RV 275 meta_mechManager_update_mech(CK_MECHANISM_TYPE mech, boolean_t force_refresh) 276 { 277 CK_RV rv; 278 CK_ULONG slot, num_slots; 279 unsigned long index = 0; 280 boolean_t found; 281 282 /* Ensure list contains the mechanism. */ 283 rv = meta_mechManager_allocmechs(&mech, 1, &index); 284 if (rv != CKR_OK) 285 return (rv); 286 287 (void) pthread_rwlock_wrlock(&mechlist_lock); 288 /* 289 * We didn't retain a lock after the first search, so it's possible 290 * that the mechlist was updated. Search again, but use the last 291 * index as a hint to quickly find the mechanism. 292 */ 293 found = find_mech_index(mech, &index); 294 if (!found) { 295 /* Shouldn't happen - entries are not removed from list. */ 296 rv = CKR_GENERAL_ERROR; 297 goto finish; 298 } 299 300 num_slots = meta_slotManager_get_slotcount(); 301 for (slot = 0; slot < num_slots; slot++) { 302 if (force_refresh || !mechlist[index].slots[slot].initialized) { 303 rv = update_slotmech(mech, slot, index); 304 if (rv != CKR_OK) { 305 /* Ignore error and continue with next slot. */ 306 rv = CKR_OK; 307 } 308 } 309 } 310 311 finish: 312 (void) pthread_rwlock_unlock(&mechlist_lock); 313 314 return (rv); 315 } 316 317 318 /* 319 * meta_mechManager_update_slot 320 * 321 * Updates a slot in the mechlist. Called by C_GetMechanismList 322 * [by way of meta_mechManager_get_mechs()]. Unlike 323 * meta_mechManager_get_slots(), the context is always to force a refresh 324 * of the mechlist. 325 * 326 */ 327 static CK_RV 328 meta_mechManager_update_slot(CK_ULONG slotnum) 329 { 330 unsigned long index = 0; 331 CK_MECHANISM_TYPE *slot_mechlist = NULL, *tmp_slot_mechlist = NULL; 332 CK_ULONG slot_mechlistsize, mechnum, tmp_mechlistsize; 333 CK_RV rv; 334 boolean_t found; 335 CK_SLOT_ID fw_st_id, true_id; 336 int i; 337 338 fw_st_id = meta_slotManager_get_framework_table_id(slotnum); 339 true_id = TRUEID(fw_st_id); 340 341 /* First, get the count. */ 342 rv = FUNCLIST(fw_st_id)->C_GetMechanismList(true_id, NULL, 343 &slot_mechlistsize); 344 if (rv != CKR_OK) { 345 goto finish; 346 } 347 348 tmp_slot_mechlist = malloc( 349 slot_mechlistsize * sizeof (CK_MECHANISM_TYPE)); 350 if (tmp_slot_mechlist == NULL) { 351 rv = CKR_HOST_MEMORY; 352 goto finish; 353 } 354 355 /* Next, get the actual list. */ 356 rv = FUNCLIST(fw_st_id)->C_GetMechanismList(true_id, 357 tmp_slot_mechlist, &slot_mechlistsize); 358 if (rv != CKR_OK) { 359 goto finish; 360 } 361 362 /* 363 * filter the list of mechanisms returned by the underlying slot 364 * to remove any mechanisms that are explicitly disabled 365 * in the configuration file. 366 */ 367 slot_mechlist = malloc(slot_mechlistsize * sizeof (CK_MECHANISM_TYPE)); 368 if (slot_mechlist == NULL) { 369 rv = CKR_HOST_MEMORY; 370 goto finish; 371 } 372 373 tmp_mechlistsize = 0; 374 for (i = 0; i < slot_mechlistsize; i++) { 375 /* filter out the disabled mechanisms */ 376 if (pkcs11_is_dismech(fw_st_id, tmp_slot_mechlist[i])) { 377 continue; 378 } 379 380 slot_mechlist[tmp_mechlistsize] = tmp_slot_mechlist[i]; 381 tmp_mechlistsize++; 382 } 383 slot_mechlistsize = tmp_mechlistsize; 384 385 /* Sort the mechanisms by value. */ 386 qsort(slot_mechlist, slot_mechlistsize, sizeof (CK_MECHANISM_TYPE), 387 qsort_mechtypes); 388 389 /* Ensure list contains the mechanisms. */ 390 rv = meta_mechManager_allocmechs(slot_mechlist, slot_mechlistsize, 391 &index); 392 if (rv != CKR_OK) 393 goto finish; 394 395 /* Update the mechanism info. */ 396 (void) pthread_rwlock_wrlock(&mechlist_lock); 397 for (mechnum = 0; mechnum < slot_mechlistsize; mechnum++) { 398 found = find_mech_index(slot_mechlist[mechnum], &index); 399 if (!found) { 400 /* This shouldn't happen. */ 401 rv = CKR_GENERAL_ERROR; 402 goto finish; 403 } 404 405 rv = update_slotmech(slot_mechlist[mechnum], slotnum, index); 406 if (rv != CKR_OK) { 407 /* Ignore error, make best effort to finish update. */ 408 rv = CKR_OK; 409 continue; 410 } 411 } 412 (void) pthread_rwlock_unlock(&mechlist_lock); 413 414 finish: 415 if (slot_mechlist) { 416 free(slot_mechlist); 417 } 418 419 if (tmp_slot_mechlist) { 420 free(tmp_slot_mechlist); 421 } 422 423 return (rv); 424 } 425 426 427 /* 428 * update_slotmech 429 * 430 * Updates the information for a particular mechanism for a particular slot. 431 * (ie, slotlist[foo].slots[bar]) 432 * 433 * It is assumed that the caller to this function (all of which are 434 * in this file) holds the write-lock to "mechlist_lock". 435 * 436 */ 437 static CK_RV 438 update_slotmech(CK_MECHANISM_TYPE mech, CK_ULONG slotnum, 439 unsigned long index) 440 { 441 CK_RV rv = CKR_OK; 442 CK_MECHANISM_INFO info; 443 CK_SLOT_ID fw_st_id, true_id; 444 445 mechlist[index].slots[slotnum].slotnum = slotnum; 446 fw_st_id = meta_slotManager_get_framework_table_id(slotnum); 447 true_id = TRUEID(fw_st_id); 448 449 /* 450 * Check if the specified mechanism is in the disabled list 451 * of the specified slot. If so, we can immediately conclude 452 * that it is not supported by the specified slot. 453 */ 454 if (pkcs11_is_dismech(fw_st_id, mech)) { 455 /* 456 * we mark this as initialized so that we won't try 457 * to do this check later 458 */ 459 mechlist[index].slots[slotnum].initialized = B_TRUE; 460 mechlist[index].slots[slotnum].supported = B_FALSE; 461 bzero(&mechlist[index].slots[slotnum].mechanism_info, 462 sizeof (CK_MECHANISM_INFO)); 463 goto finish; 464 } 465 466 rv = FUNCLIST(fw_st_id)->C_GetMechanismInfo(true_id, mech, &info); 467 if (rv == CKR_OK) { 468 mechlist[index].slots[slotnum].initialized = B_TRUE; 469 mechlist[index].slots[slotnum].supported = B_TRUE; 470 mechlist[index].slots[slotnum].mechanism_info = info; 471 } else { 472 /* record that the mechanism isn't supported for the slot */ 473 mechlist[index].slots[slotnum].initialized = B_TRUE; 474 mechlist[index].slots[slotnum].supported = B_FALSE; 475 bzero(&mechlist[index].slots[slotnum].mechanism_info, 476 sizeof (CK_MECHANISM_INFO)); 477 } 478 479 finish: 480 return (rv); 481 } 482 483 484 /* 485 * meta_mechManager_allocmechs 486 * 487 * Ensures that all of the specified mechanisms are present in the 488 * mechlist. If a mechanism is not present, an uninitialized entry is 489 * added for it. 490 * 491 * The returned index can be used by the caller as a hint to where the 492 * first mechanism was located. 493 */ 494 static CK_RV 495 meta_mechManager_allocmechs(CK_MECHANISM_TYPE *new_mechs, 496 unsigned long num_new_mechs, unsigned long *index_hint) 497 { 498 CK_RV rv = CKR_OK; 499 unsigned long i, index = 0; 500 boolean_t found; 501 502 /* The optimistic assumption is that the mech is already present. */ 503 (void) pthread_rwlock_rdlock(&mechlist_lock); 504 for (i = 0; i < num_new_mechs; i++) { 505 found = find_mech_index(new_mechs[i], &index); 506 507 if (i == 0) 508 *index_hint = index; 509 510 if (!found) 511 break; 512 } 513 (void) pthread_rwlock_unlock(&mechlist_lock); 514 515 if (found) { 516 return (CKR_OK); 517 } 518 519 /* 520 * We stopped searching when the first unknown mech was found. Now 521 * obtain a write-lock, and continue from where we left off, inserting 522 * unknown mechanisms. 523 */ 524 525 (void) pthread_rwlock_wrlock(&mechlist_lock); 526 for (; i < num_new_mechs; i++) { 527 found = find_mech_index(new_mechs[i], &index); 528 529 if (!found) { 530 mechinfo_t *new_mechinfos; 531 532 new_mechinfos = calloc(meta_slotManager_get_slotcount(), 533 sizeof (mechinfo_t)); 534 if (new_mechinfos == NULL) { 535 rv = CKR_HOST_MEMORY; 536 goto finish; 537 } 538 539 /* 540 * If the current storage for the mechlist is too 541 * small, allocate a new list twice as large. 542 */ 543 if (num_mechs == true_mechlist_size) { 544 mechlist_t *newmechlist; 545 546 newmechlist = realloc(mechlist, 547 2 * true_mechlist_size * 548 sizeof (mechlist_t)); 549 550 if (newmechlist == NULL) { 551 rv = CKR_HOST_MEMORY; 552 free(new_mechinfos); 553 goto finish; 554 } 555 556 mechlist = newmechlist; 557 true_mechlist_size *= 2; 558 } 559 560 /* Shift existing entries to make space. */ 561 (void) memmove(&mechlist[index+1], &mechlist[index], 562 (num_mechs - index) * sizeof (mechlist_t)); 563 num_mechs++; 564 565 mechlist[index].type = new_mechs[i]; 566 mechlist[index].slots = new_mechinfos; 567 } 568 } 569 570 finish: 571 (void) pthread_rwlock_unlock(&mechlist_lock); 572 573 return (rv); 574 } 575 576 577 /* 578 * find_mech_index 579 * 580 * Performs a search of mechlist for the specified mechanism, and 581 * returns if the mechanism was found or not. The value of the "index" 582 * argument will be where the mech is (if found), or where it should 583 * be (if not found). 584 * 585 * The current value of "index" will be used as a starting point, if the 586 * caller already knows where the mechanism is likely to be. 587 * 588 * The caller is assumed to have a lock on the mechlist, preventing it 589 * from being changed while searching (also to ensure the returned index 590 * will remain valid until the list is unlocked). 591 * 592 * FUTURE: convert to binary search [from O(N) to a O(log(N))]. 593 * 594 * NOTES: 595 * 1) This function assumes that mechMap is a sorted list. 596 */ 597 static boolean_t 598 find_mech_index(CK_MECHANISM_TYPE mechanism, unsigned long *index) 599 { 600 boolean_t found = B_FALSE; 601 unsigned long i; 602 603 for (i = 0; i < num_mechs; i++) { 604 605 if (mechlist[i].type == mechanism) { 606 found = B_TRUE; 607 break; 608 } 609 610 if (mechlist[i].type > mechanism) 611 break; 612 } 613 614 *index = i; 615 616 return (found); 617 } 618 619 static int 620 qsort_mechtypes(const void *arg1, const void *arg2) 621 { 622 CK_MECHANISM_TYPE mech1 = *((CK_MECHANISM_TYPE *)arg1); 623 CK_MECHANISM_TYPE mech2 = *((CK_MECHANISM_TYPE *)arg2); 624 625 if (mech1 > mech2) 626 return (1); 627 if (mech1 < mech2) 628 return (-1); 629 return (0); 630 } 631 632 /* 633 * Check if the specified mechanism is supported by the specified slot. 634 * The result is returned in the "supports" argument. If the "slot_info" 635 * argument is not NULL, it will be filled with information about 636 * the slot. 637 */ 638 CK_RV 639 meta_mechManager_slot_supports_mech(CK_MECHANISM_TYPE mechanism, 640 CK_ULONG slotnum, boolean_t *supports, mechinfo_t **slot_info, 641 boolean_t force_update) 642 { 643 644 boolean_t found; 645 CK_RV rv; 646 unsigned long index; 647 648 *supports = B_FALSE; 649 650 rv = meta_mechManager_update_mech(mechanism, force_update); 651 if (rv != CKR_OK) 652 return (rv); 653 654 (void) pthread_rwlock_rdlock(&mechlist_lock); 655 656 found = find_mech_index(mechanism, &index); 657 if (!found) { 658 goto finish; 659 } 660 661 if ((mechlist[index].slots[slotnum].initialized) && 662 (mechlist[index].slots[slotnum].supported)) { 663 *supports = B_TRUE; 664 if (slot_info) { 665 *slot_info = &(mechlist[index].slots[slotnum]); 666 } 667 } 668 669 finish: 670 (void) pthread_rwlock_unlock(&mechlist_lock); 671 672 return (rv); 673 } 674