/* * 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 #include #include "sc-pkcs11.h" sc_context_t *context = NULL; struct sc_pkcs11_pool session_pool; struct sc_pkcs11_slot virtual_slots[SC_PKCS11_MAX_VIRTUAL_SLOTS]; struct sc_pkcs11_card card_table[SC_PKCS11_MAX_READERS]; struct sc_pkcs11_config sc_pkcs11_conf; extern CK_FUNCTION_LIST pkcs11_function_list; CK_RV C_Initialize(CK_VOID_PTR pReserved) { int i, rc, rv; if (context != NULL) { sc_error(context, "C_Initialize(): Cryptoki already initialized\n"); return CKR_CRYPTOKI_ALREADY_INITIALIZED; } rc = sc_establish_context(&context, "opensc-pkcs11"); if (rc != 0) { rv = CKR_DEVICE_ERROR; goto out; } /* Load configuration */ load_pkcs11_parameters(&sc_pkcs11_conf, context); first_free_slot = 0; pool_initialize(&session_pool, POOL_TYPE_SESSION); for (i=0; icryptokiVersion.major = 2; pInfo->cryptokiVersion.minor = 11; strcpy_bp(pInfo->manufacturerID, "OpenSC Project (www.opensc-project.org)", sizeof(pInfo->manufacturerID)); strcpy_bp(pInfo->libraryDescription, "smart card PKCS#11 API", sizeof(pInfo->libraryDescription)); pInfo->libraryVersion.major = 1; pInfo->libraryVersion.minor = 0; out: sc_pkcs11_unlock(); return rv; } 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 found[SC_PKCS11_MAX_VIRTUAL_SLOTS]; int i; CK_ULONG numMatches; sc_pkcs11_slot_t *slot; CK_RV rv; rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; if (pulCount == NULL_PTR) { rv = CKR_ARGUMENTS_BAD; goto out; } sc_debug(context, "Getting slot listing\n"); card_detect_all(); numMatches = 0; for (i=0; islot_info.flags & CKF_TOKEN_PRESENT)) found[numMatches++] = i; } if (pSlotList == NULL_PTR) { sc_debug(context, "was only a size inquiry (%d)\n", numMatches); *pulCount = numMatches; rv = CKR_OK; goto out; } if (*pulCount < numMatches) { sc_debug(context, "buffer was too small (needed %d)\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_debug(context, "returned %d slots\n", numMatches); out: sc_pkcs11_unlock(); return rv; } CK_RV C_GetSlotInfo(CK_SLOT_ID slotID, CK_SLOT_INFO_PTR pInfo) { struct sc_pkcs11_slot *slot; sc_timestamp_t now; CK_RV rv; rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; if (pInfo == NULL_PTR) { rv = CKR_ARGUMENTS_BAD; goto out; } sc_debug(context, "Getting info about slot %d\n", slotID); rv = slot_get_slot(slotID, &slot); if (rv == CKR_OK){ now = sc_current_time(); if (now >= card_table[slot->reader].slot_state_expires || now == 0) { /* Update slot status */ rv = card_detect(slot->reader); /* Don't ask again within the next second */ card_table[slot->reader].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)); out: sc_pkcs11_unlock(); return rv; } CK_RV C_GetTokenInfo(CK_SLOT_ID slotID, CK_TOKEN_INFO_PTR pInfo) { struct sc_pkcs11_slot *slot; CK_RV rv; rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; if (pInfo == NULL_PTR) { rv = CKR_ARGUMENTS_BAD; goto out; } sc_debug(context, "Getting info about token in slot %d\n", slotID); rv = slot_get_token(slotID, &slot); if (rv == CKR_OK) memcpy(pInfo, &slot->token_info, sizeof(CK_TOKEN_INFO)); out: 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; 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->card, 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; rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; if (pInfo == NULL_PTR) { rv = CKR_ARGUMENTS_BAD; goto out; } rv = slot_get_token(slotID, &slot); if (rv == CKR_OK) rv = sc_pkcs11_get_mechanism_info(slot->card, type, pInfo); out: 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_pool_item *item; struct sc_pkcs11_session *session; struct sc_pkcs11_slot *slot; CK_RV rv; rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; rv = slot_get_token(slotID, &slot); if (rv != CKR_OK) goto out; /* Make sure there's no open session for this token */ for (item = session_pool.head; item; item = item->next) { session = (struct sc_pkcs11_session*) item->item; if (session->slot == slot) { rv = CKR_SESSION_EXISTS; goto out; } } if (slot->card->framework->init_token == NULL) { rv = CKR_FUNCTION_NOT_SUPPORTED; goto out; } rv = slot->card->framework->init_token(slot->card, 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(); 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 *reader, *readers[SC_MAX_SLOTS * SC_MAX_READERS]; int slots[SC_MAX_SLOTS * SC_MAX_READERS]; int i, j, k, r, found; unsigned int mask, events; CK_RV rv; /* Firefox 1.5 calls this function (blocking) from a seperate thread, * which gives 2 problems: * - on Windows/Mac: this waiting thread will log to a NULL context * after the 'main' thread does a C_Finalize() and sets the ctx to NULL. * - on Linux, things just hang (at least on Debian 'sid') * So we just return CKR_FUNCTION_NOT_SUPPORTED on a blocking call, * in which case Ff just seems to default to polling in the main thread * as the Mozilla family of browsers did before. */ if (!(flags & CKF_DONT_BLOCK)) return CKR_FUNCTION_NOT_SUPPORTED; rv = sc_pkcs11_lock(); if (rv != CKR_OK) return rv; if (pReserved != NULL_PTR) { rv = CKR_ARGUMENTS_BAD; goto out; } mask = SC_EVENT_CARD_INSERTED|SC_EVENT_CARD_REMOVED; if ((rv = slot_find_changed(pSlot, mask)) == CKR_OK || (flags & CKF_DONT_BLOCK)) goto out; for (i = k = 0; i < (int)sc_ctx_get_reader_count(context); i++) { reader = sc_ctx_get_reader(context, i); if (reader == NULL) { rv = CKR_GENERAL_ERROR; goto out; } for (j = 0; j < reader->slot_count; j++, k++) { readers[k] = reader; slots[k] = j; } } again: /* Check if C_Finalize() has been called in another thread */ if (context == NULL) return CKR_CRYPTOKI_NOT_INITIALIZED; sc_pkcs11_unlock(); r = sc_wait_for_event(readers, slots, k, mask, &found, &events, -1); /* There may have been a C_Finalize while we slept */ if (context == NULL) return CKR_CRYPTOKI_NOT_INITIALIZED; if ((rv = sc_pkcs11_lock()) != CKR_OK) return rv; if (r != SC_SUCCESS) { sc_error(context, "sc_wait_for_event() returned %d\n", r); rv = sc_to_cryptoki_error(r, -1); goto out; } /* If no changed slot was found (maybe an unsupported card * was inserted/removed) then go waiting again */ if ((rv = slot_find_changed(pSlot, mask)) != CKR_OK) goto again; out: sc_pkcs11_unlock(); return rv; } /* * Locking functions */ static CK_C_INITIALIZE_ARGS_PTR _locking; static void * _lock = NULL; CK_RV sc_pkcs11_init_lock(CK_C_INITIALIZE_ARGS_PTR args) { int rv = CKR_OK; if (_lock) return CKR_OK; /* No CK_C_INITIALIZE_ARGS pointer, no locking */ if (!args) return CKR_OK; if (args->pReserved) return CKR_ARGUMENTS_BAD; /* If the app tells us OS locking is okay, * use that. Otherwise use the supplied functions. */ _locking = NULL; if (args->flags & CKF_OS_LOCKING_OK) { #if (defined(HAVE_PTHREAD) && !defined(PKCS11_THREAD_LOCKING)) /* FIXME: * Mozilla uses the CKF_OS_LOCKING_OK flag in C_Initialize(). * The result is that the Mozilla process doesn't end when * closing Mozilla, so you have to kill the process yourself. * (If Mozilla would do a C_Finalize, the sc_pkcs11_free_lock() * would be called and there wouldn't be a problem.) * Therefore, we don't use the PTHREAD locking mechanisms, even * if they are requested. This is the old situation which seems * to work fine for Mozilla, BUT will cause problems for apps * that use multiple threads to access this lib simultaneously. * If you do want to use OS threading, compile with * -DPKCS11_THREAD_LOCKING */ return CKR_OK; #endif if (!(_lock = sc_mutex_new())) rv = CKR_CANT_LOCK; } else if (args->CreateMutex && args->DestroyMutex && args->LockMutex && args->UnlockMutex) { rv = args->CreateMutex(&_lock); if (rv == CKR_OK) _locking = args; } return rv; } CK_RV sc_pkcs11_lock() { if (context == NULL) return CKR_CRYPTOKI_NOT_INITIALIZED; if (!_lock) return CKR_OK; if (_locking) { while (_locking->LockMutex(_lock) != CKR_OK) ; } else { sc_mutex_lock((sc_mutex_t *) _lock); } return CKR_OK; } static void __sc_pkcs11_unlock(void *lock) { if (!lock) return; if (_locking) { while (_locking->UnlockMutex(lock) != CKR_OK) ; } else { sc_mutex_unlock((sc_mutex_t *) lock); } } void sc_pkcs11_unlock() { __sc_pkcs11_unlock(_lock); } /* * Free the lock - note the lock must be held when * you come here */ void sc_pkcs11_free_lock() { void *tempLock; if (!(tempLock = _lock)) return; /* Clear the global lock pointer - once we've * unlocked the mutex it's as good as gone */ _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 (_locking) _locking->DestroyMutex(tempLock); else sc_mutex_free((sc_mutex_t *) tempLock); _locking = NULL; } CK_FUNCTION_LIST pkcs11_function_list = { { 2, 11 }, 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 };