#include <sys/types.h>
#include <sys/stat.h>
#include <dlfcn.h>
#include <fcntl.h>
#include <link.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <errno.h>
#include <pthread.h>
#include <sys/mman.h>
#include <sys/crypto/elfsign.h>
#include <cryptoutil.h>
#include <security/cryptoki.h>
#include "pkcs11Global.h"
#include "pkcs11Conf.h"
#include "pkcs11Slot.h"
#include "metaGlobal.h"
boolean_t purefastpath = B_FALSE;
boolean_t policyfastpath = B_FALSE;
CK_FUNCTION_LIST_PTR fast_funcs = NULL;
CK_SLOT_ID fast_slot = 0;
boolean_t metaslot_enabled = B_FALSE;
boolean_t metaslot_auto_key_migrate = B_FALSE;
metaslot_config_t metaslot_config;
void (*Tmp_GetThreshold)(void *) = NULL;
cipher_mechs_threshold_t meta_mechs_threshold[MAX_NUM_THRESHOLD];
static const char *conf_err = "See cryptoadm(8). Skipping this plug-in.";
static CK_RV
setup_metaslot(uentry_t *metaslot_entry) {
CK_RV rv;
CK_MECHANISM_TYPE_PTR prov_pol_mechs = NULL;
pkcs11_slot_t *cur_slot;
if ((metaslot_entry) && (metaslot_entry->count > 0)) {
rv = pkcs11_mech_parse(metaslot_entry->policylist,
&prov_pol_mechs, metaslot_entry->count);
if (rv == CKR_HOST_MEMORY) {
cryptoerror(LOG_ERR,
"libpkcs11: Could not parse configuration,"
"out of memory. Cannot continue parsing "
"%s.\n", _PATH_PKCS11_CONF);
return (rv);
} else if (rv == CKR_MECHANISM_INVALID) {
cryptoerror(LOG_ERR,
"libpkcs11: Policy invalid or corrupted "
"for metaslot. Use cryptoadm(8) to fix "
"this. Disabling metaslot functionality.\n");
metaslot_enabled = B_FALSE;
return (rv);
}
}
if ((metaslot_entry) && (metaslot_entry->flag_enabledlist) &&
(prov_pol_mechs == NULL)) {
metaslot_enabled = B_FALSE;
return (rv);
}
if ((metaslot_entry) &&
(!metaslot_config.keystore_token_specified) &&
(!metaslot_config.keystore_slot_specified)) {
char blank_str[TOKEN_LABEL_SIZE + SLOT_DESCRIPTION_SIZE];
bzero(blank_str, sizeof (blank_str));
if (memcmp(metaslot_entry->metaslot_ks_token,
blank_str, TOKEN_LABEL_SIZE) != 0) {
metaslot_config.keystore_token_specified = B_TRUE;
(void) strlcpy(
(char *)metaslot_config.keystore_token,
(const char *)metaslot_entry->metaslot_ks_token,
TOKEN_LABEL_SIZE);
}
if (memcmp(metaslot_entry->metaslot_ks_slot,
blank_str, SLOT_DESCRIPTION_SIZE) != 0) {
metaslot_config.keystore_slot_specified = B_TRUE;
(void) strlcpy(
(char *)metaslot_config.keystore_slot,
(const char *)metaslot_entry->metaslot_ks_slot,
SLOT_DESCRIPTION_SIZE);
}
}
if (metaslot_config.auto_key_migrate_specified) {
metaslot_auto_key_migrate = metaslot_config.auto_key_migrate;
} else {
if (metaslot_entry) {
metaslot_auto_key_migrate =
metaslot_entry->flag_metaslot_auto_key_migrate;
} else {
metaslot_auto_key_migrate = B_TRUE;
}
}
slottable->st_first = 0;
slottable->st_slots[0] = NULL;
cur_slot = calloc(1, sizeof (pkcs11_slot_t));
if (cur_slot == NULL) {
rv = CKR_HOST_MEMORY;
return (rv);
}
cur_slot->sl_wfse_state = WFSE_CLEAR;
cur_slot->sl_enabledpol = B_FALSE;
cur_slot->sl_no_wfse = B_FALSE;
(void) pthread_mutex_init(&cur_slot->sl_mutex, NULL);
(void) pthread_mutex_lock(&slottable->st_mutex);
slottable->st_slots[0] = cur_slot;
(void) pthread_mutex_unlock(&slottable->st_mutex);
(void) pthread_mutex_lock(&cur_slot->sl_mutex);
cur_slot->sl_id = METASLOT_SLOTID;
cur_slot->sl_func_list = &metaslot_functionList;
if (metaslot_entry) {
cur_slot->sl_enabledpol = metaslot_entry->flag_enabledlist;
cur_slot->sl_pol_count = metaslot_entry->count;
} else {
cur_slot->sl_enabledpol = B_FALSE;
cur_slot->sl_pol_count = 0;
}
cur_slot->sl_pol_mechs = prov_pol_mechs;
cur_slot->sl_dldesc = NULL;
cur_slot->sl_prov_id = 0;
(void) pthread_mutex_unlock(&cur_slot->sl_mutex);
rv = meta_Initialize(NULL);
if (rv != CKR_OK) {
cryptoerror(LOG_ERR,
"libpkcs11: Can't initialize metaslot (%s)",
pkcs11_strerror(rv));
goto cleanup;
}
return (CKR_OK);
cleanup:
metaslot_enabled = B_FALSE;
slottable->st_slots[0] = NULL;
if (cur_slot) {
(void) pthread_mutex_destroy(&cur_slot->sl_mutex);
free(cur_slot);
}
return (rv);
}
CK_RV
pkcs11_slot_mapping(uentrylist_t *pplist, CK_VOID_PTR pInitArgs)
{
CK_RV rv = CKR_OK;
CK_RV prov_rv;
CK_INFO prov_info;
CK_RV (*Tmp_C_GetFunctionList)(CK_FUNCTION_LIST_PTR_PTR);
CK_FUNCTION_LIST_PTR prov_funcs = NULL;
CK_ULONG prov_slot_count;
CK_SLOT_ID slot_id;
CK_SLOT_ID_PTR prov_slots = NULL;
CK_MECHANISM_TYPE_PTR prov_pol_mechs = NULL;
void *dldesc = NULL;
char *isa, *fullpath = NULL, *dl_error;
uentrylist_t *phead;
uint_t prov_count = 0;
pkcs11_slot_t *cur_slot;
CK_ULONG i;
size_t len;
uentry_t *metaslot_entry = NULL;
uint_t slot_count = 0;
phead = pplist;
while (phead != NULL) {
if (!strcasecmp(phead->puent->name, "metaslot")) {
if (metaslot_entry != NULL) {
cryptoerror(LOG_ERR,
"libpkcs11: multiple entries for metaslot "
"detected. All but the first entry will "
"be ignored");
} else {
metaslot_entry = phead->puent;
}
goto contparse;
}
if (!strcasecmp(phead->puent->name, FIPS_KEYWORD)) {
goto contparse;
}
if ((isa = strstr(phead->puent->name, PKCS11_ISA)) != NULL) {
len = strlen(phead->puent->name) -
strlen(PKCS11_ISA) +
strlen(PKCS11_ISA_DIR) + 1;
if ((fullpath = (char *)malloc(len)) == NULL) {
cryptoerror(LOG_ERR,
"libpksc11: parsing %s, out of memory. "
"Cannot continue parsing.",
_PATH_PKCS11_CONF);
rv = CKR_HOST_MEMORY;
goto conferror;
}
*isa = '\000';
isa += strlen(PKCS11_ISA);
(void) snprintf(fullpath, len, "%s%s%s",
phead->puent->name, PKCS11_ISA_DIR, isa);
} else if ((fullpath = strdup(phead->puent->name)) == 0) {
cryptoerror(LOG_ERR,
"libpkcs11: parsing %s, out of memory. "
"Cannot continue parsing.",
_PATH_PKCS11_CONF);
rv = CKR_HOST_MEMORY;
goto conferror;
}
dldesc = dlopen(fullpath, RTLD_LAZY);
if (dldesc == NULL) {
dl_error = dlerror();
cryptoerror(LOG_ERR,
"libpkcs11: Cannot load PKCS#11 library %s. "
"dlerror: %s. %s",
fullpath, dl_error != NULL ? dl_error : "Unknown",
conf_err);
goto contparse;
}
Tmp_C_GetFunctionList =
(CK_RV(*)())dlsym(dldesc, "C_GetFunctionList");
if (Tmp_C_GetFunctionList == NULL) {
cryptoerror(LOG_ERR,
"libpkcs11: Could not dlsym() C_GetFunctionList() "
"for %s. May not be a PKCS#11 library. %s",
fullpath, conf_err);
(void) dlclose(dldesc);
goto contparse;
}
prov_rv = Tmp_C_GetFunctionList(&prov_funcs);
if (prov_rv != CKR_OK) {
cryptoerror(LOG_ERR,
"libpkcs11: Could not get function list for %s. "
"%s Error: %s.",
fullpath, conf_err, pkcs11_strerror(prov_rv));
(void) dlclose(dldesc);
goto contparse;
}
prov_rv = prov_funcs->C_Initialize(pInitArgs);
if ((prov_rv != CKR_OK) &&
(prov_rv != CKR_CRYPTOKI_ALREADY_INITIALIZED)) {
cryptoerror(LOG_ERR,
"libpkcs11: Could not initialize %s. "
"%s Error: %s.",
fullpath, conf_err, pkcs11_strerror(prov_rv));
(void) dlclose(dldesc);
goto contparse;
}
prov_rv = prov_funcs->C_GetInfo(&prov_info);
if ((prov_rv != CKR_OK) ||
(prov_info.cryptokiVersion.major !=
CRYPTOKI_VERSION_MAJOR)) {
if (prov_rv != CKR_OK) {
cryptoerror(LOG_ERR,
"libpkcs11: Could not verify version of "
"%s. %s Error: %s.", fullpath,
conf_err, pkcs11_strerror(prov_rv));
} else {
cryptoerror(LOG_ERR,
"libpkcs11: Only CRYPTOKI major version "
"%d is supported. %s is major "
"version %d. %s",
CRYPTOKI_VERSION_MAJOR, fullpath,
prov_info.cryptokiVersion.major, conf_err);
}
(void) prov_funcs->C_Finalize(NULL);
(void) dlclose(dldesc);
goto contparse;
}
if ((prov_info.cryptokiVersion.minor <
CRYPTOKI_VERSION_WARN_MINOR) ||
(prov_info.cryptokiVersion.minor >
CRYPTOKI_VERSION_MINOR)) {
cryptoerror(LOG_DEBUG,
"libpkcs11: %s CRYPTOKI minor version, %d, may "
"not be compatible with minor version %d.",
fullpath, prov_info.cryptokiVersion.minor,
CRYPTOKI_VERSION_MINOR);
}
prov_rv = prov_funcs->C_GetSlotList(FALSE,
NULL, &prov_slot_count);
if (prov_rv != CKR_OK) {
cryptoerror(LOG_ERR,
"libpksc11: Could not get slot list from %s. "
"%s Error: %s.",
fullpath, conf_err, pkcs11_strerror(prov_rv));
(void) prov_funcs->C_Finalize(NULL);
(void) dlclose(dldesc);
goto contparse;
}
if (prov_slot_count == 0) {
cryptodebug("libpkcs11: No slots presented from %s. "
"Skipping this plug-in at this time.\n",
fullpath);
(void) prov_funcs->C_Finalize(NULL);
(void) dlclose(dldesc);
goto contparse;
}
prov_slots = calloc(prov_slot_count, sizeof (CK_SLOT_ID));
if (prov_slots == NULL) {
cryptoerror(LOG_ERR,
"libpkcs11: Could not allocate memory for "
"plug-in slots. Cannot continue parsing %s\n",
_PATH_PKCS11_CONF);
rv = CKR_HOST_MEMORY;
goto conferror;
}
prov_rv = prov_funcs->C_GetSlotList(FALSE,
prov_slots, &prov_slot_count);
if (prov_rv != CKR_OK) {
cryptoerror(LOG_ERR,
"libpkcs11: Second call to C_GetSlotList() for %s "
"failed. %s Error: %s.",
fullpath, conf_err, pkcs11_strerror(prov_rv));
(void) prov_funcs->C_Finalize(NULL);
(void) dlclose(dldesc);
goto contparse;
}
if (phead->puent->count > 0) {
rv = pkcs11_mech_parse(phead->puent->policylist,
&prov_pol_mechs, phead->puent->count);
if (rv == CKR_HOST_MEMORY) {
cryptoerror(LOG_ERR,
"libpkcs11: Could not parse configuration,"
"out of memory. Cannot continue parsing "
"%s.", _PATH_PKCS11_CONF);
goto conferror;
} else if (rv == CKR_MECHANISM_INVALID) {
cryptoerror(LOG_ERR,
"libpkcs11: Policy invalid or corrupted "
"for %s. Use cryptoadm(8) to fix "
"this. Skipping this plug-in.",
fullpath);
(void) prov_funcs->C_Finalize(NULL);
(void) dlclose(dldesc);
goto contparse;
}
}
rv = pkcs11_slottable_increase(prov_slot_count);
if (rv != CKR_OK) {
cryptoerror(LOG_ERR,
"libpkcs11: slottable could not increase. "
"Cannot continue parsing %s.",
_PATH_PKCS11_CONF);
goto conferror;
}
for (i = 0; i < prov_slot_count; i++) {
rv = pkcs11_slot_allocate(&slot_id);
if (rv != CKR_OK) {
cryptoerror(LOG_ERR,
"libpkcs11: Could not allocate "
"new slot. Cannot continue parsing %s.",
_PATH_PKCS11_CONF);
goto conferror;
}
slot_count++;
cur_slot = slottable->st_slots[slot_id];
(void) pthread_mutex_lock(&cur_slot->sl_mutex);
cur_slot->sl_id = prov_slots[i];
cur_slot->sl_func_list = prov_funcs;
cur_slot->sl_enabledpol =
phead->puent->flag_enabledlist;
cur_slot->sl_pol_mechs = prov_pol_mechs;
cur_slot->sl_pol_count = phead->puent->count;
cur_slot->sl_norandom = phead->puent->flag_norandom;
cur_slot->sl_dldesc = dldesc;
cur_slot->sl_prov_id = prov_count + 1;
(void) pthread_mutex_unlock(&cur_slot->sl_mutex);
}
if (Tmp_GetThreshold == NULL) {
Tmp_GetThreshold =
(void(*)())dlsym(dldesc, "_SUNW_GetThreshold");
if (Tmp_GetThreshold != NULL) {
(void) memset(meta_mechs_threshold, 0,
sizeof (meta_mechs_threshold));
Tmp_GetThreshold(meta_mechs_threshold);
}
}
prov_count++;
contparse:
prov_slot_count = 0;
Tmp_C_GetFunctionList = NULL;
prov_funcs = NULL;
dldesc = NULL;
if (fullpath != NULL) {
free(fullpath);
fullpath = NULL;
}
if (prov_slots != NULL) {
free(prov_slots);
prov_slots = NULL;
}
phead = phead->next;
}
if (slot_count == 0) {
goto config_complete;
}
get_user_metaslot_config();
if (!metaslot_entry) {
if ((metaslot_config.enabled_specified) &&
(metaslot_config.enabled)) {
metaslot_enabled = B_TRUE;
}
} else {
if (!metaslot_config.enabled_specified) {
metaslot_enabled
= metaslot_entry->flag_metaslot_enabled;
} else {
metaslot_enabled = metaslot_config.enabled;
}
}
if (slottable->st_last == slottable->st_first) {
cur_slot = slottable->st_slots[slottable->st_first];
(void) pthread_mutex_lock(&cur_slot->sl_mutex);
if ((cur_slot->sl_pol_count == 0) &&
(!cur_slot->sl_enabledpol) && (!cur_slot->sl_norandom)) {
fast_funcs = cur_slot->sl_func_list;
purefastpath = B_TRUE;
} else {
fast_funcs = cur_slot->sl_func_list;
fast_slot = slottable->st_first;
policyfastpath = B_TRUE;
}
(void) pthread_mutex_unlock(&cur_slot->sl_mutex);
}
if ((purefastpath || policyfastpath) && (!metaslot_enabled)) {
goto config_complete;
}
if (metaslot_enabled) {
rv = setup_metaslot(metaslot_entry);
if (rv != CKR_OK) {
goto conferror;
}
}
config_complete:
return (CKR_OK);
conferror:
if (prov_funcs != NULL) {
(void) prov_funcs->C_Finalize(NULL);
}
if (dldesc != NULL) {
(void) dlclose(dldesc);
}
if (fullpath != NULL) {
free(fullpath);
fullpath = NULL;
}
if (prov_slots != NULL) {
free(prov_slots);
prov_slots = NULL;
}
return (rv);
}
CK_RV
pkcs11_mech_parse(umechlist_t *str_list, CK_MECHANISM_TYPE_PTR *mech_list,
int mech_count)
{
CK_MECHANISM_TYPE_PTR tmp_list;
umechlist_t *shead = str_list;
tmp_list = malloc(mech_count * sizeof (CK_MECHANISM_TYPE));
if (tmp_list == NULL) {
cryptoerror(LOG_ERR, "libpkcs11: parsing %s, out of memory. "
"Cannot continue.",
_PATH_PKCS11_CONF);
return (CKR_HOST_MEMORY);
}
*mech_list = tmp_list;
while (shead != NULL) {
CK_MECHANISM_TYPE cur_mech;
errno = 0;
cur_mech = strtoul(shead->name, NULL, 16);
if ((cur_mech == 0) &&
((errno == EINVAL) || (errno == ERANGE))) {
free(mech_list);
return (CKR_MECHANISM_INVALID);
}
*tmp_list = (CK_MECHANISM_TYPE)cur_mech;
tmp_list++;
shead = shead->next;
}
return (CKR_OK);
}
boolean_t
pkcs11_is_dismech(CK_SLOT_ID slotid, CK_MECHANISM_TYPE mech)
{
ulong_t i;
boolean_t enabled_pol;
CK_MECHANISM_TYPE_PTR pol_mechs;
ulong_t pol_count;
(void) pthread_mutex_lock(&slottable->st_slots[slotid]->sl_mutex);
enabled_pol = slottable->st_slots[slotid]->sl_enabledpol;
pol_mechs = slottable->st_slots[slotid]->sl_pol_mechs;
pol_count = slottable->st_slots[slotid]->sl_pol_count;
(void) pthread_mutex_unlock(&slottable->st_slots[slotid]->sl_mutex);
if ((!enabled_pol) && (pol_mechs == NULL)) {
return (B_FALSE);
} else if (pol_mechs == NULL) {
return (B_TRUE);
}
for (i = 0; i < pol_count; i++) {
if (pol_mechs[i] == mech) {
return (enabled_pol ? B_FALSE : B_TRUE);
}
}
return (enabled_pol ? B_TRUE : B_FALSE);
}