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.
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.
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]
22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 #pragma ident "%Z%%M% %I% %E% SMI"
29 * Mechanism Manager - centralized knowledge of mechanisms.
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.
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.
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
47 #include "pkcs11Conf.h"
48 #include "metaGlobal.h"
53 #define INITIAL_MECHLIST_SIZE 256
55 typedef struct mechliststruct
{
56 CK_MECHANISM_TYPE type
;
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
;
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,
72 static boolean_t
find_mech_index(CK_MECHANISM_TYPE
, unsigned long *);
73 static int qsort_mechtypes(const void *, const void *);
77 * meta_mechManager_initialize
79 * Called from C_Initialize. Allocates and initializes storage needed
80 * by the slot manager.
83 meta_mechManager_initialize()
85 /* The mechlist can dynamically grow, but let's preallocate space. */
86 mechlist
= calloc(INITIAL_MECHLIST_SIZE
, sizeof (mechlist_t
));
88 return (CKR_HOST_MEMORY
);
90 true_mechlist_size
= INITIAL_MECHLIST_SIZE
;
98 * meta_mechManager_finalize
100 * Called from C_Finalize. Deallocates any storage held by the slot manager.
103 meta_mechManager_finalize()
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
);
115 true_mechlist_size
= 0;
120 * meta_mechManager_get_mechs
122 * Get list of all available mechanisms.
124 * Follows PKCS#11 semantics, where list may be NULL to only request a
125 * count of available mechanisms.
128 meta_mechManager_get_mechs(CK_MECHANISM_TYPE
*list
, CK_ULONG
*listsize
)
131 CK_ULONG num_found
= 0;
132 CK_ULONG slotnum
, num_slots
;
135 /* get number of slots */
136 num_slots
= meta_slotManager_get_slotcount();
139 * Update slot info. Ignore any errors.
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).
147 for (slotnum
= 0; slotnum
< num_slots
; slotnum
++) {
148 (void) meta_mechManager_update_slot(slotnum
);
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.
158 (void) pthread_rwlock_rdlock(&mechlist_lock
);
159 for (i
= 0; i
< num_mechs
; i
++) {
163 if (pkcs11_is_dismech(METASLOT_FRAMEWORK_ID
,
165 /* skip mechs disabled by policy */
170 for (j
= 0; j
< num_slots
; j
++) {
171 if (!mechlist
[i
].slots
[j
].initialized
)
174 if (mechlist
[i
].slots
[j
].supported
) {
183 if (list
&& *listsize
>= num_found
) {
184 list
[num_found
- 1] = mechlist
[i
].type
;
188 (void) pthread_rwlock_unlock(&mechlist_lock
);
190 if (num_found
> *listsize
)
191 rv
= CKR_BUFFER_TOO_SMALL
;
193 *listsize
= num_found
;
200 * meta_mechManager_get_slots
202 * Get list of all slots supporting the specified mechanism.
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.
210 * If any error occurred in getting the list, info in
211 * mech_support_info argument is not updated.
215 meta_mechManager_get_slots(mech_support_info_t
*mech_support_info
,
216 boolean_t force_update
, CK_MECHANISM_INFO
*mech_info
)
220 CK_ULONG i
, num_slots
;
221 unsigned long index
, num_found
= 0;
222 CK_MECHANISM_INFO info
;
224 rv
= meta_mechManager_update_mech(mech_support_info
->mech
,
230 (void) pthread_rwlock_rdlock(&mechlist_lock
);
232 found
= find_mech_index(mech_support_info
->mech
, &index
);
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
)
244 info
= mechlist
[index
].slots
[i
].mechanism_info
;
245 if (!(info
.flags
& mech_info
->flags
)) {
251 (mech_support_info
->supporting_slots
)[num_found
- 1]
252 = &mechlist
[index
].slots
[i
];
256 (void) pthread_rwlock_unlock(&mechlist_lock
);
258 if (num_found
== 0) {
259 rv
= CKR_MECHANISM_INVALID
;
261 mech_support_info
->num_supporting_slots
= num_found
;
269 * meta_mechManager_update_mech
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.
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.
282 meta_mechManager_update_mech(CK_MECHANISM_TYPE mech
, boolean_t force_refresh
)
285 CK_ULONG slot
, num_slots
;
286 unsigned long index
= 0;
289 /* Ensure list contains the mechanism. */
290 rv
= meta_mechManager_allocmechs(&mech
, 1, &index
);
294 (void) pthread_rwlock_wrlock(&mechlist_lock
);
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.
300 found
= find_mech_index(mech
, &index
);
302 /* Shouldn't happen - entries are not removed from list. */
303 rv
= CKR_GENERAL_ERROR
;
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
);
312 /* Ignore error and continue with next slot. */
319 (void) pthread_rwlock_unlock(&mechlist_lock
);
326 * meta_mechManager_update_slot
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
335 meta_mechManager_update_slot(CK_ULONG slotnum
)
337 unsigned long index
= 0;
338 CK_MECHANISM_TYPE
*slot_mechlist
= NULL
, *tmp_slot_mechlist
= NULL
;
339 CK_ULONG slot_mechlistsize
, mechnum
, tmp_mechlistsize
;
342 CK_SLOT_ID fw_st_id
, true_id
;
345 fw_st_id
= meta_slotManager_get_framework_table_id(slotnum
);
346 true_id
= TRUEID(fw_st_id
);
348 /* First, get the count. */
349 rv
= FUNCLIST(fw_st_id
)->C_GetMechanismList(true_id
, NULL
,
355 tmp_slot_mechlist
= malloc(
356 slot_mechlistsize
* sizeof (CK_MECHANISM_TYPE
));
357 if (tmp_slot_mechlist
== NULL
) {
358 rv
= CKR_HOST_MEMORY
;
362 /* Next, get the actual list. */
363 rv
= FUNCLIST(fw_st_id
)->C_GetMechanismList(true_id
,
364 tmp_slot_mechlist
, &slot_mechlistsize
);
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.
374 slot_mechlist
= malloc(slot_mechlistsize
* sizeof (CK_MECHANISM_TYPE
));
375 if (slot_mechlist
== NULL
) {
376 rv
= CKR_HOST_MEMORY
;
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
])) {
387 slot_mechlist
[tmp_mechlistsize
] = tmp_slot_mechlist
[i
];
390 slot_mechlistsize
= tmp_mechlistsize
;
392 /* Sort the mechanisms by value. */
393 qsort(slot_mechlist
, slot_mechlistsize
, sizeof (CK_MECHANISM_TYPE
),
396 /* Ensure list contains the mechanisms. */
397 rv
= meta_mechManager_allocmechs(slot_mechlist
, slot_mechlistsize
,
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
);
407 /* This shouldn't happen. */
408 rv
= CKR_GENERAL_ERROR
;
412 rv
= update_slotmech(slot_mechlist
[mechnum
], slotnum
, index
);
414 /* Ignore error, make best effort to finish update. */
419 (void) pthread_rwlock_unlock(&mechlist_lock
);
426 if (tmp_slot_mechlist
) {
427 free(tmp_slot_mechlist
);
437 * Updates the information for a particular mechanism for a particular slot.
438 * (ie, slotlist[foo].slots[bar])
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".
445 update_slotmech(CK_MECHANISM_TYPE mech
, CK_ULONG slotnum
,
449 CK_MECHANISM_INFO info
;
450 CK_SLOT_ID fw_st_id
, true_id
;
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
);
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.
461 if (pkcs11_is_dismech(fw_st_id
, mech
)) {
463 * we mark this as initialized so that we won't try
464 * to do this check later
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
));
473 rv
= FUNCLIST(fw_st_id
)->C_GetMechanismInfo(true_id
, mech
, &info
);
475 mechlist
[index
].slots
[slotnum
].initialized
= B_TRUE
;
476 mechlist
[index
].slots
[slotnum
].supported
= B_TRUE
;
477 mechlist
[index
].slots
[slotnum
].mechanism_info
= info
;
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
));
492 * meta_mechManager_allocmechs
494 * Ensures that all of the specified mechanisms are present in the
495 * mechlist. If a mechanism is not present, an uninitialized entry is
498 * The returned index can be used by the caller as a hint to where the
499 * first mechanism was located.
502 meta_mechManager_allocmechs(CK_MECHANISM_TYPE
*new_mechs
,
503 unsigned long num_new_mechs
, unsigned long *index_hint
)
506 unsigned long i
, index
= 0;
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
);
520 (void) pthread_rwlock_unlock(&mechlist_lock
);
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.
532 (void) pthread_rwlock_wrlock(&mechlist_lock
);
533 for (; i
< num_new_mechs
; i
++) {
534 found
= find_mech_index(new_mechs
[i
], &index
);
537 mechinfo_t
*new_mechinfos
;
539 new_mechinfos
= calloc(meta_slotManager_get_slotcount(),
540 sizeof (mechinfo_t
));
541 if (new_mechinfos
== NULL
) {
542 rv
= CKR_HOST_MEMORY
;
547 * If the current storage for the mechlist is too
548 * small, allocate a new list twice as large.
550 if (num_mechs
== true_mechlist_size
) {
551 mechlist_t
*newmechlist
;
553 newmechlist
= realloc(mechlist
,
554 2 * true_mechlist_size
*
555 sizeof (mechlist_t
));
557 if (newmechlist
== NULL
) {
558 rv
= CKR_HOST_MEMORY
;
563 mechlist
= newmechlist
;
564 true_mechlist_size
*= 2;
567 /* Shift existing entries to make space. */
568 (void) memmove(&mechlist
[index
+1], &mechlist
[index
],
569 (num_mechs
- index
) * sizeof (mechlist_t
));
572 mechlist
[index
].type
= new_mechs
[i
];
573 mechlist
[index
].slots
= new_mechinfos
;
578 (void) pthread_rwlock_unlock(&mechlist_lock
);
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
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.
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).
599 * FUTURE: convert to binary search [from O(N) to a O(log(N))].
602 * 1) This function assumes that mechMap is a sorted list.
605 find_mech_index(CK_MECHANISM_TYPE mechanism
, unsigned long *index
)
607 boolean_t found
= B_FALSE
;
610 for (i
= 0; i
< num_mechs
; i
++) {
612 if (mechlist
[i
].type
== mechanism
) {
617 if (mechlist
[i
].type
> mechanism
)
627 qsort_mechtypes(const void *arg1
, const void *arg2
)
629 CK_MECHANISM_TYPE mech1
= *((CK_MECHANISM_TYPE
*)arg1
);
630 CK_MECHANISM_TYPE mech2
= *((CK_MECHANISM_TYPE
*)arg2
);
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
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
)
654 CK_MECHANISM_INFO info
;
658 rv
= meta_mechManager_update_mech(mechanism
, force_update
);
662 (void) pthread_rwlock_rdlock(&mechlist_lock
);
664 found
= find_mech_index(mechanism
, &index
);
669 if ((mechlist
[index
].slots
[slotnum
].initialized
) &&
670 (mechlist
[index
].slots
[slotnum
].supported
)) {
672 info
= mechlist
[index
].slots
[slotnum
].mechanism_info
;
673 if (!(info
.flags
& mech_info
->flags
)) {
679 *slot_info
= &(mechlist
[index
].slots
[slotnum
]);
684 (void) pthread_rwlock_unlock(&mechlist_lock
);