/* * pkcs11-global.c: PKCS#11 module level functions and function table * * Copyright (C) 2002 Timo Teräs * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #ifdef HAVE_SYS_TIME_H #include #endif #ifdef PKCS11_THREAD_LOCKING #if defined(HAVE_PTHREAD) #include #elif defined(_WIN32) #include #endif #endif /* PKCS11_THREAD_LOCKING */ #include "sc-pkcs11.h" #include "ui/notify.h" #include "libopensc/sc-ossl-compat.h" #ifdef ENABLE_OPENPACE #include #endif #ifndef MODULE_APP_NAME #define MODULE_APP_NAME "opensc-pkcs11" #endif sc_context_t *context = NULL; struct sc_pkcs11_config sc_pkcs11_conf; list_t sessions; list_t virtual_slots; #if !defined(_WIN32) pid_t initialized_pid = (pid_t)-1; #endif static int in_finalize = 0; extern CK_FUNCTION_LIST pkcs11_function_list; extern CK_FUNCTION_LIST_3_0 pkcs11_function_list_3_0; #ifdef PKCS11_THREAD_LOCKING #if defined(HAVE_PTHREAD) /* mutex used to control C_Initilize creation of mutexes */ static pthread_mutex_t c_initialize_m = PTHREAD_MUTEX_INITIALIZER; #define C_INITIALIZE_M_LOCK pthread_mutex_lock(&c_initialize_m); #define C_INITIALIZE_M_UNLOCK pthread_mutex_unlock(&c_initialize_m); CK_RV mutex_create(void **mutex) { pthread_mutex_t *m; m = calloc(1, sizeof(*m)); if (m == NULL) return CKR_GENERAL_ERROR;; pthread_mutex_init(m, NULL); *mutex = m; return CKR_OK; } CK_RV mutex_lock(void *p) { if (pthread_mutex_lock((pthread_mutex_t *) p) == 0) return CKR_OK; else return CKR_GENERAL_ERROR; } CK_RV mutex_unlock(void *p) { if (pthread_mutex_unlock((pthread_mutex_t *) p) == 0) return CKR_OK; else return CKR_GENERAL_ERROR; } CK_RV mutex_destroy(void *p) { pthread_mutex_destroy((pthread_mutex_t *) p); free(p); return CKR_OK; } static CK_C_INITIALIZE_ARGS _def_locks = { mutex_create, mutex_destroy, mutex_lock, mutex_unlock, 0, NULL }; #define HAVE_OS_LOCKING #elif defined(_WIN32) CRITICAL_SECTION c_initialize_cs = {0}; #define C_INITIALIZE_M_LOCK EnterCriticalSection(&c_initialize_cs); #define C_INITIALIZE_M_UNLOCK LeaveCriticalSection(&c_initialize_cs); CK_RV mutex_create(void **mutex) { CRITICAL_SECTION *m; m = calloc(1, sizeof(*m)); if (m == NULL) return CKR_GENERAL_ERROR; InitializeCriticalSection(m); *mutex = m; return CKR_OK; } CK_RV mutex_lock(void *p) { EnterCriticalSection((CRITICAL_SECTION *) p); return CKR_OK; } CK_RV mutex_unlock(void *p) { LeaveCriticalSection((CRITICAL_SECTION *) p); return CKR_OK; } CK_RV mutex_destroy(void *p) { DeleteCriticalSection((CRITICAL_SECTION *) p); free(p); return CKR_OK; } static CK_C_INITIALIZE_ARGS _def_locks = { mutex_create, mutex_destroy, mutex_lock, mutex_unlock, 0, NULL }; #define HAVE_OS_LOCKING #endif #else /* PKCS11_THREAD_LOCKING */ #define C_INITIALIZE_M_LOCK #define C_INITIALIZE_M_UNLOCK #endif /* PKCS11_THREAD_LOCKING */ static CK_C_INITIALIZE_ARGS_PTR global_locking; static void *global_lock = NULL; #ifdef HAVE_OS_LOCKING static CK_C_INITIALIZE_ARGS_PTR default_mutex_funcs = &_def_locks; #else static CK_C_INITIALIZE_ARGS_PTR default_mutex_funcs = NULL; #endif /* wrapper for the locking functions for libopensc */ static int sc_create_mutex(void **m) { if (global_locking == NULL) return SC_SUCCESS; if (global_locking->CreateMutex(m) == CKR_OK) return SC_SUCCESS; else return SC_ERROR_INTERNAL; } static int sc_lock_mutex(void *m) { if (global_locking == NULL) return SC_SUCCESS; if (global_locking->LockMutex(m) == CKR_OK) return SC_SUCCESS; else return SC_ERROR_INTERNAL; } static int sc_unlock_mutex(void *m) { if (global_locking == NULL) return SC_SUCCESS; if (global_locking->UnlockMutex(m) == CKR_OK) return SC_SUCCESS; else return SC_ERROR_INTERNAL; } static int sc_destroy_mutex(void *m) { if (global_locking == NULL) return SC_SUCCESS; if (global_locking->DestroyMutex(m) == CKR_OK) return SC_SUCCESS; else return SC_ERROR_INTERNAL; } static sc_thread_context_t sc_thread_ctx = { 0, sc_create_mutex, sc_lock_mutex, sc_unlock_mutex, sc_destroy_mutex, NULL }; /* simclist helpers to locate interesting objects by ID */ static int session_list_seeker(const void *el, const void *key) { const struct sc_pkcs11_session *session = (struct sc_pkcs11_session *)el; if ((el == NULL) || (key == NULL)) return 0; if (session->handle == *(CK_SESSION_HANDLE*)key) return 1; return 0; } static int slot_list_seeker(const void *el, const void *key) { const struct sc_pkcs11_slot *slot = (struct sc_pkcs11_slot *)el; if ((el == NULL) || (key == NULL)) return 0; if (slot->id == *(CK_SLOT_ID *)key) return 1; return 0; } #ifndef _WIN32 __attribute__((constructor)) #endif int module_init() { #ifdef _WIN32 InitializeCriticalSection(&c_initialize_cs); #endif sc_notify_init(); return 1; } #ifndef _WIN32 __attribute__((destructor)) #endif int module_close() { sc_notify_close(); #if defined(ENABLE_OPENSSL) && defined(OPENSSL_SECURE_MALLOC_SIZE) CRYPTO_secure_malloc_done(); #endif #ifdef ENABLE_OPENPACE EAC_cleanup(); #endif #ifdef _WIN32 DeleteCriticalSection(&c_initialize_cs); #endif return 1; } #ifdef _WIN32 BOOL APIENTRY DllMain( HINSTANCE hinstDLL, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: if (!module_init()) return FALSE; break; case DLL_PROCESS_DETACH: if (lpReserved == NULL) { if (!module_close()) return FALSE; } break; } return TRUE; } #endif CK_RV C_Initialize(CK_VOID_PTR pInitArgs) { CK_RV rv; #if !defined(_WIN32) pid_t current_pid = getpid(); #endif int rc; sc_context_param_t ctx_opts; #if !defined(_WIN32) /* Handle fork() exception */ if (current_pid != initialized_pid) { if (context) context->flags |= SC_CTX_FLAG_TERMINATE; C_Finalize(NULL_PTR); } initialized_pid = current_pid; in_finalize = 0; #endif /* protect from multiple threads tryng to setup locking */ C_INITIALIZE_M_LOCK if (context != NULL) { sc_log(context, "C_Initialize(): Cryptoki already initialized\n"); C_INITIALIZE_M_UNLOCK return CKR_CRYPTOKI_ALREADY_INITIALIZED; } rv = sc_pkcs11_init_lock((CK_C_INITIALIZE_ARGS_PTR) pInitArgs); if (rv != CKR_OK) goto out; /* set context options */ memset(&ctx_opts, 0, sizeof(sc_context_param_t)); ctx_opts.ver = 0; ctx_opts.app_name = MODULE_APP_NAME; ctx_opts.thread_ctx = &sc_thread_ctx; rc = sc_context_create(&context, &ctx_opts); if (rc != SC_SUCCESS) { rv = CKR_GENERAL_ERROR; goto out; } /* Load configuration */ load_pkcs11_parameters(&sc_pkcs11_conf, context); /* List of sessions */ if (0 != list_init(&sessions)) { rv = CKR_HOST_MEMORY; goto out; } list_attributes_seeker(&sessions, session_list_seeker); /* List of slots */ if (0 != list_init(&virtual_slots)) { rv = CKR_HOST_MEMORY; goto out; } list_attributes_seeker(&virtual_slots, slot_list_seeker); card_detect_all(); out: if (context != NULL) sc_log(context, "C_Initialize() = %s", lookup_enum ( RV_T, rv )); if (rv != CKR_OK) { if (context != NULL) { sc_release_context(context); context = NULL; } /* Release and destroy the mutex */ sc_pkcs11_free_lock(); } /* protect from multiple threads tryng to setup locking */ C_INITIALIZE_M_UNLOCK return rv; } CK_RV C_Finalize(CK_VOID_PTR pReserved) { int i; void *p; sc_pkcs11_slot_t *slot; CK_RV rv; if (pReserved != NULL_PTR) return CKR_ARGUMENTS_BAD; #if !defined(_WIN32) sc_notify_close(); #endif if (context == NULL) return CKR_CRYPTOKI_NOT_INITIALIZED; rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; sc_log(context, "C_Finalize()"); /* cancel pending calls */ in_finalize = 1; sc_cancel(context); /* remove all cards from readers */ for (i=0; i < (int)sc_ctx_get_reader_count(context); i++) card_removed(sc_ctx_get_reader(context, i)); while ((p = list_fetch(&sessions))) free(p); list_destroy(&sessions); while ((slot = list_fetch(&virtual_slots))) { list_destroy(&slot->objects); list_destroy(&slot->logins); free(slot); } list_destroy(&virtual_slots); sc_release_context(context); context = NULL; /* Release and destroy the mutex */ sc_pkcs11_free_lock(); return rv; } CK_RV get_info_version(CK_INFO_PTR pInfo, CK_VERSION version) { CK_RV rv = CKR_OK; if (pInfo == NULL_PTR) return CKR_ARGUMENTS_BAD; rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; sc_log(context, "C_GetInfo()"); memset(pInfo, 0, sizeof(CK_INFO)); pInfo->cryptokiVersion.major = version.major; pInfo->cryptokiVersion.minor = version.minor; strcpy_bp(pInfo->manufacturerID, OPENSC_VS_FF_COMPANY_NAME, sizeof(pInfo->manufacturerID)); strcpy_bp(pInfo->libraryDescription, OPENSC_VS_FF_PRODUCT_NAME, sizeof(pInfo->libraryDescription)); pInfo->libraryVersion.major = OPENSC_VERSION_MAJOR; pInfo->libraryVersion.minor = OPENSC_VERSION_MINOR; sc_pkcs11_unlock(); return rv; } CK_RV C_GetInfoV2(CK_INFO_PTR pInfo) { CK_VERSION v = {2, 20}; return get_info_version(pInfo, v); } CK_RV C_GetInfo(CK_INFO_PTR pInfo) { CK_VERSION v = {3, 0}; return get_info_version(pInfo, v); } CK_RV C_GetFunctionList(CK_FUNCTION_LIST_PTR_PTR ppFunctionList) { if (ppFunctionList == NULL_PTR) return CKR_ARGUMENTS_BAD; *ppFunctionList = &pkcs11_function_list; return CKR_OK; } CK_RV C_GetSlotList(CK_BBOOL tokenPresent, /* only slots with token present */ CK_SLOT_ID_PTR pSlotList, /* receives the array of slot IDs */ CK_ULONG_PTR pulCount) /* receives the number of slots */ { CK_SLOT_ID_PTR found = NULL; unsigned int i; CK_ULONG numMatches; sc_pkcs11_slot_t *slot; sc_reader_t *prev_reader = NULL; CK_RV rv; if (pulCount == NULL_PTR) return CKR_ARGUMENTS_BAD; rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; sc_log(context, "C_GetSlotList(token=%d, %s)", tokenPresent, pSlotList==NULL_PTR? "plug-n-play":"refresh"); DEBUG_VSS(NULL, "C_GetSlotList before ctx_detect_detect"); /* Slot list can only change in v2.20 */ if (pSlotList == NULL_PTR) sc_ctx_detect_readers(context); DEBUG_VSS(NULL, "C_GetSlotList after ctx_detect_readers"); card_detect_all(); if (list_empty(&virtual_slots)) { sc_log(context, "returned 0 slots\n"); *pulCount = 0; rv = CKR_OK; goto out; } found = calloc(list_size(&virtual_slots), sizeof(CK_SLOT_ID)); if (found == NULL) { rv = CKR_HOST_MEMORY; goto out; } prev_reader = NULL; numMatches = 0; for (i=0; ireader != prev_reader || slot->flags & SC_PKCS11_SLOT_FLAG_SEEN)) || slot->slot_info.flags & CKF_TOKEN_PRESENT) { found[numMatches++] = slot->id; slot->flags |= SC_PKCS11_SLOT_FLAG_SEEN; } prev_reader = slot->reader; } DEBUG_VSS(NULL, "C_GetSlotList after card_detect_all"); if (pSlotList == NULL_PTR) { sc_log(context, "was only a size inquiry (%lu)\n", numMatches); *pulCount = numMatches; rv = CKR_OK; goto out; } DEBUG_VSS(NULL, "C_GetSlotList after slot->id reassigned"); if (*pulCount < numMatches) { sc_log(context, "buffer was too small (needed %lu)\n", numMatches); *pulCount = numMatches; rv = CKR_BUFFER_TOO_SMALL; goto out; } memcpy(pSlotList, found, numMatches * sizeof(CK_SLOT_ID)); *pulCount = numMatches; rv = CKR_OK; sc_log(context, "returned %lu slots\n", numMatches); DEBUG_VSS(NULL, "Returning a new slot list"); out: free (found); sc_pkcs11_unlock(); return rv; } static sc_timestamp_t get_current_time(void) { #if HAVE_GETTIMEOFDAY struct timeval tv; struct timezone tz; sc_timestamp_t curr; if (gettimeofday(&tv, &tz) != 0) return 0; curr = tv.tv_sec; curr *= 1000; curr += tv.tv_usec / 1000; #else struct _timeb time_buf; sc_timestamp_t curr; _ftime(&time_buf); curr = time_buf.time; curr *= 1000; curr += time_buf.millitm; #endif return curr; } CK_RV C_GetSlotInfo(CK_SLOT_ID slotID, CK_SLOT_INFO_PTR pInfo) { struct sc_pkcs11_slot *slot = NULL; sc_timestamp_t now; CK_RV rv; if (pInfo == NULL_PTR) return CKR_ARGUMENTS_BAD; rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; sc_log(context, "C_GetSlotInfo(0x%lx)", slotID); if (sc_pkcs11_conf.init_sloppy) { /* Most likely virtual_slots is empty and has not * been initialized because the caller has *not* called C_GetSlotList * before C_GetSlotInfo, as required by PKCS#11. Initialize * virtual_slots to make things work and hope the caller knows what * it's doing... */ card_detect_all(); } rv = slot_get_slot(slotID, &slot); DEBUG_VSS(slot, "C_GetSlotInfo found"); sc_log(context, "C_GetSlotInfo() get slot rv %s", lookup_enum( RV_T, rv)); if (rv == CKR_OK) { if (slot->reader == NULL) { rv = CKR_TOKEN_NOT_PRESENT; } else { now = get_current_time(); if (now >= slot->slot_state_expires || now == 0) { /* Update slot status */ rv = card_detect(slot->reader); sc_log(context, "C_GetSlotInfo() card detect rv 0x%lX", rv); if (rv == CKR_TOKEN_NOT_RECOGNIZED || rv == CKR_OK) slot->slot_info.flags |= CKF_TOKEN_PRESENT; /* Don't ask again within the next second */ slot->slot_state_expires = now + 1000; } } } if (rv == CKR_TOKEN_NOT_PRESENT || rv == CKR_TOKEN_NOT_RECOGNIZED) rv = CKR_OK; if (rv == CKR_OK) memcpy(pInfo, &slot->slot_info, sizeof(CK_SLOT_INFO)); sc_log(context, "C_GetSlotInfo() flags 0x%lX", pInfo->flags); sc_log(context, "C_GetSlotInfo(0x%lx) = %s", slotID, lookup_enum( RV_T, rv)); sc_pkcs11_unlock(); return rv; } CK_RV C_GetMechanismList(CK_SLOT_ID slotID, CK_MECHANISM_TYPE_PTR pMechanismList, CK_ULONG_PTR pulCount) { struct sc_pkcs11_slot *slot; CK_RV rv; if (pulCount == NULL_PTR) return CKR_ARGUMENTS_BAD; rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; rv = slot_get_token(slotID, &slot); if (rv == CKR_OK) rv = sc_pkcs11_get_mechanism_list(slot->p11card, pMechanismList, pulCount); sc_pkcs11_unlock(); return rv; } CK_RV C_GetMechanismInfo(CK_SLOT_ID slotID, CK_MECHANISM_TYPE type, CK_MECHANISM_INFO_PTR pInfo) { struct sc_pkcs11_slot *slot; CK_RV rv; if (pInfo == NULL_PTR) return CKR_ARGUMENTS_BAD; rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; rv = slot_get_token(slotID, &slot); if (rv == CKR_OK) rv = sc_pkcs11_get_mechanism_info(slot->p11card, type, pInfo); sc_pkcs11_unlock(); return rv; } CK_RV C_InitToken(CK_SLOT_ID slotID, CK_CHAR_PTR pPin, CK_ULONG ulPinLen, CK_CHAR_PTR pLabel) { struct sc_pkcs11_session *session; struct sc_pkcs11_slot *slot; CK_RV rv; unsigned int i; sc_log(context, "C_InitToken(pLabel='%s') called", pLabel); rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; rv = slot_get_token(slotID, &slot); if (rv != CKR_OK) { sc_log(context, "C_InitToken() get token error 0x%lX", rv); goto out; } if (!slot->p11card || !slot->p11card->framework || !slot->p11card->framework->init_token) { sc_log(context, "C_InitToken() not supported by framework"); rv = CKR_FUNCTION_NOT_SUPPORTED; goto out; } /* Make sure there's no open session for this token */ for (i=0; islot == slot) { rv = CKR_SESSION_EXISTS; goto out; } } rv = slot->p11card->framework->init_token(slot, slot->fw_data, pPin, ulPinLen, pLabel); if (rv == CKR_OK) { /* Now we should re-bind all tokens so they get the * corresponding function vector and flags */ } out: sc_pkcs11_unlock(); sc_log(context, "C_InitToken(pLabel='%s') returns 0x%lX", pLabel, rv); return rv; } CK_RV C_WaitForSlotEvent(CK_FLAGS flags, /* blocking/nonblocking flag */ CK_SLOT_ID_PTR pSlot, /* location that receives the slot ID */ CK_VOID_PTR pReserved) /* reserved. Should be NULL_PTR */ { sc_reader_t *found; unsigned int mask, events; void *reader_states = NULL; CK_SLOT_ID slot_id; CK_RV rv; int r; if (pReserved != NULL_PTR) return CKR_ARGUMENTS_BAD; sc_log(context, "C_WaitForSlotEvent(block=%d)", !(flags & CKF_DONT_BLOCK)); #ifndef PCSCLITE_GOOD /* Not all pcsc-lite versions implement consistently used functions as they are */ if (!(flags & CKF_DONT_BLOCK)) return CKR_FUNCTION_NOT_SUPPORTED; #endif /* PCSCLITE_GOOD */ rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; mask = SC_EVENT_CARD_EVENTS | SC_EVENT_READER_EVENTS; /* Detect and add new slots for added readers v2.20 */ rv = slot_find_changed(&slot_id, mask); if ((rv == CKR_OK) || (flags & CKF_DONT_BLOCK)) goto out; again: sc_log(context, "C_WaitForSlotEvent() reader_states:%p", reader_states); sc_pkcs11_unlock(); r = sc_wait_for_event(context, mask, &found, &events, -1, &reader_states); /* Was C_Finalize called ? */ if (in_finalize == 1) return CKR_CRYPTOKI_NOT_INITIALIZED; if ((rv = sc_pkcs11_lock()) != CKR_OK) return rv; if (r != SC_SUCCESS) { sc_log(context, "sc_wait_for_event() returned %d\n", r); rv = sc_to_cryptoki_error(r, "C_WaitForSlotEvent"); goto out; } /* If no changed slot was found (maybe an unsupported card * was inserted/removed) then go waiting again */ rv = slot_find_changed(&slot_id, mask); if (rv != CKR_OK) goto again; out: if (pSlot) *pSlot = slot_id; /* Free allocated readers states holder */ if (reader_states) { sc_log(context, "free reader states"); sc_wait_for_event(context, 0, NULL, NULL, -1, &reader_states); } sc_log(context, "C_WaitForSlotEvent() = %s", lookup_enum (RV_T, rv)); sc_pkcs11_unlock(); return rv; } /* * Interfaces */ #define NUM_INTERFACES 2 #define DEFAULT_INTERFACE 0 CK_INTERFACE interfaces[NUM_INTERFACES] = { {"PKCS 11", (void *)&pkcs11_function_list_3_0, 0}, {"PKCS 11", (void *)&pkcs11_function_list, 0} }; CK_RV C_GetInterfaceList(CK_INTERFACE_PTR pInterfacesList, /* returned interfaces */ CK_ULONG_PTR pulCount) /* number of interfaces returned */ { sc_log(context, "C_GetInterfaceList()"); if (pulCount == NULL_PTR) return CKR_ARGUMENTS_BAD; if (pInterfacesList == NULL_PTR) { *pulCount = NUM_INTERFACES; sc_log(context, "was only a size inquiry (%lu)\n", *pulCount); return CKR_OK; } if (*pulCount < NUM_INTERFACES) { sc_log(context, "buffer was too small (needed %d)\n", NUM_INTERFACES); *pulCount = NUM_INTERFACES; return CKR_BUFFER_TOO_SMALL; } memcpy(pInterfacesList, interfaces, NUM_INTERFACES * sizeof(CK_INTERFACE)); *pulCount = NUM_INTERFACES; sc_log(context, "returned %lu interfaces\n", *pulCount); return CKR_OK; } CK_RV C_GetInterface(CK_UTF8CHAR_PTR pInterfaceName, /* name of the interface */ CK_VERSION_PTR pVersion, /* version of the interface */ CK_INTERFACE_PTR_PTR ppInterface, /* returned interface */ CK_FLAGS flags) /* flags controlling the semantics * of the interface */ { int i; sc_log(context, "C_GetInterface(%s)", pInterfaceName == NULL_PTR ? "" : (char *)pInterfaceName); if (ppInterface == NULL) { return CKR_ARGUMENTS_BAD; } if (pInterfaceName == NULL_PTR) { /* return default interface */ *ppInterface = &interfaces[DEFAULT_INTERFACE]; sc_log(context, "Returning default interface\n"); return CKR_OK; } for (i = 0; i < NUM_INTERFACES; i++) { CK_VERSION_PTR interface_version = (CK_VERSION_PTR)interfaces[i].pFunctionList; /* The interface name is not null here */ if (strcmp((char *)pInterfaceName, interfaces[i].pInterfaceName) != 0) { continue; } /* If version is not null, it must match */ if (pVersion != NULL_PTR && (pVersion->major != interface_version->major || pVersion->minor != interface_version->minor)) { continue; } /* If any flags specified, it must be supported by the interface */ if ((flags & interfaces[i].flags) != flags) { continue; } *ppInterface = &interfaces[i]; sc_log(context, "Returning interface %s\n", (*ppInterface)->pInterfaceName); return CKR_OK; } sc_log(context, "Interface not found: %s, version=%d.%d, flags=%lu\n", pInterfaceName ? (char *)pInterfaceName : "", pVersion ? (*pVersion).major : 0, pVersion ? (*pVersion).minor : 0, flags); return CKR_ARGUMENTS_BAD; } /* * Locking functions */ CK_RV sc_pkcs11_init_lock(CK_C_INITIALIZE_ARGS_PTR args) { CK_RV rv = CKR_OK; int applock = 0; int oslock = 0; if (global_lock) return CKR_OK; /* No CK_C_INITIALIZE_ARGS pointer, no locking */ if (!args) return CKR_OK; if (args->pReserved != NULL_PTR) return CKR_ARGUMENTS_BAD; /* If the app tells us OS locking is okay, * use that. Otherwise use the supplied functions. */ global_locking = NULL; if (args->CreateMutex && args->DestroyMutex && args->LockMutex && args->UnlockMutex) { applock = 1; } if ((args->flags & CKF_OS_LOCKING_OK)) { oslock = 1; } /* Based on PKCS#11 v2.11 11.4 */ if (applock && oslock) { /* Shall be used in threaded environment, prefer app provided locking */ global_locking = args; } else if (!applock && oslock) { /* Shall be used in threaded environment, must use operating system locking */ global_locking = default_mutex_funcs; } else if (applock && !oslock) { /* Shall be used in threaded environment, must use app provided locking */ global_locking = args; } else if (!applock && !oslock) { /* Shall not be used in threaded environment, use operating system locking */ global_locking = default_mutex_funcs; } if (global_locking != NULL) { /* create mutex */ rv = global_locking->CreateMutex(&global_lock); } return rv; } CK_RV sc_pkcs11_lock(void) { if (context == NULL) return CKR_CRYPTOKI_NOT_INITIALIZED; if (!global_lock) return CKR_OK; if (global_locking) { while (global_locking->LockMutex(global_lock) != CKR_OK) ; } return CKR_OK; } static void __sc_pkcs11_unlock(void *lock) { if (!lock) return; if (global_locking) { while (global_locking->UnlockMutex(lock) != CKR_OK) ; } } void sc_pkcs11_unlock(void) { __sc_pkcs11_unlock(global_lock); } /* * Free the lock - note the lock must be held when * you come here */ void sc_pkcs11_free_lock(void) { void *tempLock; if (!(tempLock = global_lock)) return; /* Clear the global lock pointer - once we've * unlocked the mutex it's as good as gone */ global_lock = NULL; /* Now unlock. On SMP machines the synchronization * primitives should take care of flushing out * all changed data to RAM */ __sc_pkcs11_unlock(tempLock); if (global_locking) global_locking->DestroyMutex(tempLock); global_locking = NULL; } CK_FUNCTION_LIST pkcs11_function_list = { { 2, 20 }, /* Note: NSS/Firefox ignores this version number and uses C_GetInfo() */ C_Initialize, C_Finalize, C_GetInfoV2, C_GetFunctionList, C_GetSlotList, C_GetSlotInfo, C_GetTokenInfo, C_GetMechanismList, C_GetMechanismInfo, C_InitToken, C_InitPIN, C_SetPIN, C_OpenSession, C_CloseSession, C_CloseAllSessions, C_GetSessionInfo, C_GetOperationState, C_SetOperationState, C_Login, C_Logout, C_CreateObject, C_CopyObject, C_DestroyObject, C_GetObjectSize, C_GetAttributeValue, C_SetAttributeValue, C_FindObjectsInit, C_FindObjects, C_FindObjectsFinal, C_EncryptInit, C_Encrypt, C_EncryptUpdate, C_EncryptFinal, C_DecryptInit, C_Decrypt, C_DecryptUpdate, C_DecryptFinal, C_DigestInit, C_Digest, C_DigestUpdate, C_DigestKey, C_DigestFinal, C_SignInit, C_Sign, C_SignUpdate, C_SignFinal, C_SignRecoverInit, C_SignRecover, C_VerifyInit, C_Verify, C_VerifyUpdate, C_VerifyFinal, C_VerifyRecoverInit, C_VerifyRecover, C_DigestEncryptUpdate, C_DecryptDigestUpdate, C_SignEncryptUpdate, C_DecryptVerifyUpdate, C_GenerateKey, C_GenerateKeyPair, C_WrapKey, C_UnwrapKey, C_DeriveKey, C_SeedRandom, C_GenerateRandom, C_GetFunctionStatus, C_CancelFunction, C_WaitForSlotEvent }; /* Returned from getInterface */ CK_FUNCTION_LIST_3_0 pkcs11_function_list_3_0 = { { 3, 0 }, C_Initialize, C_Finalize, C_GetInfo, C_GetFunctionList, C_GetSlotList, C_GetSlotInfo, C_GetTokenInfo, C_GetMechanismList, C_GetMechanismInfo, C_InitToken, C_InitPIN, C_SetPIN, C_OpenSession, C_CloseSession, C_CloseAllSessions, C_GetSessionInfo, C_GetOperationState, C_SetOperationState, C_Login, C_Logout, C_CreateObject, C_CopyObject, C_DestroyObject, C_GetObjectSize, C_GetAttributeValue, C_SetAttributeValue, C_FindObjectsInit, C_FindObjects, C_FindObjectsFinal, C_EncryptInit, C_Encrypt, C_EncryptUpdate, C_EncryptFinal, C_DecryptInit, C_Decrypt, C_DecryptUpdate, C_DecryptFinal, C_DigestInit, C_Digest, C_DigestUpdate, C_DigestKey, C_DigestFinal, C_SignInit, C_Sign, C_SignUpdate, C_SignFinal, C_SignRecoverInit, C_SignRecover, C_VerifyInit, C_Verify, C_VerifyUpdate, C_VerifyFinal, C_VerifyRecoverInit, C_VerifyRecover, C_DigestEncryptUpdate, C_DecryptDigestUpdate, C_SignEncryptUpdate, C_DecryptVerifyUpdate, C_GenerateKey, C_GenerateKeyPair, C_WrapKey, C_UnwrapKey, C_DeriveKey, C_SeedRandom, C_GenerateRandom, C_GetFunctionStatus, C_CancelFunction, C_WaitForSlotEvent, C_GetInterfaceList, C_GetInterface, C_LoginUser, C_SessionCancel, C_MessageEncryptInit, C_EncryptMessage, C_EncryptMessageBegin, C_EncryptMessageNext, C_MessageEncryptFinal, C_MessageDecryptInit, C_DecryptMessage, C_DecryptMessageBegin, C_DecryptMessageNext, C_MessageDecryptFinal, C_MessageSignInit, C_SignMessage, C_SignMessageBegin, C_SignMessageNext, C_MessageSignFinal, C_MessageVerifyInit, C_VerifyMessage, C_VerifyMessageBegin, C_VerifyMessageNext, C_MessageVerifyFinal };