#include <string.h>
#include <strings.h>
#include "pkcs11Conf.h"
#include "metaGlobal.h"
#define INITIAL_MECHLIST_SIZE 256
typedef struct mechliststruct {
CK_MECHANISM_TYPE type;
mechinfo_t *slots;
} mechlist_t;
static pthread_rwlock_t mechlist_lock = PTHREAD_RWLOCK_INITIALIZER;
static mechlist_t *mechlist;
static unsigned long num_mechs;
static unsigned long true_mechlist_size;
static CK_RV meta_mechManager_update_mech(CK_MECHANISM_TYPE, boolean_t);
static CK_RV meta_mechManager_update_slot(CK_ULONG);
static CK_RV update_slotmech(CK_MECHANISM_TYPE, CK_ULONG, unsigned long);
static CK_RV meta_mechManager_allocmechs(CK_MECHANISM_TYPE *, unsigned long,
unsigned long *);
static boolean_t find_mech_index(CK_MECHANISM_TYPE, unsigned long *);
static int qsort_mechtypes(const void *, const void *);
CK_RV
meta_mechManager_initialize()
{
mechlist = calloc(INITIAL_MECHLIST_SIZE, sizeof (mechlist_t));
if (mechlist == NULL)
return (CKR_HOST_MEMORY);
true_mechlist_size = INITIAL_MECHLIST_SIZE;
num_mechs = 0;
return (CKR_OK);
}
void
meta_mechManager_finalize()
{
int i;
for (i = 0; i < num_mechs; i++) {
free(mechlist[i].slots);
}
free(mechlist);
mechlist = NULL;
num_mechs = 0;
true_mechlist_size = 0;
}
CK_RV
meta_mechManager_get_mechs(CK_MECHANISM_TYPE *list, CK_ULONG *listsize)
{
CK_RV rv = CKR_OK;
CK_ULONG num_found = 0;
CK_ULONG slotnum, num_slots;
unsigned long i;
num_slots = meta_slotManager_get_slotcount();
for (slotnum = 0; slotnum < num_slots; slotnum++) {
(void) meta_mechManager_update_slot(slotnum);
}
(void) pthread_rwlock_rdlock(&mechlist_lock);
for (i = 0; i < num_mechs; i++) {
CK_ULONG j;
boolean_t supported;
if (pkcs11_is_dismech(METASLOT_FRAMEWORK_ID,
mechlist[i].type)) {
continue;
}
supported = FALSE;
for (j = 0; j < num_slots; j++) {
if (!mechlist[i].slots[j].initialized)
continue;
if (mechlist[i].slots[j].supported) {
supported = B_TRUE;
break;
}
}
if (supported) {
num_found++;
if (list && *listsize >= num_found) {
list[num_found - 1] = mechlist[i].type;
}
}
}
(void) pthread_rwlock_unlock(&mechlist_lock);
if (num_found > *listsize)
rv = CKR_BUFFER_TOO_SMALL;
*listsize = num_found;
return (rv);
}
CK_RV
meta_mechManager_get_slots(mech_support_info_t *mech_support_info,
boolean_t force_update, CK_MECHANISM_INFO *mech_info)
{
CK_RV rv;
boolean_t found;
CK_ULONG i, num_slots;
unsigned long index, num_found = 0;
CK_MECHANISM_INFO info;
rv = meta_mechManager_update_mech(mech_support_info->mech,
force_update);
if (rv != CKR_OK) {
return (rv);
}
(void) pthread_rwlock_rdlock(&mechlist_lock);
found = find_mech_index(mech_support_info->mech, &index);
if (!found) {
goto finish;
}
num_slots = meta_slotManager_get_slotcount();
for (i = 0; i < num_slots; i++) {
if (!mechlist[index].slots[i].initialized ||
!mechlist[index].slots[i].supported)
continue;
if (mech_info) {
info = mechlist[index].slots[i].mechanism_info;
if (!(info.flags & mech_info->flags)) {
continue;
}
}
num_found++;
(mech_support_info->supporting_slots)[num_found - 1]
= &mechlist[index].slots[i];
}
finish:
(void) pthread_rwlock_unlock(&mechlist_lock);
if (num_found == 0) {
rv = CKR_MECHANISM_INVALID;
} else {
mech_support_info->num_supporting_slots = num_found;
}
return (rv);
}
static CK_RV
meta_mechManager_update_mech(CK_MECHANISM_TYPE mech, boolean_t force_refresh)
{
CK_RV rv;
CK_ULONG slot, num_slots;
unsigned long index = 0;
boolean_t found;
rv = meta_mechManager_allocmechs(&mech, 1, &index);
if (rv != CKR_OK)
return (rv);
(void) pthread_rwlock_wrlock(&mechlist_lock);
found = find_mech_index(mech, &index);
if (!found) {
rv = CKR_GENERAL_ERROR;
goto finish;
}
num_slots = meta_slotManager_get_slotcount();
for (slot = 0; slot < num_slots; slot++) {
if (force_refresh || !mechlist[index].slots[slot].initialized) {
rv = update_slotmech(mech, slot, index);
if (rv != CKR_OK) {
rv = CKR_OK;
}
}
}
finish:
(void) pthread_rwlock_unlock(&mechlist_lock);
return (rv);
}
static CK_RV
meta_mechManager_update_slot(CK_ULONG slotnum)
{
unsigned long index = 0;
CK_MECHANISM_TYPE *slot_mechlist = NULL, *tmp_slot_mechlist = NULL;
CK_ULONG slot_mechlistsize, mechnum, tmp_mechlistsize;
CK_RV rv;
boolean_t found;
CK_SLOT_ID fw_st_id, true_id;
int i;
fw_st_id = meta_slotManager_get_framework_table_id(slotnum);
true_id = TRUEID(fw_st_id);
rv = FUNCLIST(fw_st_id)->C_GetMechanismList(true_id, NULL,
&slot_mechlistsize);
if (rv != CKR_OK) {
goto finish;
}
tmp_slot_mechlist = malloc(
slot_mechlistsize * sizeof (CK_MECHANISM_TYPE));
if (tmp_slot_mechlist == NULL) {
rv = CKR_HOST_MEMORY;
goto finish;
}
rv = FUNCLIST(fw_st_id)->C_GetMechanismList(true_id,
tmp_slot_mechlist, &slot_mechlistsize);
if (rv != CKR_OK) {
goto finish;
}
slot_mechlist = malloc(slot_mechlistsize * sizeof (CK_MECHANISM_TYPE));
if (slot_mechlist == NULL) {
rv = CKR_HOST_MEMORY;
goto finish;
}
tmp_mechlistsize = 0;
for (i = 0; i < slot_mechlistsize; i++) {
if (pkcs11_is_dismech(fw_st_id, tmp_slot_mechlist[i])) {
continue;
}
slot_mechlist[tmp_mechlistsize] = tmp_slot_mechlist[i];
tmp_mechlistsize++;
}
slot_mechlistsize = tmp_mechlistsize;
qsort(slot_mechlist, slot_mechlistsize, sizeof (CK_MECHANISM_TYPE),
qsort_mechtypes);
rv = meta_mechManager_allocmechs(slot_mechlist, slot_mechlistsize,
&index);
if (rv != CKR_OK)
goto finish;
(void) pthread_rwlock_wrlock(&mechlist_lock);
for (mechnum = 0; mechnum < slot_mechlistsize; mechnum++) {
found = find_mech_index(slot_mechlist[mechnum], &index);
if (!found) {
rv = CKR_GENERAL_ERROR;
goto finish;
}
rv = update_slotmech(slot_mechlist[mechnum], slotnum, index);
if (rv != CKR_OK) {
rv = CKR_OK;
continue;
}
}
(void) pthread_rwlock_unlock(&mechlist_lock);
finish:
if (slot_mechlist) {
free(slot_mechlist);
}
if (tmp_slot_mechlist) {
free(tmp_slot_mechlist);
}
return (rv);
}
static CK_RV
update_slotmech(CK_MECHANISM_TYPE mech, CK_ULONG slotnum,
unsigned long index)
{
CK_RV rv = CKR_OK;
CK_MECHANISM_INFO info;
CK_SLOT_ID fw_st_id, true_id;
mechlist[index].slots[slotnum].slotnum = slotnum;
fw_st_id = meta_slotManager_get_framework_table_id(slotnum);
true_id = TRUEID(fw_st_id);
if (pkcs11_is_dismech(fw_st_id, mech)) {
mechlist[index].slots[slotnum].initialized = B_TRUE;
mechlist[index].slots[slotnum].supported = B_FALSE;
bzero(&mechlist[index].slots[slotnum].mechanism_info,
sizeof (CK_MECHANISM_INFO));
goto finish;
}
rv = FUNCLIST(fw_st_id)->C_GetMechanismInfo(true_id, mech, &info);
if (rv == CKR_OK) {
mechlist[index].slots[slotnum].initialized = B_TRUE;
mechlist[index].slots[slotnum].supported = B_TRUE;
mechlist[index].slots[slotnum].mechanism_info = info;
} else {
mechlist[index].slots[slotnum].initialized = B_TRUE;
mechlist[index].slots[slotnum].supported = B_FALSE;
bzero(&mechlist[index].slots[slotnum].mechanism_info,
sizeof (CK_MECHANISM_INFO));
}
finish:
return (rv);
}
static CK_RV
meta_mechManager_allocmechs(CK_MECHANISM_TYPE *new_mechs,
unsigned long num_new_mechs, unsigned long *index_hint)
{
CK_RV rv = CKR_OK;
unsigned long i, index = 0;
boolean_t found;
(void) pthread_rwlock_rdlock(&mechlist_lock);
for (i = 0; i < num_new_mechs; i++) {
found = find_mech_index(new_mechs[i], &index);
if (i == 0)
*index_hint = index;
if (!found)
break;
}
(void) pthread_rwlock_unlock(&mechlist_lock);
if (found) {
return (CKR_OK);
}
(void) pthread_rwlock_wrlock(&mechlist_lock);
for (; i < num_new_mechs; i++) {
found = find_mech_index(new_mechs[i], &index);
if (!found) {
mechinfo_t *new_mechinfos;
new_mechinfos = calloc(meta_slotManager_get_slotcount(),
sizeof (mechinfo_t));
if (new_mechinfos == NULL) {
rv = CKR_HOST_MEMORY;
goto finish;
}
if (num_mechs == true_mechlist_size) {
mechlist_t *newmechlist;
newmechlist = realloc(mechlist,
2 * true_mechlist_size *
sizeof (mechlist_t));
if (newmechlist == NULL) {
rv = CKR_HOST_MEMORY;
free(new_mechinfos);
goto finish;
}
mechlist = newmechlist;
true_mechlist_size *= 2;
}
(void) memmove(&mechlist[index+1], &mechlist[index],
(num_mechs - index) * sizeof (mechlist_t));
num_mechs++;
mechlist[index].type = new_mechs[i];
mechlist[index].slots = new_mechinfos;
}
}
finish:
(void) pthread_rwlock_unlock(&mechlist_lock);
return (rv);
}
static boolean_t
find_mech_index(CK_MECHANISM_TYPE mechanism, unsigned long *index)
{
boolean_t found = B_FALSE;
unsigned long i;
for (i = 0; i < num_mechs; i++) {
if (mechlist[i].type == mechanism) {
found = B_TRUE;
break;
}
if (mechlist[i].type > mechanism)
break;
}
*index = i;
return (found);
}
static int
qsort_mechtypes(const void *arg1, const void *arg2)
{
CK_MECHANISM_TYPE mech1 = *((CK_MECHANISM_TYPE *)arg1);
CK_MECHANISM_TYPE mech2 = *((CK_MECHANISM_TYPE *)arg2);
if (mech1 > mech2)
return (1);
if (mech1 < mech2)
return (-1);
return (0);
}
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)
{
boolean_t found;
CK_RV rv;
unsigned long index;
CK_MECHANISM_INFO info;
*supports = B_FALSE;
rv = meta_mechManager_update_mech(mechanism, force_update);
if (rv != CKR_OK)
return (rv);
(void) pthread_rwlock_rdlock(&mechlist_lock);
found = find_mech_index(mechanism, &index);
if (!found) {
goto finish;
}
if ((mechlist[index].slots[slotnum].initialized) &&
(mechlist[index].slots[slotnum].supported)) {
if (mech_info) {
info = mechlist[index].slots[slotnum].mechanism_info;
if (!(info.flags & mech_info->flags)) {
goto finish;
}
}
*supports = B_TRUE;
if (slot_info) {
*slot_info = &(mechlist[index].slots[slotnum]);
}
}
finish:
(void) pthread_rwlock_unlock(&mechlist_lock);
return (rv);
}