/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
*/
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <time.h>
#include <security/cryptoki.h>
#include "pkcs11Global.h"
#include "pkcs11Conf.h"
#include "pkcs11Slot.h"
#include "metaGlobal.h"
static void *listener_waitforslotevent(void *arg);
static void *child_waitforslotevent(void *arg);
/*
* C_GetSlotList is implemented entirely within this framework,
* using the slottable that was created during the call to
* C_Initialize in pkcs11_slot_mapping(). The plugged in providers
* are only queried when tokenPresent is set.
*
* If metaslot is enabled, the slot that provides keystore support
* needs to be hidden. Therefore, even when fastpath is enabled,
* we can't go through fastpath because the slot needs to be
* hidden.
*/
{
/* Check for a fastpath */
pulCount));
}
if (!pkcs11_initialized) {
return (CKR_CRYPTOKI_NOT_INITIALIZED);
}
return (CKR_ARGUMENTS_BAD);
}
if (tokenPresent) {
/* Need to allocate memory for pinfo */
return (CKR_HOST_MEMORY);
}
}
/*
* Count the number of valid slots for returning to the application.
* If metaslot is enabled, the slot providing keystore support for
* metaslot is skipped.
*/
if ((pkcs11_is_valid_slot(i) == CKR_OK) &&
((!metaslot_enabled) || (i != metaslot_keystore_slotid))) {
/* Check if token present is required */
if (tokenPresent) {
/* Check with provider */
continue;
}
}
/* Fill in the given buffer if it is sufficient */
}
count++;
}
}
/* pSlotList set to NULL means caller only wants count */
} else {
}
if (tokenPresent) {
}
return (rv);
}
{
if (!pkcs11_initialized) {
return (CKR_CRYPTOKI_NOT_INITIALIZED);
}
/* Check for a fastpath */
if (slotID == METASLOT_FRAMEWORK_ID) {
/* just need to get metaslot information */
}
/* Check that slotID is not metaslot's keystore */
if (slotID == metaslot_keystore_slotid) {
return (CKR_SLOT_ID_INVALID);
}
/* Present consistent interface to the application */
if (rv == CKR_FUNCTION_NOT_SUPPORTED) {
return (CKR_FUNCTION_FAILED);
}
return (rv);
}
{
if (!pkcs11_initialized) {
return (CKR_CRYPTOKI_NOT_INITIALIZED);
}
/* Check for a fastpath */
if (slotID == METASLOT_FRAMEWORK_ID) {
/* just need to get metaslot information */
}
/* Check that slotID is not metaslot's keystore */
if (slotID == metaslot_keystore_slotid) {
return (CKR_SLOT_ID_INVALID);
}
/* Present consistent interface to the application */
if (rv == CKR_FUNCTION_NOT_SUPPORTED) {
return (CKR_FUNCTION_FAILED);
}
return (rv);
}
/*
* C_WaitForSlotEvent cannot be a direct pass through to the underlying
* provider (except in the case of fastpath), due to the complex nature
* of this function. The calling application is asking to be alerted
* when an event has occurred on any of the slots in the framework, so
* we need to check with all underlying providers and ask for events
* on any of their slots. If this is called in blocking mode, we will
* need to start threads to wait for slot events for each provider
* plugged into the framework.
*/
{
CK_SLOT_ID i, j;
/* Check for a fastpath */
if (purefastpath || policyfastpath) {
pReserved));
}
if (!pkcs11_initialized) {
return (CKR_CRYPTOKI_NOT_INITIALIZED);
}
return (CKR_ARGUMENTS_BAD);
}
/*
* Check to see if we're already blocking on another threads
* call to this function. If so, behaviour is undefined so
* we should return to application.
*/
return (CKR_FUNCTION_FAILED);
} else {
}
/*
* Check first to see if any events have been recorded
* already on any of the slots, regardless of blocking or
* thread status.
*/
/* found one, clear event and notify application */
*pSlot = i;
/*
* This event has been captured, clear the function's
* active status. Other threads may now enter this
* function.
*/
return (CKR_OK);
}
}
/*
* We could not find any existing event, so let's see
* if we can block and start threads to watch for events.
*/
if (flags & CKF_DONT_BLOCK) {
/*
* Application does not want us to block so check with
* underlying providers to see if any events have occurred.
* Not every provider will have implemented this function,
* so error codes or CKR_NO_EVENT can be ignored.
*/
/*
* Only do process once per provider.
*/
if (prov_id == last_prov_id) {
continue;
}
/*
* Check to make sure a child thread is not already
* running, due to another of the application's
* thread calling this function.
*/
(void) pthread_mutex_unlock(
continue;
}
/*
* Release the hold on the slot's mutex while we
* are waiting for this function to complete.
*/
/* See if we've found a slot with an event */
/*
* Try to map the returned slotid to a slot
* allocated by the framework. All slots from
* one provider are adjacent in the framework's
* slottable, so search for a mapping while
* the prov_id field is the same.
*/
j = i;
while (prov_id ==
/* Find the slot, remap pSlot */
*pSlot = j;
(void) pthread_mutex_lock(
(void) pthread_mutex_unlock(
return (CKR_OK);
}
j++;
}
}
/*
* If we reach this part of the loop, this
* provider either had no events, did not support
* this function, or set pSlot to a value we
* could not find in the slots associated with
* this provider. Continue checking with remaining
* providers.
*/
}
/* No provider had any events */
return (CKR_NO_EVENT);
/*
* Application has asked us to block, but forbidden
* us from creating threads. This is too risky to perform
* with underlying providers (we may block indefinitely),
* so will return an error in this case.
*/
return (CKR_FUNCTION_FAILED);
}
/*
* Grab the st_start_mutex now, which will prevent the listener
* thread from signaling on st_start_cond before we're ready to
* wait for it.
*/
/*
* Application allows us to create threads and has
* asked us to block. Create listener thread to wait for
* child threads to return.
*/
listener_waitforslotevent, NULL) != 0) {
return (CKR_FUNCTION_FAILED);
}
/*
* Wait for the listening thread to get started before
* we spawn child threads.
*/
/*
* Need to hold the mutex on the entire slottable for the
* entire setup of the child threads. Otherwise, the first
* child thread may complete before a later child thread is
* fully started, resulting in an inaccurate value of
* st_thr_count and a potential race condition.
*/
/*
* Create child threads to check with the plugged in providers
* to check for events. Keep a count of the current open threads,
* so the listener thread knows when there are no more children
* to listen for. Also, make sure a thread is not already active
* for that provider.
*/
/*
* Only do process once per provider.
*/
if (prov_id == last_prov_id) {
continue;
}
/*
* Check to make sure a child thread is not already running,
* due to another of the application's threads calling
* this function. Also, check that the provider has actually
* implemented this function.
*/
(cur_slot->sl_no_wfse)) {
continue;
}
/* Set slot to active */
/*
* set up variable to pass arguments to child threads.
* Only need to set up once, as values will remain the
* same for each successive call.
*/
(void) pthread_mutex_unlock(
(void) pthread_mutex_unlock(
return (CKR_HOST_MEMORY);
}
}
/* Create child thread */
(void *)cur_slot->sl_wfse_args) != 0) {
continue;
}
/*
* This counter is decremented every time a
* child_waitforslotevent() wakes up the listener.
*/
}
/* If no children are listening, kill the listener */
if (slottable->st_thr_count == 0) {
/* If there are no child threads, no event will occur */
return (CKR_NO_EVENT);
}
/* Wait for listener thread to terminate */
/* Make sure C_Finalize has not been called */
if (!pkcs11_initialized) {
return (CKR_CRYPTOKI_NOT_INITIALIZED);
}
/* See if any events actually occurred */
(void) pthread_mutex_lock(&slottable->
sl_wfse_state == WFSE_EVENT) {
/* An event has occurred on this slot */
(void) pthread_mutex_unlock(&slottable->
*pSlot = event_slot;
return (CKR_OK);
} else {
(void) pthread_mutex_unlock(&slottable->
}
}
/* No provider reported any events, or no provider implemented this */
return (CKR_NO_EVENT);
}
/*
* C_GetMechanismList cannot just be a direct pass through to the
* underlying provider, because we allow the administrator to
* disable certain mechanisms from specific providers. This affects
* both pulCount and pMechanismList. Only when the fastpath with
* no policy is in effect can we pass through directly to the
* underlying provider.
*
* It is necessary, for policy filtering, to get the actual list
* of mechanisms from the underlying provider, even if the calling
* application is just requesting a count. It is the only way to
* get an accurate count of the number of mechanisms actually available.
*/
{
CK_ULONG i;
if (!pkcs11_initialized) {
return (CKR_CRYPTOKI_NOT_INITIALIZED);
}
/* Check for a fastpath */
if (slotID == METASLOT_FRAMEWORK_ID) {
pulCount));
}
/* Check that slotID is not metaslot's keystore */
if (slotID == metaslot_keystore_slotid) {
return (CKR_SLOT_ID_INVALID);
}
if (policyfastpath) {
} else {
}
mech_count = 0;
/*
* Allocate memory for a mechanism list. We are assuming
* that most mechanism lists will be less than MECHLIST_SIZE.
* If that is not enough memory, we will try a second time
* with more memory allocated.
*/
if (pmech_list == NULL) {
return (CKR_HOST_MEMORY);
}
/*
* Attempt to get the mechanism list. PKCS11 supports
* removable media, so the mechanism list of a slot can vary
* over the life of the application.
*/
if (rv == CKR_BUFFER_TOO_SMALL) {
/* Need to use more space */
if (pmech_list == NULL) {
return (CKR_HOST_MEMORY);
}
/* Try again to get mechanism list. */
}
/*
* Present consistent face to calling application.
* If something strange has happened, or this function
* is not supported by this provider, return a count
* of zero mechanisms.
*/
*pulCount = 0;
return (CKR_OK);
}
/*
* Process the mechanism list, removing any mechanisms
* that are disabled via the framework. Even if the
* application is only asking for a count, we must
* process the actual mechanisms being offered by this slot.
* We could not just subtract our stored count of disabled
* mechanisms, since it is not guaranteed that those
* mechanisms are actually supported by the slot.
*/
for (i = 0; i < tmpmech_count; i++) {
/* Filter out the disabled mechanisms */
continue;
}
/*
* Only set pMechanismList if enough memory
* is available. If it was set to NULL
* originally, this loop will just be counting
* mechanisms.
*/
}
mech_count++;
}
/*
* Catch the case where pMechanismList was not set to NULL,
* yet the buffer was not large enough. If pMechanismList is
* set to NULL, this function will simply set pulCount and
* return CKR_OK.
*/
*pulCount = mech_count;
return (CKR_BUFFER_TOO_SMALL);
}
*pulCount = mech_count;
return (CKR_OK);
}
{
if (!pkcs11_initialized) {
return (CKR_CRYPTOKI_NOT_INITIALIZED);
}
/* Check for a fastpath */
if (slotID == METASLOT_FRAMEWORK_ID) {
/* just need to get metaslot information */
}
/* Check that slotID is not metaslot's keystore */
if (slotID == metaslot_keystore_slotid) {
return (CKR_SLOT_ID_INVALID);
}
if (policyfastpath) {
} else {
}
/* Make sure this is not a disabled mechanism */
return (CKR_MECHANISM_INVALID);
}
/* Present consistent interface to the application */
if (rv == CKR_FUNCTION_NOT_SUPPORTED) {
return (CKR_FUNCTION_FAILED);
}
return (rv);
}
{
if (!pkcs11_initialized) {
return (CKR_CRYPTOKI_NOT_INITIALIZED);
}
/* Check for a fastpath */
pLabel));
if (slotID == METASLOT_FRAMEWORK_ID) {
/* just need to get metaslot information */
pLabel));
}
/* Check that slotID is not metaslot's keystore */
if (slotID == metaslot_keystore_slotid) {
return (CKR_SLOT_ID_INVALID);
}
pLabel);
/* Present consistent interface to the application */
if (rv == CKR_FUNCTION_NOT_SUPPORTED) {
return (CKR_FUNCTION_FAILED);
}
return (rv);
}
{
/* Check for a fastpath */
if (purefastpath || policyfastpath) {
}
if (!pkcs11_initialized) {
return (CKR_CRYPTOKI_NOT_INITIALIZED);
}
/* Obtain the session pointer */
return (rv);
}
/* Initialize the PIN with the provider */
/* Present consistent interface to the application */
if (rv == CKR_FUNCTION_NOT_SUPPORTED) {
return (CKR_FUNCTION_FAILED);
}
return (rv);
}
{
/* Check for a fastpath */
if (purefastpath || policyfastpath) {
pNewPin, ulNewPinLen));
}
if (!pkcs11_initialized) {
return (CKR_CRYPTOKI_NOT_INITIALIZED);
}
/* Obtain the session pointer */
return (rv);
}
/* Set the PIN with the provider */
/* Present consistent interface to the application */
if (rv == CKR_FUNCTION_NOT_SUPPORTED) {
return (CKR_FUNCTION_FAILED);
}
return (rv);
}
/*
* listener_waitforslotevent is spawned by the main C_WaitForSlotEvent()
* to listen for events from any of the providers. It also watches the
* count of threads, which may go to zero with no recorded events, if
* none of the underlying providers have actually implemented this
* function.
*/
/*ARGSUSED*/
static void *
/* Mark slottable in state blocking */
/* alert calling thread that this thread has started */
/* wait for an event, or number of threads to reach zero */
for (;;) {
/*
* Make sure we've really been signaled, and not waking
* for another reason.
*/
}
/* See why we were awakened */
if (!pkcs11_initialized) {
/* Another thread has called C_Finalize() */
return (NULL);
}
/* A thread has finished, decrement counter */
(void) pthread_mutex_lock(&slottable->
sl_wfse_state == WFSE_EVENT) {
(void) pthread_mutex_unlock(&slottable->
/*
* st_event_slot is set to a valid value, event
* flag is set for that slot. The flag will
* be cleared by main C_WaitForSlotEvent().
*/
(void) pthread_mutex_unlock(
pthread_exit(0);
} else {
(void) pthread_mutex_unlock(&slottable->
}
}
if (slottable->st_thr_count == 0) {
/* No more threads, no events found */
pthread_exit(0);
}
}
/*NOTREACHED*/
return (NULL);
}
/*
* child_waitforslotevent is used as a child thread to contact
* underlying provider's C_WaitForSlotEvent().
*/
static void *
CK_SLOT_ID i;
/*
* Need to hold the mutex while processing the results, to
* keep things synchronized with the listener thread and
* the slottable. Otherwise, due to the timing
* at which some underlying providers complete, the listener
* thread may not actually be blocking on st_wait_cond when
* this child signals. Holding the lock a bit longer prevents
* this from happening.
*/
/*
* We've taken the mutex when the listener should have
* control. Release the mutex, thread scheduler should
* give control back to the listener.
*/
(void) sleep(1);
}
/* we've had an event, find slot and store it */
/*
* It is safe to unset active status now, since call to
* underlying provider has already terminated, and we
* hold the slottable wide mutex (st_mutex).
*/
(void) pthread_mutex_lock(&slottable->
(void) pthread_mutex_unlock(&slottable->
break;
}
(void) pthread_mutex_lock(&slottable->
(void) pthread_mutex_unlock(&slottable->
slottable->st_event_slot = i;
if (slottable->st_blocking) {
(void) pthread_cond_signal(&slottable->
}
(void) pthread_mutex_unlock(
pthread_exit(0);
}
}
}
(void) pthread_mutex_lock(&slottable->
/*
* If the provider told us that it does not support
* this function, we should mark it so we do not waste
* time later with it. If an error returned, we'll clean
* up this thread now and possibly try it again later.
*/
if (rv == CKR_FUNCTION_NOT_SUPPORTED) {
}
/*
* It is safe to unset active status now, since call to
* underlying provider has already terminated, and we
* hold the slottable wide mutex (st_mutex).
*/
(void) pthread_mutex_unlock(&slottable->
if (slottable->st_blocking) {
}
/* Manually exit the thread, since nobody will join to it */
pthread_exit(0);
/*NOTREACHED*/
return (NULL);
}