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