#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);
CK_RV
C_GetSlotList(CK_BBOOL tokenPresent, CK_SLOT_ID_PTR pSlotList,
CK_ULONG_PTR pulCount)
{
CK_RV rv;
CK_RV prov_rv;
CK_SLOT_ID true_id;
CK_SLOT_INFO_PTR pinfo;
CK_SLOT_ID count = 0, i;
CK_SLOT_ID slot_id;
if ((purefastpath || policyfastpath) && (!metaslot_enabled)) {
return (fast_funcs->C_GetSlotList(tokenPresent, pSlotList,
pulCount));
}
if (!pkcs11_initialized) {
return (CKR_CRYPTOKI_NOT_INITIALIZED);
}
if (pulCount == NULL) {
return (CKR_ARGUMENTS_BAD);
}
if (tokenPresent) {
pinfo = malloc(sizeof (CK_SLOT_INFO));
if (pinfo == NULL) {
return (CKR_HOST_MEMORY);
}
}
slot_id = slottable->st_first;
for (i = slottable->st_first; i <= slottable->st_last; i++) {
if ((pkcs11_is_valid_slot(i) == CKR_OK) &&
((!metaslot_enabled) || (i != metaslot_keystore_slotid))) {
if (tokenPresent) {
true_id = TRUEID(i);
prov_rv = FUNCLIST(i)->
C_GetSlotInfo(true_id, pinfo);
if ((prov_rv != CKR_OK) ||
!(pinfo->flags & CKF_TOKEN_PRESENT)) {
continue;
}
}
if (pSlotList && (*pulCount > count)) {
pSlotList[count] = slot_id;
slot_id++;
}
count++;
}
}
if ((*pulCount < count) && (pSlotList != NULL)) {
rv = CKR_BUFFER_TOO_SMALL;
} else {
rv = CKR_OK;
}
*pulCount = count;
if (tokenPresent) {
free(pinfo);
}
return (rv);
}
CK_RV
C_GetSlotInfo(CK_SLOT_ID slotID, CK_SLOT_INFO_PTR pInfo)
{
CK_RV rv;
CK_SLOT_ID fw_st_id;
if (!pkcs11_initialized) {
return (CKR_CRYPTOKI_NOT_INITIALIZED);
}
if ((purefastpath || policyfastpath) && !metaslot_enabled)
return (fast_funcs->C_GetSlotInfo(slotID, pInfo));
if (slotID == METASLOT_FRAMEWORK_ID) {
return (meta_GetSlotInfo(METASLOT_SLOTID, pInfo));
}
if (pkcs11_validate_and_convert_slotid(slotID, &fw_st_id) != CKR_OK) {
return (CKR_SLOT_ID_INVALID);
}
rv = FUNCLIST(fw_st_id)->C_GetSlotInfo(TRUEID(fw_st_id), pInfo);
if (rv == CKR_FUNCTION_NOT_SUPPORTED) {
return (CKR_FUNCTION_FAILED);
}
return (rv);
}
CK_RV
C_GetTokenInfo(CK_SLOT_ID slotID, CK_TOKEN_INFO_PTR pInfo)
{
CK_RV rv;
CK_SLOT_ID fw_st_id;
if (!pkcs11_initialized) {
return (CKR_CRYPTOKI_NOT_INITIALIZED);
}
if ((purefastpath || policyfastpath) && !metaslot_enabled)
return (fast_funcs->C_GetTokenInfo(slotID, pInfo));
if (slotID == METASLOT_FRAMEWORK_ID) {
return (meta_GetTokenInfo(METASLOT_SLOTID, pInfo));
}
if (pkcs11_validate_and_convert_slotid(slotID, &fw_st_id) != CKR_OK) {
return (CKR_SLOT_ID_INVALID);
}
rv = FUNCLIST(fw_st_id)->C_GetTokenInfo(TRUEID(fw_st_id), pInfo);
if (rv == CKR_FUNCTION_NOT_SUPPORTED) {
return (CKR_FUNCTION_FAILED);
}
return (rv);
}
CK_RV
C_WaitForSlotEvent(CK_FLAGS flags, CK_SLOT_ID_PTR pSlot, CK_VOID_PTR pReserved)
{
CK_SLOT_ID i, j;
uint32_t prov_id;
int32_t last_prov_id = -1;
CK_RV rv = CKR_OK;
CK_SLOT_ID event_slot;
pkcs11_slot_t *cur_slot;
if (purefastpath || policyfastpath) {
return (fast_funcs->C_WaitForSlotEvent(flags, pSlot,
pReserved));
}
if (!pkcs11_initialized) {
return (CKR_CRYPTOKI_NOT_INITIALIZED);
}
if (pReserved != NULL) {
return (CKR_ARGUMENTS_BAD);
}
(void) pthread_mutex_lock(&slottable->st_mutex);
if ((slottable->st_blocking) || (slottable->st_wfse_active)) {
(void) pthread_mutex_unlock(&slottable->st_mutex);
return (CKR_FUNCTION_FAILED);
} else {
slottable->st_wfse_active = B_TRUE;
(void) pthread_mutex_unlock(&slottable->st_mutex);
}
for (i = slottable->st_first; i <= slottable->st_last; i++) {
cur_slot = slottable->st_slots[i];
if (cur_slot->sl_wfse_state == WFSE_EVENT) {
(void) pthread_mutex_lock(&cur_slot->sl_mutex);
cur_slot->sl_wfse_state = WFSE_CLEAR;
(void) pthread_mutex_unlock(&cur_slot->sl_mutex);
*pSlot = i;
(void) pthread_mutex_lock(&slottable->st_mutex);
slottable->st_wfse_active = B_FALSE;
(void) pthread_mutex_unlock(&slottable->st_mutex);
return (CKR_OK);
}
}
if (flags & CKF_DONT_BLOCK) {
for (i = slottable->st_first; i <= slottable->st_last; i++) {
prov_id = slottable->st_slots[i]->sl_prov_id;
cur_slot = slottable->st_slots[i];
if (prov_id == last_prov_id) {
continue;
}
(void) pthread_mutex_lock(&cur_slot->sl_mutex);
if (cur_slot->sl_wfse_state == WFSE_ACTIVE) {
(void) pthread_mutex_unlock(
&cur_slot->sl_mutex);
continue;
}
cur_slot->sl_wfse_state = WFSE_ACTIVE;
(void) pthread_mutex_unlock(&cur_slot->sl_mutex);
rv = FUNCLIST(i)->C_WaitForSlotEvent(flags,
pSlot, pReserved);
(void) pthread_mutex_lock(&cur_slot->sl_mutex);
cur_slot->sl_wfse_state = WFSE_CLEAR;
(void) pthread_mutex_unlock(&cur_slot->sl_mutex);
if ((rv == CKR_OK) && (pSlot != NULL)) {
j = i;
while (prov_id ==
slottable->st_slots[j]->sl_prov_id) {
if (*pSlot == TRUEID(j)) {
*pSlot = j;
(void) pthread_mutex_lock(
&slottable->st_mutex);
slottable->st_wfse_active =
B_FALSE;
(void) pthread_mutex_unlock(
&slottable->st_mutex);
return (CKR_OK);
}
j++;
}
}
last_prov_id = prov_id;
}
(void) pthread_mutex_lock(&slottable->st_mutex);
slottable->st_wfse_active = B_FALSE;
(void) pthread_mutex_unlock(&slottable->st_mutex);
return (CKR_NO_EVENT);
} else if (!(flags & CKF_DONT_BLOCK) && (pkcs11_cant_create_threads)) {
(void) pthread_mutex_lock(&slottable->st_mutex);
slottable->st_wfse_active = B_FALSE;
(void) pthread_mutex_unlock(&slottable->st_mutex);
return (CKR_FUNCTION_FAILED);
}
(void) pthread_mutex_lock(&slottable->st_start_mutex);
(void) pthread_mutex_lock(&slottable->st_mutex);
if (pthread_create(&slottable->st_tid, NULL,
listener_waitforslotevent, NULL) != 0) {
slottable->st_wfse_active = B_FALSE;
(void) pthread_mutex_unlock(&slottable->st_mutex);
(void) pthread_mutex_unlock(&slottable->st_start_mutex);
return (CKR_FUNCTION_FAILED);
}
(void) pthread_mutex_unlock(&slottable->st_mutex);
(void) pthread_cond_wait(&slottable->st_start_cond,
&slottable->st_start_mutex);
(void) pthread_mutex_unlock(&slottable->st_start_mutex);
(void) pthread_mutex_lock(&slottable->st_mutex);
for (i = slottable->st_first; i <= slottable->st_last; i++) {
prov_id = slottable->st_slots[i]->sl_prov_id;
cur_slot = slottable->st_slots[i];
if (prov_id == last_prov_id) {
continue;
}
(void) pthread_mutex_lock(&cur_slot->sl_mutex);
if ((cur_slot->sl_wfse_state == WFSE_ACTIVE) ||
(cur_slot->sl_no_wfse)) {
(void) pthread_mutex_unlock(&cur_slot->sl_mutex);
last_prov_id = prov_id;
continue;
}
cur_slot->sl_wfse_state = WFSE_ACTIVE;
if (cur_slot->sl_wfse_args == NULL) {
cur_slot->sl_wfse_args = malloc(sizeof (wfse_args_t));
if (cur_slot->sl_wfse_args == NULL) {
(void) pthread_mutex_unlock(
&cur_slot->sl_mutex);
slottable->st_wfse_active = B_FALSE;
(void) pthread_mutex_unlock(
&slottable->st_mutex);
return (CKR_HOST_MEMORY);
}
cur_slot->sl_wfse_args->flags = flags;
cur_slot->sl_wfse_args->pReserved = pReserved;
cur_slot->sl_wfse_args->slotid = i;
}
if (pthread_create(&cur_slot->sl_tid, NULL,
child_waitforslotevent,
(void *)cur_slot->sl_wfse_args) != 0) {
(void) pthread_mutex_unlock(&cur_slot->sl_mutex);
continue;
}
(void) pthread_mutex_unlock(&cur_slot->sl_mutex);
slottable->st_thr_count++;
last_prov_id = prov_id;
}
if (slottable->st_thr_count == 0) {
(void) pthread_cancel(slottable->st_tid);
slottable->st_wfse_active = B_FALSE;
(void) pthread_mutex_unlock(&slottable->st_mutex);
return (CKR_NO_EVENT);
}
(void) pthread_mutex_unlock(&slottable->st_mutex);
(void) pthread_join(slottable->st_tid, NULL);
if (!pkcs11_initialized) {
(void) pthread_mutex_lock(&slottable->st_mutex);
slottable->st_wfse_active = B_FALSE;
(void) pthread_mutex_unlock(&slottable->st_mutex);
return (CKR_CRYPTOKI_NOT_INITIALIZED);
}
(void) pthread_mutex_lock(&slottable->st_mutex);
event_slot = slottable->st_event_slot;
(void) pthread_mutex_unlock(&slottable->st_mutex);
if (pkcs11_is_valid_slot(event_slot) == CKR_OK) {
(void) pthread_mutex_lock(&slottable->
st_slots[event_slot]->sl_mutex);
if (slottable->st_slots[event_slot]->
sl_wfse_state == WFSE_EVENT) {
slottable->st_slots[event_slot]->sl_wfse_state =
WFSE_CLEAR;
(void) pthread_mutex_unlock(&slottable->
st_slots[event_slot]->sl_mutex);
*pSlot = event_slot;
(void) pthread_mutex_lock(&slottable->st_mutex);
slottable->st_blocking = B_FALSE;
slottable->st_wfse_active = B_FALSE;
(void) pthread_mutex_unlock(&slottable->st_mutex);
return (CKR_OK);
} else {
(void) pthread_mutex_unlock(&slottable->
st_slots[event_slot]->sl_mutex);
}
}
(void) pthread_mutex_lock(&slottable->st_mutex);
slottable->st_blocking = B_FALSE;
slottable->st_wfse_active = B_FALSE;
(void) pthread_mutex_unlock(&slottable->st_mutex);
return (CKR_NO_EVENT);
}
CK_RV
C_GetMechanismList(CK_SLOT_ID slotID, CK_MECHANISM_TYPE_PTR pMechanismList,
CK_ULONG_PTR pulCount)
{
CK_RV rv = CKR_OK;
CK_ULONG mech_count;
CK_ULONG tmpmech_count;
CK_MECHANISM_TYPE_PTR pmech_list, tmpmech_list;
CK_SLOT_ID true_id;
CK_SLOT_ID fw_st_id;
CK_FUNCTION_LIST_PTR prov_funcs;
CK_ULONG i;
if (!pkcs11_initialized) {
return (CKR_CRYPTOKI_NOT_INITIALIZED);
}
if ((purefastpath || policyfastpath) && !metaslot_enabled)
return (fast_funcs->C_GetMechanismList(slotID,
pMechanismList, pulCount));
if (slotID == METASLOT_FRAMEWORK_ID) {
return (meta_GetMechanismList(METASLOT_SLOTID, pMechanismList,
pulCount));
}
if (pkcs11_validate_and_convert_slotid(slotID, &fw_st_id) != CKR_OK) {
return (CKR_SLOT_ID_INVALID);
}
if (policyfastpath) {
true_id = fw_st_id;
slotID = fast_slot;
prov_funcs = fast_funcs;
} else {
true_id = TRUEID(fw_st_id);
prov_funcs = FUNCLIST(fw_st_id);
}
mech_count = 0;
tmpmech_count = MECHLIST_SIZE;
pmech_list = malloc(tmpmech_count * sizeof (CK_MECHANISM_TYPE));
if (pmech_list == NULL) {
return (CKR_HOST_MEMORY);
}
rv = prov_funcs->C_GetMechanismList(true_id,
pmech_list, &tmpmech_count);
if (rv == CKR_BUFFER_TOO_SMALL) {
tmpmech_list = pmech_list;
pmech_list = realloc
(tmpmech_list, tmpmech_count * sizeof (CK_MECHANISM_TYPE));
if (pmech_list == NULL) {
free(tmpmech_list);
return (CKR_HOST_MEMORY);
}
rv = prov_funcs->C_GetMechanismList(true_id,
pmech_list, &tmpmech_count);
}
if (rv != CKR_OK) {
*pulCount = 0;
free(pmech_list);
return (CKR_OK);
}
for (i = 0; i < tmpmech_count; i++) {
if (pkcs11_is_dismech(fw_st_id, pmech_list[i])) {
continue;
}
if (pMechanismList && (*pulCount > mech_count)) {
pMechanismList[mech_count] = pmech_list[i];
}
mech_count++;
}
if ((*pulCount < mech_count) && (pMechanismList != NULL)) {
*pulCount = mech_count;
free(pmech_list);
return (CKR_BUFFER_TOO_SMALL);
}
*pulCount = mech_count;
free(pmech_list);
return (CKR_OK);
}
CK_RV
C_GetMechanismInfo(CK_SLOT_ID slotID, CK_MECHANISM_TYPE type,
CK_MECHANISM_INFO_PTR pInfo)
{
CK_RV rv;
CK_SLOT_ID true_id;
CK_SLOT_ID fw_st_id;
CK_FUNCTION_LIST_PTR prov_funcs;
if (!pkcs11_initialized) {
return (CKR_CRYPTOKI_NOT_INITIALIZED);
}
if ((purefastpath || policyfastpath) && !metaslot_enabled)
return (fast_funcs->C_GetMechanismInfo(slotID, type, pInfo));
if (slotID == METASLOT_FRAMEWORK_ID) {
return (meta_GetMechanismInfo(METASLOT_SLOTID, type, pInfo));
}
if (pkcs11_validate_and_convert_slotid(slotID, &fw_st_id) != CKR_OK) {
return (CKR_SLOT_ID_INVALID);
}
if (policyfastpath) {
true_id = fw_st_id;
slotID = fast_slot;
prov_funcs = fast_funcs;
} else {
true_id = TRUEID(fw_st_id);
prov_funcs = FUNCLIST(fw_st_id);
}
if (pkcs11_is_dismech(fw_st_id, type)) {
return (CKR_MECHANISM_INVALID);
}
rv = prov_funcs->C_GetMechanismInfo(true_id, type, pInfo);
if (rv == CKR_FUNCTION_NOT_SUPPORTED) {
return (CKR_FUNCTION_FAILED);
}
return (rv);
}
CK_RV
C_InitToken(CK_SLOT_ID slotID, CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen,
CK_UTF8CHAR_PTR pLabel)
{
CK_RV rv;
CK_SLOT_ID fw_st_id;
if (!pkcs11_initialized) {
return (CKR_CRYPTOKI_NOT_INITIALIZED);
}
if ((purefastpath || policyfastpath) && !metaslot_enabled)
return (fast_funcs->C_InitToken(slotID, pPin, ulPinLen,
pLabel));
if (slotID == METASLOT_FRAMEWORK_ID) {
return (meta_InitToken(METASLOT_SLOTID, pPin, ulPinLen,
pLabel));
}
if (pkcs11_validate_and_convert_slotid(slotID, &fw_st_id) != CKR_OK) {
return (CKR_SLOT_ID_INVALID);
}
rv = FUNCLIST(fw_st_id)->C_InitToken(TRUEID(fw_st_id), pPin, ulPinLen,
pLabel);
if (rv == CKR_FUNCTION_NOT_SUPPORTED) {
return (CKR_FUNCTION_FAILED);
}
return (rv);
}
CK_RV
C_InitPIN(CK_SESSION_HANDLE hSession, CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen)
{
CK_RV rv;
pkcs11_session_t *sessp;
if (purefastpath || policyfastpath) {
return (fast_funcs->C_InitPIN(hSession, pPin, ulPinLen));
}
if (!pkcs11_initialized) {
return (CKR_CRYPTOKI_NOT_INITIALIZED);
}
HANDLE2SESSION(hSession, sessp, rv);
if (rv != CKR_OK) {
return (rv);
}
rv = FUNCLIST(sessp->se_slotid)->C_InitPIN(sessp->se_handle,
pPin, ulPinLen);
if (rv == CKR_FUNCTION_NOT_SUPPORTED) {
return (CKR_FUNCTION_FAILED);
}
return (rv);
}
CK_RV
C_SetPIN(CK_SESSION_HANDLE hSession, CK_UTF8CHAR_PTR pOldPin,
CK_ULONG ulOldPinLen, CK_UTF8CHAR_PTR pNewPin,
CK_ULONG ulNewPinLen)
{
CK_RV rv;
pkcs11_session_t *sessp;
if (purefastpath || policyfastpath) {
return (fast_funcs->C_SetPIN(hSession, pOldPin, ulOldPinLen,
pNewPin, ulNewPinLen));
}
if (!pkcs11_initialized) {
return (CKR_CRYPTOKI_NOT_INITIALIZED);
}
HANDLE2SESSION(hSession, sessp, rv);
if (rv != CKR_OK) {
return (rv);
}
rv = FUNCLIST(sessp->se_slotid)->C_SetPIN(sessp->se_handle,
pOldPin, ulOldPinLen, pNewPin, ulNewPinLen);
if (rv == CKR_FUNCTION_NOT_SUPPORTED) {
return (CKR_FUNCTION_FAILED);
}
return (rv);
}
static void *
listener_waitforslotevent(void *arg) {
CK_SLOT_ID eventID;
(void) pthread_mutex_lock(&slottable->st_mutex);
slottable->st_blocking = B_TRUE;
(void) pthread_mutex_lock(&slottable->st_start_mutex);
(void) pthread_cond_signal(&slottable->st_start_cond);
(void) pthread_mutex_unlock(&slottable->st_start_mutex);
for (;;) {
while (slottable->st_list_signaled != B_TRUE) {
(void) pthread_cond_wait(&slottable->st_wait_cond,
&slottable->st_mutex);
}
slottable->st_list_signaled = B_FALSE;
if (!pkcs11_initialized) {
(void) pthread_mutex_unlock(&slottable->st_mutex);
return (NULL);
}
slottable->st_thr_count--;
eventID = slottable->st_event_slot;
if (pkcs11_is_valid_slot(eventID) == CKR_OK) {
(void) pthread_mutex_lock(&slottable->
st_slots[eventID]->sl_mutex);
if (slottable->st_slots[eventID]->
sl_wfse_state == WFSE_EVENT) {
(void) pthread_mutex_unlock(&slottable->
st_slots[eventID]->sl_mutex);
(void) pthread_mutex_unlock(
&slottable->st_mutex);
pthread_exit(0);
} else {
(void) pthread_mutex_unlock(&slottable->
st_slots[eventID]->sl_mutex);
}
}
if (slottable->st_thr_count == 0) {
(void) pthread_mutex_unlock(&slottable->st_mutex);
pthread_exit(0);
}
}
return (NULL);
}
static void *
child_waitforslotevent(void *arg) {
wfse_args_t *wfse = (wfse_args_t *)arg;
CK_SLOT_ID slot;
CK_RV rv;
uint32_t cur_prov;
CK_SLOT_ID i;
rv = FUNCLIST(wfse->slotid)->C_WaitForSlotEvent(wfse->flags, &slot,
wfse->pReserved);
(void) pthread_mutex_lock(&slottable->st_mutex);
while (slottable->st_list_signaled == B_TRUE) {
(void) pthread_mutex_unlock(&slottable->st_mutex);
(void) sleep(1);
(void) pthread_mutex_lock(&slottable->st_mutex);
}
if (rv == CKR_OK) {
cur_prov = slottable->st_slots[wfse->slotid]->sl_prov_id;
(void) pthread_mutex_lock(&slottable->
st_slots[wfse->slotid]->sl_mutex);
slottable->st_slots[wfse->slotid]->sl_wfse_state = WFSE_CLEAR;
(void) pthread_mutex_unlock(&slottable->
st_slots[wfse->slotid]->sl_mutex);
for (i = wfse->slotid; i <= slottable->st_last; i++) {
if (cur_prov != slottable->st_slots[i]->sl_prov_id) {
break;
}
if (slot == slottable->st_slots[i]->sl_id) {
(void) pthread_mutex_lock(&slottable->
st_slots[i]->sl_mutex);
slottable->st_slots[i]->
sl_wfse_state = WFSE_EVENT;
(void) pthread_mutex_unlock(&slottable->
st_slots[i]->sl_mutex);
slottable->st_event_slot = i;
if (slottable->st_blocking) {
slottable->st_list_signaled = B_TRUE;
(void) pthread_cond_signal(&slottable->
st_wait_cond);
}
(void) pthread_mutex_unlock(
&slottable->st_mutex);
pthread_exit(0);
}
}
}
(void) pthread_mutex_lock(&slottable->
st_slots[wfse->slotid]->sl_mutex);
if (rv == CKR_FUNCTION_NOT_SUPPORTED) {
slottable->st_slots[wfse->slotid]->sl_no_wfse = B_TRUE;
}
slottable->st_slots[wfse->slotid]->sl_wfse_state = WFSE_CLEAR;
(void) pthread_mutex_unlock(&slottable->
st_slots[wfse->slotid]->sl_mutex);
if (slottable->st_blocking) {
slottable->st_list_signaled = B_TRUE;
(void) pthread_cond_signal(&slottable->st_wait_cond);
}
(void) pthread_mutex_unlock(&slottable->st_mutex);
pthread_exit(0);
return (NULL);
}