2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include <fcntl.h>
2N/A#include <pthread.h>
2N/A#include <strings.h>
2N/A#include <unistd.h> /* for pid */
2N/A#include <errno.h>
2N/A#include <security/cryptoki.h>
2N/A#include "kernelGlobal.h"
2N/A#include "kernelSession.h"
2N/A#include "kernelSlot.h"
2N/A
2N/A#pragma init(kernel_init)
2N/A#pragma fini(kernel_fini)
2N/A
2N/Astatic struct CK_FUNCTION_LIST functionList = {
2N/A { 2, 20 }, /* version */
2N/A C_Initialize,
2N/A C_Finalize,
2N/A C_GetInfo,
2N/A C_GetFunctionList,
2N/A C_GetSlotList,
2N/A C_GetSlotInfo,
2N/A C_GetTokenInfo,
2N/A C_GetMechanismList,
2N/A C_GetMechanismInfo,
2N/A C_InitToken,
2N/A C_InitPIN,
2N/A C_SetPIN,
2N/A C_OpenSession,
2N/A C_CloseSession,
2N/A C_CloseAllSessions,
2N/A C_GetSessionInfo,
2N/A C_GetOperationState,
2N/A C_SetOperationState,
2N/A C_Login,
2N/A C_Logout,
2N/A C_CreateObject,
2N/A C_CopyObject,
2N/A C_DestroyObject,
2N/A C_GetObjectSize,
2N/A C_GetAttributeValue,
2N/A C_SetAttributeValue,
2N/A C_FindObjectsInit,
2N/A C_FindObjects,
2N/A C_FindObjectsFinal,
2N/A C_EncryptInit,
2N/A C_Encrypt,
2N/A C_EncryptUpdate,
2N/A C_EncryptFinal,
2N/A C_DecryptInit,
2N/A C_Decrypt,
2N/A C_DecryptUpdate,
2N/A C_DecryptFinal,
2N/A C_DigestInit,
2N/A C_Digest,
2N/A C_DigestUpdate,
2N/A C_DigestKey,
2N/A C_DigestFinal,
2N/A C_SignInit,
2N/A C_Sign,
2N/A C_SignUpdate,
2N/A C_SignFinal,
2N/A C_SignRecoverInit,
2N/A C_SignRecover,
2N/A C_VerifyInit,
2N/A C_Verify,
2N/A C_VerifyUpdate,
2N/A C_VerifyFinal,
2N/A C_VerifyRecoverInit,
2N/A C_VerifyRecover,
2N/A C_DigestEncryptUpdate,
2N/A C_DecryptDigestUpdate,
2N/A C_SignEncryptUpdate,
2N/A C_DecryptVerifyUpdate,
2N/A C_GenerateKey,
2N/A C_GenerateKeyPair,
2N/A C_WrapKey,
2N/A C_UnwrapKey,
2N/A C_DeriveKey,
2N/A C_SeedRandom,
2N/A C_GenerateRandom,
2N/A C_GetFunctionStatus,
2N/A C_CancelFunction,
2N/A C_WaitForSlotEvent
2N/A};
2N/A
2N/Aboolean_t kernel_initialized = B_FALSE;
2N/Astatic pid_t kernel_pid = 0;
2N/A
2N/Aextern pthread_mutex_t mechhash_mutex;
2N/A
2N/Aint kernel_fd = -1;
2N/A
2N/A
2N/A/* protects kernel_initialized and entrance to C_Initialize/Finalize */
2N/Astatic pthread_mutex_t globalmutex = PTHREAD_MUTEX_INITIALIZER;
2N/A
2N/Ases_to_be_freed_list_t ses_delay_freed;
2N/Aobject_to_be_freed_list_t obj_delay_freed;
2N/Akmh_elem_t **kernel_mechhash; /* Hash table for kCF mech numbers */
2N/A
2N/Astatic void finalize_common(void);
2N/Astatic void cleanup_library(void);
2N/Astatic void kernel_init(void);
2N/Astatic void kernel_fini(void);
2N/Astatic void kernel_fork_prepare(void);
2N/Astatic void kernel_fork_after(void);
2N/A
2N/ACK_RV
2N/AC_Initialize(CK_VOID_PTR pInitArgs)
2N/A{
2N/A int initialize_pid;
2N/A boolean_t supplied_ok;
2N/A CK_RV rv = CKR_OK;
2N/A
2N/A /*
2N/A * Grab lock to insure that only one thread enters this
2N/A * function at a time.
2N/A */
2N/A (void) pthread_mutex_lock(&globalmutex);
2N/A initialize_pid = getpid();
2N/A
2N/A if (kernel_initialized) {
2N/A if (initialize_pid == kernel_pid) {
2N/A /*
2N/A * This process has called C_Initialize already
2N/A */
2N/A (void) pthread_mutex_unlock(&globalmutex);
2N/A return (CKR_CRYPTOKI_ALREADY_INITIALIZED);
2N/A } else {
2N/A /*
2N/A * A fork has happened and the child is
2N/A * reinitializing. Do a cleanup_library to close
2N/A * out any state from the parent, and then
2N/A * continue on.
2N/A */
2N/A cleanup_library();
2N/A }
2N/A }
2N/A
2N/A if (pInitArgs != NULL) {
2N/A CK_C_INITIALIZE_ARGS *initargs1 =
2N/A (CK_C_INITIALIZE_ARGS *) pInitArgs;
2N/A
2N/A /* pReserved must be NULL */
2N/A if (initargs1->pReserved != NULL) {
2N/A (void) pthread_mutex_unlock(&globalmutex);
2N/A return (CKR_ARGUMENTS_BAD);
2N/A }
2N/A
2N/A /*
2N/A * ALL supplied function pointers need to have the value
2N/A * either NULL or non-NULL.
2N/A */
2N/A supplied_ok = (initargs1->CreateMutex == NULL &&
2N/A initargs1->DestroyMutex == NULL &&
2N/A initargs1->LockMutex == NULL &&
2N/A initargs1->UnlockMutex == NULL) ||
2N/A (initargs1->CreateMutex != NULL &&
2N/A initargs1->DestroyMutex != NULL &&
2N/A initargs1->LockMutex != NULL &&
2N/A initargs1->UnlockMutex != NULL);
2N/A
2N/A if (!supplied_ok) {
2N/A (void) pthread_mutex_unlock(&globalmutex);
2N/A return (CKR_ARGUMENTS_BAD);
2N/A }
2N/A
2N/A /*
2N/A * When the CKF_OS_LOCKING_OK flag isn't set and mutex
2N/A * function pointers are supplied by an application,
2N/A * return an error. We must be able to use our own locks.
2N/A */
2N/A if (!(initargs1->flags & CKF_OS_LOCKING_OK) &&
2N/A (initargs1->CreateMutex != NULL)) {
2N/A (void) pthread_mutex_unlock(&globalmutex);
2N/A return (CKR_CANT_LOCK);
2N/A }
2N/A }
2N/A
2N/A while ((kernel_fd = open(CRYPTO_DEVICE, O_RDWR)) < 0) {
2N/A if (errno != EINTR)
2N/A break;
2N/A }
2N/A if (kernel_fd < 0) {
2N/A (void) pthread_mutex_unlock(&globalmutex);
2N/A return (CKR_FUNCTION_FAILED);
2N/A }
2N/A
2N/A /* Mark kernel_fd "close on exec" */
2N/A (void) fcntl(kernel_fd, F_SETFD, FD_CLOEXEC);
2N/A
2N/A /* Create the hash table */
2N/A kernel_mechhash = calloc(KMECH_HASHTABLE_SIZE, sizeof (void *));
2N/A if (kernel_mechhash == NULL) {
2N/A (void) close(kernel_fd);
2N/A (void) pthread_mutex_unlock(&globalmutex);
2N/A return (CKR_HOST_MEMORY);
2N/A }
2N/A
2N/A /* Initialize the slot table */
2N/A rv = kernel_slottable_init();
2N/A if (rv != CKR_OK) {
2N/A free(kernel_mechhash);
2N/A (void) close(kernel_fd);
2N/A (void) pthread_mutex_unlock(&globalmutex);
2N/A return (rv);
2N/A }
2N/A
2N/A /* Initialize the object_to_be_freed list */
2N/A (void) pthread_mutex_init(&obj_delay_freed.obj_to_be_free_mutex, NULL);
2N/A obj_delay_freed.count = 0;
2N/A obj_delay_freed.first = NULL;
2N/A obj_delay_freed.last = NULL;
2N/A
2N/A /* Initialize the session_to_be_freed list */
2N/A (void) pthread_mutex_init(&ses_delay_freed.ses_to_be_free_mutex, NULL);
2N/A ses_delay_freed.count = 0;
2N/A ses_delay_freed.first = NULL;
2N/A ses_delay_freed.last = NULL;
2N/A
2N/A kernel_initialized = B_TRUE;
2N/A kernel_pid = initialize_pid;
2N/A
2N/A (void) pthread_mutex_unlock(&globalmutex);
2N/A
2N/A return (CKR_OK);
2N/A
2N/A}
2N/A
2N/A
2N/A/*
2N/A * C_Finalize is a wrapper around finalize_common. The
2N/A * globalmutex should be locked by C_Finalize().
2N/A */
2N/ACK_RV
2N/AC_Finalize(CK_VOID_PTR pReserved)
2N/A{
2N/A int i;
2N/A
2N/A (void) pthread_mutex_lock(&globalmutex);
2N/A
2N/A if (!kernel_initialized) {
2N/A (void) pthread_mutex_unlock(&globalmutex);
2N/A return (CKR_CRYPTOKI_NOT_INITIALIZED);
2N/A }
2N/A
2N/A /* Check to see if pReserved is NULL */
2N/A if (pReserved != NULL) {
2N/A (void) pthread_mutex_unlock(&globalmutex);
2N/A return (CKR_ARGUMENTS_BAD);
2N/A }
2N/A
2N/A /*
2N/A * Delete all the sessions for each slot and release the allocated
2N/A * resources
2N/A */
2N/A for (i = 0; i < slot_count; i++) {
2N/A kernel_delete_all_sessions(i, B_FALSE);
2N/A }
2N/A
2N/A finalize_common();
2N/A
2N/A (void) pthread_mutex_unlock(&globalmutex);
2N/A
2N/A return (CKR_OK);
2N/A}
2N/A
2N/A/*
2N/A * finalize_common() does the work for C_Finalize. globalmutex
2N/A * must be held before calling this function.
2N/A */
2N/Astatic void
2N/Afinalize_common() {
2N/A
2N/A int i;
2N/A kmh_elem_t *elem, *next;
2N/A kernel_object_t *delay_free_obj, *tmpo;
2N/A kernel_session_t *delay_free_ses, *tmps;
2N/A
2N/A /*
2N/A * Free the resources allocated for the slot table and reset
2N/A * slot_count to 0.
2N/A */
2N/A if (slot_count > 0) {
2N/A for (i = 0; i < slot_count; i++) {
2N/A (void) pthread_mutex_destroy(&slot_table[i]->sl_mutex);
2N/A (void) free(slot_table[i]);
2N/A }
2N/A (void) free(slot_table);
2N/A slot_count = 0;
2N/A }
2N/A
2N/A /* Close CRYPTO_DEVICE */
2N/A if (kernel_fd >= 0) {
2N/A (void) close(kernel_fd);
2N/A }
2N/A
2N/A /* Walk the hash table and free all entries */
2N/A for (i = 0; i < KMECH_HASHTABLE_SIZE; i++) {
2N/A elem = kernel_mechhash[i];
2N/A while (elem != NULL) {
2N/A next = elem->knext;
2N/A free(elem);
2N/A elem = next;
2N/A }
2N/A }
2N/A
2N/A free(kernel_mechhash);
2N/A
2N/A kernel_fd = -1;
2N/A kernel_initialized = B_FALSE;
2N/A kernel_pid = 0;
2N/A
2N/A /*
2N/A * free all entries in the delay_freed list
2N/A */
2N/A delay_free_obj = obj_delay_freed.first;
2N/A while (delay_free_obj != NULL) {
2N/A tmpo = delay_free_obj->next;
2N/A free(delay_free_obj);
2N/A delay_free_obj = tmpo;
2N/A }
2N/A (void) pthread_mutex_destroy(&obj_delay_freed.obj_to_be_free_mutex);
2N/A
2N/A delay_free_ses = ses_delay_freed.first;
2N/A while (delay_free_ses != NULL) {
2N/A tmps = delay_free_ses->next;
2N/A free(delay_free_ses);
2N/A delay_free_ses = tmps;
2N/A }
2N/A (void) pthread_mutex_destroy(&ses_delay_freed.ses_to_be_free_mutex);
2N/A}
2N/A
2N/A/*
2N/A * This function cleans up all the resources in the library (user space only)
2N/A */
2N/Astatic void
2N/Acleanup_library(void)
2N/A{
2N/A int i;
2N/A
2N/A /*
2N/A * Delete all the sessions for each slot and release the allocated
2N/A * resources from the library. The boolean argument TRUE indicates
2N/A * that we only wants to clean up the resource in the library only.
2N/A * We don't want to clean up the corresponding kernel part of
2N/A * resources, because they are used by the parent process still.
2N/A */
2N/A
2N/A for (i = 0; i < slot_count; i++) {
2N/A kernel_delete_all_sessions(i, B_TRUE);
2N/A }
2N/A
2N/A finalize_common();
2N/A}
2N/A
2N/Astatic void
2N/Akernel_init(void)
2N/A{
2N/A (void) pthread_atfork(kernel_fork_prepare, kernel_fork_after,
2N/A kernel_fork_after);
2N/A}
2N/A
2N/A/*
2N/A * kernel_fini() function required to make sure complete cleanup
2N/A * is done if pkcs11_kernel is ever unloaded without
2N/A * a C_Finalize() call.
2N/A */
2N/Astatic void
2N/Akernel_fini(void)
2N/A{
2N/A
2N/A (void) pthread_mutex_lock(&globalmutex);
2N/A
2N/A /* if we're not initialized, do not attempt to finalize */
2N/A if (!kernel_initialized) {
2N/A (void) pthread_mutex_unlock(&globalmutex);
2N/A return;
2N/A }
2N/A
2N/A cleanup_library();
2N/A
2N/A (void) pthread_mutex_unlock(&globalmutex);
2N/A}
2N/A
2N/ACK_RV
2N/AC_GetInfo(CK_INFO_PTR pInfo)
2N/A{
2N/A if (!kernel_initialized)
2N/A return (CKR_CRYPTOKI_NOT_INITIALIZED);
2N/A
2N/A if (pInfo == NULL) {
2N/A return (CKR_ARGUMENTS_BAD);
2N/A }
2N/A
2N/A /* Check if the cryptoki was initialized */
2N/A
2N/A pInfo->cryptokiVersion.major = CRYPTOKI_VERSION_MAJOR;
2N/A pInfo->cryptokiVersion.minor = CRYPTOKI_VERSION_MINOR;
2N/A (void) strncpy((char *)pInfo->manufacturerID,
2N/A MANUFACTURER_ID, 32);
2N/A pInfo->flags = 0;
2N/A (void) strncpy((char *)pInfo->libraryDescription,
2N/A LIBRARY_DESCRIPTION, 32);
2N/A pInfo->libraryVersion.major = LIBRARY_VERSION_MAJOR;
2N/A pInfo->libraryVersion.minor = LIBRARY_VERSION_MINOR;
2N/A
2N/A return (CKR_OK);
2N/A}
2N/A
2N/ACK_RV
2N/AC_GetFunctionList(CK_FUNCTION_LIST_PTR_PTR ppFunctionList)
2N/A{
2N/A if (ppFunctionList == NULL) {
2N/A return (CKR_ARGUMENTS_BAD);
2N/A }
2N/A
2N/A *ppFunctionList = &functionList;
2N/A
2N/A return (CKR_OK);
2N/A}
2N/A
2N/A/*
2N/A * PKCS#11 states that C_GetFunctionStatus should always return
2N/A * CKR_FUNCTION_NOT_PARALLEL
2N/A */
2N/A/*ARGSUSED*/
2N/ACK_RV
2N/AC_GetFunctionStatus(CK_SESSION_HANDLE hSession)
2N/A{
2N/A return (CKR_FUNCTION_NOT_PARALLEL);
2N/A}
2N/A
2N/A/*
2N/A * Take out all mutexes before fork.
2N/A * Order:
2N/A * 1. globalmutex
2N/A * 2. all slots mutexes (and all their sessions) via
2N/A * kernel_acquire_all_slots_mutexes()
2N/A * 3. obj_delay_freed.obj_to_be_free_mutex;
2N/A * 4. ses_delay_freed.ses_to_be_free_mutex
2N/A */
2N/Avoid
2N/Akernel_fork_prepare(void)
2N/A{
2N/A (void) pthread_mutex_lock(&globalmutex);
2N/A if (kernel_initialized) {
2N/A kernel_acquire_all_slots_mutexes();
2N/A (void) pthread_mutex_lock(
2N/A &obj_delay_freed.obj_to_be_free_mutex);
2N/A (void) pthread_mutex_lock(
2N/A &ses_delay_freed.ses_to_be_free_mutex);
2N/A (void) pthread_mutex_lock(&mechhash_mutex);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * Release in opposite order to kernel_fork_prepare().
2N/A * Function is used for parent and child.
2N/A */
2N/Avoid
2N/Akernel_fork_after(void)
2N/A{
2N/A if (kernel_initialized) {
2N/A (void) pthread_mutex_unlock(&mechhash_mutex);
2N/A (void) pthread_mutex_unlock(
2N/A &ses_delay_freed.ses_to_be_free_mutex);
2N/A (void) pthread_mutex_unlock(
2N/A &obj_delay_freed.obj_to_be_free_mutex);
2N/A kernel_release_all_slots_mutexes();
2N/A }
2N/A (void) pthread_mutex_unlock(&globalmutex);
2N/A}
2N/A
2N/A/*
2N/A * PKCS#11 states that C_CancelFunction should always return
2N/A * CKR_FUNCTION_NOT_PARALLEL
2N/A */
2N/A/*ARGSUSED*/
2N/ACK_RV
2N/AC_CancelFunction(CK_SESSION_HANDLE hSession)
2N/A{
2N/A return (CKR_FUNCTION_NOT_PARALLEL);
2N/A}