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
meta_mechManager_initialize()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
meta_mechManager_finalize()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
meta_mechManager_get_mechs(CK_MECHANISM_TYPE * list,CK_ULONG * listsize)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
meta_mechManager_get_slots(mech_support_info_t * mech_support_info,boolean_t force_update,CK_MECHANISM_INFO * mech_info)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
meta_mechManager_update_mech(CK_MECHANISM_TYPE mech,boolean_t force_refresh)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
meta_mechManager_update_slot(CK_ULONG slotnum)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
update_slotmech(CK_MECHANISM_TYPE mech,CK_ULONG slotnum,unsigned long index)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
meta_mechManager_allocmechs(CK_MECHANISM_TYPE * new_mechs,unsigned long num_new_mechs,unsigned long * index_hint)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
find_mech_index(CK_MECHANISM_TYPE mechanism,unsigned long * index)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
qsort_mechtypes(const void * arg1,const void * arg2)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
meta_mechManager_slot_supports_mech(CK_MECHANISM_TYPE mechanism,CK_ULONG slotnum,boolean_t * supports,mechinfo_t ** slot_info,boolean_t force_update,CK_MECHANISM_INFO * mech_info)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