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