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