#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <errno.h>
#include <security/cryptoki.h>
#include <cryptoutil.h>
#include "kernelGlobal.h"
#include "kernelObject.h"
#include "kernelSession.h"
#include "kernelSlot.h"
void
kernel_add_object_to_session(kernel_object_t *objp, kernel_session_t *sp)
{
(void) pthread_mutex_lock(&sp->session_mutex);
if (sp->object_list == NULL) {
sp->object_list = objp;
objp->next = NULL;
objp->prev = NULL;
} else {
sp->object_list->prev = objp;
objp->next = sp->object_list;
objp->prev = NULL;
sp->object_list = objp;
}
(void) pthread_mutex_unlock(&sp->session_mutex);
}
void
kernel_cleanup_object(kernel_object_t *objp)
{
if (objp->class == CKO_SECRET_KEY) {
if (OBJ_SEC(objp) != NULL && OBJ_SEC_VALUE(objp) != NULL) {
freezero(OBJ_SEC_VALUE(objp), OBJ_SEC_VALUE_LEN(objp));
OBJ_SEC_VALUE(objp) = NULL;
OBJ_SEC_VALUE_LEN(objp) = 0;
}
free(OBJ_SEC(objp));
OBJ_SEC(objp) = NULL;
} else {
kernel_cleanup_object_bigint_attrs(objp);
}
kernel_cleanup_extra_attr(objp);
}
CK_RV
kernel_copy_object(kernel_object_t *old_object, kernel_object_t **new_object,
boolean_t copy_everything, kernel_session_t *sp)
{
CK_RV rv = CKR_OK;
kernel_object_t *new_objp = NULL;
CK_ATTRIBUTE_INFO_PTR attrp;
new_objp = calloc(1, sizeof (kernel_object_t));
if (new_objp == NULL)
return (CKR_HOST_MEMORY);
new_objp->class = old_object->class;
new_objp->bool_attr_mask = old_object->bool_attr_mask;
attrp = old_object->extra_attrlistp;
while (attrp) {
rv = kernel_copy_extra_attr(attrp, new_objp);
if (rv != CKR_OK) {
kernel_cleanup_extra_attr(new_objp);
free(new_objp);
return (rv);
}
attrp = attrp->next;
}
*new_object = new_objp;
if (!copy_everything) {
return (CKR_OK);
}
new_objp->key_type = old_object->key_type;
new_objp->magic_marker = old_object->magic_marker;
new_objp->mechanism = old_object->mechanism;
new_objp->session_handle = (CK_SESSION_HANDLE)sp;
(void) pthread_mutex_init(&(new_objp->object_mutex), NULL);
switch (new_objp->class) {
case CKO_PUBLIC_KEY:
rv = kernel_copy_public_key_attr(OBJ_PUB(old_object),
&(OBJ_PUB(new_objp)), new_objp->key_type);
break;
case CKO_PRIVATE_KEY:
rv = kernel_copy_private_key_attr(OBJ_PRI(old_object),
&(OBJ_PRI(new_objp)), new_objp->key_type);
break;
case CKO_SECRET_KEY:
rv = kernel_copy_secret_key_attr(OBJ_SEC(old_object),
&(OBJ_SEC(new_objp)));
break;
default:
break;
}
if (rv != CKR_OK) {
kernel_cleanup_extra_attr(new_objp);
free(new_objp);
}
return (rv);
}
void
kernel_merge_object(kernel_object_t *old_object, kernel_object_t *new_object)
{
old_object->bool_attr_mask = new_object->bool_attr_mask;
kernel_cleanup_extra_attr(old_object);
old_object->extra_attrlistp = new_object->extra_attrlistp;
}
CK_RV
kernel_add_object(CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount,
CK_ULONG *objecthandle_p, kernel_session_t *sp)
{
CK_RV rv = CKR_OK;
kernel_object_t *new_objp = NULL;
kernel_slot_t *pslot;
crypto_object_create_t objc;
CK_BBOOL is_pri_obj;
CK_BBOOL is_token_obj = B_FALSE;
int r;
new_objp = calloc(1, sizeof (kernel_object_t));
if (new_objp == NULL) {
rv = CKR_HOST_MEMORY;
goto fail_cleanup;
}
new_objp->extra_attrlistp = NULL;
new_objp->is_lib_obj = B_TRUE;
pslot = slot_table[sp->ses_slotid];
if (pslot->sl_func_list.fl_object_create) {
new_objp->is_lib_obj = B_FALSE;
objc.oc_session = sp->k_session;
objc.oc_count = ulCount;
rv = process_object_attributes(pTemplate, ulCount,
&objc.oc_attributes, &is_token_obj);
if (rv != CKR_OK) {
goto fail_cleanup;
}
if (is_token_obj && sp->ses_RO) {
free_object_attributes(objc.oc_attributes, ulCount);
rv = CKR_SESSION_READ_ONLY;
goto fail_cleanup;
}
while ((r = ioctl(kernel_fd, CRYPTO_OBJECT_CREATE,
&objc)) < 0) {
if (errno != EINTR)
break;
}
if (r < 0) {
rv = CKR_FUNCTION_FAILED;
} else {
rv = crypto2pkcs11_error_number(objc.oc_return_value);
}
free_object_attributes(objc.oc_attributes, ulCount);
if (rv != CKR_OK) {
goto fail_cleanup;
}
new_objp->k_handle = objc.oc_handle;
rv = get_cka_private_value(sp, new_objp->k_handle,
&is_pri_obj);
if (rv != CKR_OK) {
goto fail_cleanup;
}
if (is_pri_obj)
new_objp->bool_attr_mask |= PRIVATE_BOOL_ON;
else
new_objp->bool_attr_mask &= ~PRIVATE_BOOL_ON;
if (is_token_obj)
new_objp->bool_attr_mask |= TOKEN_BOOL_ON;
else
new_objp->bool_attr_mask &= ~TOKEN_BOOL_ON;
} else {
rv = kernel_build_object(pTemplate, ulCount, new_objp, sp,
KERNEL_CREATE_OBJ);
if (rv != CKR_OK) {
goto fail_cleanup;
}
}
(void) pthread_mutex_init(&new_objp->object_mutex, NULL);
new_objp->magic_marker = KERNELTOKEN_OBJECT_MAGIC;
new_objp->session_handle = (CK_SESSION_HANDLE)sp;
if (is_token_obj) {
pslot = slot_table[sp->ses_slotid];
kernel_add_token_object_to_slot(new_objp, pslot);
} else {
kernel_add_object_to_session(new_objp, sp);
}
*objecthandle_p = (CK_ULONG)new_objp;
return (CKR_OK);
fail_cleanup:
if (new_objp) {
free(new_objp);
}
return (rv);
}
CK_RV
kernel_remove_object_from_session(kernel_object_t *objp, kernel_session_t *sp)
{
kernel_object_t *tmp_objp;
boolean_t found = B_FALSE;
if ((sp == NULL) ||
(sp->magic_marker != KERNELTOKEN_SESSION_MAGIC)) {
return (CKR_SESSION_HANDLE_INVALID);
}
if ((sp->object_list == NULL) || (objp == NULL) ||
(objp->magic_marker != KERNELTOKEN_OBJECT_MAGIC)) {
return (CKR_OBJECT_HANDLE_INVALID);
}
tmp_objp = sp->object_list;
while (tmp_objp) {
if (tmp_objp == objp) {
found = B_TRUE;
break;
}
tmp_objp = tmp_objp->next;
}
if (!found)
return (CKR_OBJECT_HANDLE_INVALID);
if (sp->object_list == objp) {
if (objp->next) {
sp->object_list = objp->next;
objp->next->prev = NULL;
} else {
sp->object_list = NULL;
}
} else {
if (objp->next) {
objp->prev->next = objp->next;
objp->next->prev = objp->prev;
} else {
objp->prev->next = NULL;
}
}
return (CKR_OK);
}
static void
kernel_delete_object_cleanup(kernel_object_t *objp, boolean_t wrapper_only)
{
(void) pthread_mutex_lock(&objp->object_mutex);
if (objp->magic_marker != KERNELTOKEN_OBJECT_MAGIC) {
(void) pthread_mutex_unlock(&objp->object_mutex);
return;
}
if (wrapper_only) {
objp->obj_refcnt = 0;
}
while (objp->obj_refcnt != 0) {
objp->obj_delete_sync |= OBJECT_REFCNT_WAITING;
(void) pthread_cond_wait(&objp->obj_free_cond,
&objp->object_mutex);
}
objp->obj_delete_sync &= ~OBJECT_REFCNT_WAITING;
objp->magic_marker = 0;
(void) pthread_cond_destroy(&objp->obj_free_cond);
}
CK_RV
kernel_delete_session_object(kernel_session_t *sp, kernel_object_t *objp,
boolean_t ses_lock_held, boolean_t wrapper_only)
{
CK_RV rv = CKR_OK;
crypto_object_destroy_t obj_destroy;
if (!ses_lock_held) {
(void) pthread_mutex_lock(&sp->session_mutex);
}
rv = kernel_remove_object_from_session(objp, sp);
if (!ses_lock_held) {
(void) pthread_mutex_unlock(&sp->session_mutex);
}
if (rv != CKR_OK)
return (rv);
kernel_delete_object_cleanup(objp, wrapper_only);
if (objp->is_lib_obj) {
kernel_cleanup_object(objp);
} else {
if (!wrapper_only) {
obj_destroy.od_session = sp->k_session;
obj_destroy.od_handle = objp->k_handle;
while (ioctl(kernel_fd, CRYPTO_OBJECT_DESTROY,
&obj_destroy) < 0) {
if (errno != EINTR)
break;
}
}
}
objp->obj_delete_sync &= ~OBJECT_IS_DELETING;
(void) pthread_mutex_unlock(&objp->object_mutex);
(void) pthread_mutex_destroy(&objp->object_mutex);
kernel_object_delay_free(objp);
return (CKR_OK);
}
void
kernel_delete_all_objects_in_session(kernel_session_t *sp,
boolean_t wrapper_only)
{
kernel_object_t *objp = sp->object_list;
kernel_object_t *objp1;
while (objp) {
objp1 = objp->next;
(void) kernel_delete_session_object(sp, objp, B_TRUE,
wrapper_only);
objp = objp1;
}
}
static CK_RV
add_to_search_result(kernel_object_t *obj, find_context_t *fcontext,
CK_ULONG *num_result_alloc)
{
if (*num_result_alloc <= fcontext->num_results) {
fcontext->objs_found = realloc(fcontext->objs_found,
sizeof (kernel_object_t *) * (*num_result_alloc + BUFSIZ));
if (fcontext->objs_found == NULL) {
return (CKR_HOST_MEMORY);
}
*num_result_alloc += BUFSIZ;
}
(fcontext->objs_found)[(fcontext->num_results)++] = obj;
return (CKR_OK);
}
static CK_RV
search_for_objects(kernel_session_t *sp, CK_ATTRIBUTE_PTR pTemplate,
CK_ULONG ulCount, find_context_t *fcontext)
{
kernel_session_t *session_p;
kernel_object_t *obj;
CK_OBJECT_CLASS pclasses[6];
CK_ULONG num_pclasses;
CK_ULONG num_result_alloc = 0;
CK_RV rv = CKR_OK;
kernel_slot_t *pslot;
if (ulCount > 0) {
kernel_process_find_attr(pclasses, &num_pclasses,
pTemplate, ulCount);
}
pslot = slot_table[sp->ses_slotid];
(void) pthread_mutex_lock(&pslot->sl_mutex);
session_p = pslot->sl_sess_list;
while (session_p) {
(void) pthread_mutex_lock(&session_p->session_mutex);
obj = session_p->object_list;
while (obj) {
(void) pthread_mutex_lock(&obj->object_mutex);
if (ulCount > 0) {
if (kernel_find_match_attrs(obj, pclasses,
num_pclasses, pTemplate, ulCount)) {
rv = add_to_search_result(
obj, fcontext, &num_result_alloc);
}
} else {
rv = add_to_search_result(obj, fcontext,
&num_result_alloc);
}
(void) pthread_mutex_unlock(&obj->object_mutex);
if (rv != CKR_OK) {
(void) pthread_mutex_unlock(
&session_p->session_mutex);
goto cleanup;
}
obj = obj->next;
}
(void) pthread_mutex_unlock(&session_p->session_mutex);
session_p = session_p->next;
}
cleanup:
(void) pthread_mutex_unlock(&pslot->sl_mutex);
return (rv);
}
CK_RV
kernel_find_objects_init(kernel_session_t *sp, CK_ATTRIBUTE_PTR pTemplate,
CK_ULONG ulCount)
{
CK_RV rv = CKR_OK;
CK_OBJECT_CLASS class;
find_context_t *fcontext;
if (ulCount) {
rv = kernel_validate_attr(pTemplate, ulCount, &class);
if (rv != CKR_OK) {
return (rv);
}
}
fcontext = calloc(1, sizeof (find_context_t));
if (fcontext == NULL) {
return (CKR_HOST_MEMORY);
}
rv = search_for_objects(sp, pTemplate, ulCount, fcontext);
if (rv != CKR_OK) {
free(fcontext);
return (rv);
}
sp->find_objects.context = (CK_VOID_PTR)fcontext;
return (rv);
}
void
kernel_find_objects_final(kernel_session_t *sp)
{
find_context_t *fcontext;
fcontext = sp->find_objects.context;
sp->find_objects.context = NULL;
sp->find_objects.flags = 0;
if (fcontext->objs_found != NULL) {
free(fcontext->objs_found);
}
free(fcontext);
}
void
kernel_find_objects(kernel_session_t *sp, CK_OBJECT_HANDLE *obj_found,
CK_ULONG max_obj_requested, CK_ULONG *found_obj_count)
{
find_context_t *fcontext;
CK_ULONG num_obj_found = 0;
CK_ULONG i;
kernel_object_t *obj;
fcontext = sp->find_objects.context;
for (i = fcontext->next_result_index;
((num_obj_found < max_obj_requested) &&
(i < fcontext->num_results));
i++) {
obj = fcontext->objs_found[i];
if (obj != NULL) {
(void) pthread_mutex_lock(&obj->object_mutex);
if (obj->magic_marker == KERNELTOKEN_OBJECT_MAGIC) {
obj_found[num_obj_found] =
(CK_OBJECT_HANDLE)obj;
num_obj_found++;
}
(void) pthread_mutex_unlock(&obj->object_mutex);
}
}
fcontext->next_result_index = i;
*found_obj_count = num_obj_found;
}
void
kernel_add_token_object_to_slot(kernel_object_t *objp, kernel_slot_t *pslot)
{
(void) pthread_mutex_lock(&pslot->sl_mutex);
if (pslot->sl_tobj_list == NULL) {
pslot->sl_tobj_list = objp;
objp->next = NULL;
objp->prev = NULL;
} else {
pslot->sl_tobj_list->prev = objp;
objp->next = pslot->sl_tobj_list;
objp->prev = NULL;
pslot->sl_tobj_list = objp;
}
(void) pthread_mutex_unlock(&pslot->sl_mutex);
}
void
kernel_remove_token_object_from_slot(kernel_slot_t *pslot,
kernel_object_t *objp)
{
if (pslot->sl_tobj_list == objp) {
if (objp->next) {
pslot->sl_tobj_list = objp->next;
objp->next->prev = NULL;
} else {
pslot->sl_tobj_list = NULL;
}
} else {
if (objp->next) {
objp->prev->next = objp->next;
objp->next->prev = objp->prev;
} else {
objp->prev->next = NULL;
}
}
}
CK_RV
kernel_delete_token_object(kernel_slot_t *pslot, kernel_session_t *sp,
kernel_object_t *objp, boolean_t slot_lock_held, boolean_t wrapper_only)
{
CK_RV rv;
crypto_object_destroy_t obj_destroy;
int r;
if (!slot_lock_held) {
(void) pthread_mutex_lock(&pslot->sl_mutex);
}
kernel_remove_token_object_from_slot(pslot, objp);
if (!slot_lock_held) {
(void) pthread_mutex_unlock(&pslot->sl_mutex);
}
kernel_delete_object_cleanup(objp, wrapper_only);
if (!wrapper_only) {
obj_destroy.od_session = sp->k_session;
obj_destroy.od_handle = objp->k_handle;
while ((r = ioctl(kernel_fd, CRYPTO_OBJECT_DESTROY,
&obj_destroy)) < 0) {
if (errno != EINTR)
break;
}
if (r < 0) {
rv = CKR_FUNCTION_FAILED;
} else {
rv = crypto2pkcs11_error_number(
obj_destroy.od_return_value);
}
if (rv != CKR_OK) {
cryptoerror(LOG_ERR, "pkcs11_kernel: Could not "
"destroy an object in kernel.");
}
}
(void) pthread_mutex_unlock(&objp->object_mutex);
(void) pthread_mutex_destroy(&objp->object_mutex);
kernel_object_delay_free(objp);
return (CKR_OK);
}
void
kernel_cleanup_pri_objects_in_slot(kernel_slot_t *pslot,
kernel_session_t *cur_sp)
{
kernel_session_t *session_p;
kernel_object_t *objp;
kernel_object_t *objp1;
objp = pslot->sl_tobj_list;
while (objp) {
objp1 = objp->next;
if (objp->bool_attr_mask & PRIVATE_BOOL_ON) {
(void) kernel_delete_token_object(pslot, cur_sp, objp,
B_TRUE, B_TRUE);
}
objp = objp1;
}
session_p = pslot->sl_sess_list;
while (session_p) {
objp = session_p->object_list;
while (objp) {
objp1 = objp->next;
if (objp->bool_attr_mask & PRIVATE_BOOL_ON) {
(void) kernel_delete_session_object(session_p,
objp, B_FALSE, B_TRUE);
}
objp = objp1;
}
session_p = session_p->next;
}
}
CK_RV
kernel_get_object_size(kernel_object_t *obj, CK_ULONG_PTR pulSize)
{
CK_RV rv = CKR_OK;
CK_ULONG obj_size;
biginteger_t *big;
obj_size = sizeof (kernel_object_t);
switch (obj->class) {
case CKO_PUBLIC_KEY:
if (obj->key_type == CKK_RSA) {
big = OBJ_PUB_RSA_PUBEXPO(obj);
obj_size += big->big_value_len;
big = OBJ_PUB_RSA_MOD(obj);
obj_size += big->big_value_len;
} else if (obj->key_type == CKK_DSA) {
big = OBJ_PUB_DSA_PRIME(obj);
obj_size += big->big_value_len;
big = OBJ_PUB_DSA_SUBPRIME(obj);
obj_size += big->big_value_len;
big = OBJ_PUB_DSA_BASE(obj);
obj_size += big->big_value_len;
big = OBJ_PUB_DSA_VALUE(obj);
obj_size += big->big_value_len;
} else if (obj->key_type == CKK_EC) {
big = OBJ_PUB_EC_POINT(obj);
obj_size += big->big_value_len;
} else {
rv = CKR_OBJECT_HANDLE_INVALID;
}
break;
case CKO_PRIVATE_KEY:
if (obj->key_type == CKK_RSA) {
big = OBJ_PRI_RSA_MOD(obj);
obj_size += big->big_value_len;
big = OBJ_PRI_RSA_PUBEXPO(obj);
if (big != NULL) {
obj_size += big->big_value_len;
}
big = OBJ_PRI_RSA_PRIEXPO(obj);
obj_size += big->big_value_len;
big = OBJ_PRI_RSA_PRIME1(obj);
if (big != NULL) {
obj_size += big->big_value_len;
}
big = OBJ_PRI_RSA_PRIME2(obj);
if (big != NULL) {
obj_size += big->big_value_len;
}
big = OBJ_PRI_RSA_EXPO1(obj);
if (big != NULL) {
obj_size += big->big_value_len;
}
big = OBJ_PRI_RSA_EXPO2(obj);
if (big != NULL) {
obj_size += big->big_value_len;
}
big = OBJ_PRI_RSA_COEF(obj);
if (big != NULL) {
obj_size += big->big_value_len;
}
} else if (obj->key_type == CKK_DSA) {
big = OBJ_PRI_DSA_PRIME(obj);
obj_size += big->big_value_len;
big = OBJ_PRI_DSA_SUBPRIME(obj);
obj_size += big->big_value_len;
big = OBJ_PRI_DSA_BASE(obj);
obj_size += big->big_value_len;
big = OBJ_PRI_DSA_VALUE(obj);
obj_size += big->big_value_len;
} else if (obj->key_type == CKK_EC) {
big = OBJ_PRI_EC_VALUE(obj);
obj_size += big->big_value_len;
} else {
rv = CKR_OBJECT_HANDLE_INVALID;
}
break;
case CKO_SECRET_KEY:
obj_size += OBJ_SEC_VALUE_LEN(obj);
break;
default:
rv = CKR_OBJECT_HANDLE_INVALID;
}
if (rv == CKR_OK) {
*pulSize = obj_size;
}
return (rv);
}
void
kernel_object_delay_free(kernel_object_t *objp)
{
kernel_object_t *tmp;
(void) pthread_mutex_lock(&obj_delay_freed.obj_to_be_free_mutex);
objp->next = NULL;
if (obj_delay_freed.first == NULL) {
obj_delay_freed.last = objp;
obj_delay_freed.first = objp;
} else {
obj_delay_freed.last->next = objp;
obj_delay_freed.last = objp;
}
if (++obj_delay_freed.count >= MAX_OBJ_TO_BE_FREED) {
obj_delay_freed.count--;
tmp = obj_delay_freed.first->next;
free(obj_delay_freed.first);
obj_delay_freed.first = tmp;
}
(void) pthread_mutex_unlock(&obj_delay_freed.obj_to_be_free_mutex);
}