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