| /* This Source Code Form is subject to the terms of the Mozilla Public |
| * License, v. 2.0. If a copy of the MPL was not distributed with this |
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| /* |
| * Initialize the PCKS 11 subsystem |
| */ |
| #include "seccomon.h" |
| #include "secmod.h" |
| #include "nssilock.h" |
| #include "secmodi.h" |
| #include "secmodti.h" |
| #include "pk11func.h" |
| #include "pki3hack.h" |
| #include "secerr.h" |
| #include "dev.h" |
| #include "utilpars.h" |
| #include "pkcs11uri.h" |
| |
| /* these are for displaying error messages */ |
| |
| static SECMODModuleList *modules = NULL; |
| static SECMODModuleList *modulesDB = NULL; |
| static SECMODModuleList *modulesUnload = NULL; |
| static SECMODModule *internalModule = NULL; |
| static SECMODModule *defaultDBModule = NULL; |
| static SECMODModule *pendingModule = NULL; |
| static SECMODListLock *moduleLock = NULL; |
| |
| int secmod_PrivateModuleCount = 0; |
| |
| extern const PK11DefaultArrayEntry PK11_DefaultArray[]; |
| extern const int num_pk11_default_mechanisms; |
| |
| void |
| SECMOD_Init() |
| { |
| /* don't initialize twice */ |
| if (moduleLock) |
| return; |
| |
| moduleLock = SECMOD_NewListLock(); |
| PK11_InitSlotLists(); |
| } |
| |
| SECStatus |
| SECMOD_Shutdown() |
| { |
| /* destroy the lock */ |
| if (moduleLock) { |
| SECMOD_DestroyListLock(moduleLock); |
| moduleLock = NULL; |
| } |
| /* free the internal module */ |
| if (internalModule) { |
| SECMOD_DestroyModule(internalModule); |
| internalModule = NULL; |
| } |
| |
| /* free the default database module */ |
| if (defaultDBModule) { |
| SECMOD_DestroyModule(defaultDBModule); |
| defaultDBModule = NULL; |
| } |
| |
| /* destroy the list */ |
| if (modules) { |
| SECMOD_DestroyModuleList(modules); |
| modules = NULL; |
| } |
| |
| if (modulesDB) { |
| SECMOD_DestroyModuleList(modulesDB); |
| modulesDB = NULL; |
| } |
| |
| if (modulesUnload) { |
| SECMOD_DestroyModuleList(modulesUnload); |
| modulesUnload = NULL; |
| } |
| |
| /* make all the slots and the lists go away */ |
| PK11_DestroySlotLists(); |
| |
| nss_DumpModuleLog(); |
| |
| #ifdef DEBUG |
| if (PR_GetEnvSecure("NSS_STRICT_SHUTDOWN")) { |
| PORT_Assert(secmod_PrivateModuleCount == 0); |
| } |
| #endif |
| if (secmod_PrivateModuleCount) { |
| PORT_SetError(SEC_ERROR_BUSY); |
| return SECFailure; |
| } |
| return SECSuccess; |
| } |
| |
| PRBool |
| SECMOD_GetSystemFIPSEnabled(void) |
| { |
| #ifdef LINUX |
| #ifndef NSS_FIPS_DISABLED |
| FILE *f; |
| char d; |
| size_t size; |
| |
| f = fopen("/proc/sys/crypto/fips_enabled", "r"); |
| if (!f) { |
| return PR_FALSE; |
| } |
| |
| size = fread(&d, 1, sizeof(d), f); |
| fclose(f); |
| if (size != sizeof(d)) { |
| return PR_FALSE; |
| } |
| if (d == '1') { |
| return PR_TRUE; |
| } |
| #endif |
| #endif |
| return PR_FALSE; |
| } |
| |
| /* |
| * retrieve the internal module |
| */ |
| SECMODModule * |
| SECMOD_GetInternalModule(void) |
| { |
| return internalModule; |
| } |
| |
| SECStatus |
| secmod_AddModuleToList(SECMODModuleList **moduleList, SECMODModule *newModule) |
| { |
| SECMODModuleList *mlp, *newListElement, *last = NULL; |
| |
| newListElement = SECMOD_NewModuleListElement(); |
| if (newListElement == NULL) { |
| return SECFailure; |
| } |
| |
| newListElement->module = SECMOD_ReferenceModule(newModule); |
| |
| SECMOD_GetWriteLock(moduleLock); |
| /* Added it to the end (This is very inefficient, but Adding a module |
| * on the fly should happen maybe 2-3 times through the life this program |
| * on a given computer, and this list should be *SHORT*. */ |
| for (mlp = *moduleList; mlp != NULL; mlp = mlp->next) { |
| last = mlp; |
| } |
| |
| if (last == NULL) { |
| *moduleList = newListElement; |
| } else { |
| SECMOD_AddList(last, newListElement, NULL); |
| } |
| SECMOD_ReleaseWriteLock(moduleLock); |
| return SECSuccess; |
| } |
| |
| SECStatus |
| SECMOD_AddModuleToList(SECMODModule *newModule) |
| { |
| if (newModule->internal && !internalModule) { |
| internalModule = SECMOD_ReferenceModule(newModule); |
| } |
| return secmod_AddModuleToList(&modules, newModule); |
| } |
| |
| SECStatus |
| SECMOD_AddModuleToDBOnlyList(SECMODModule *newModule) |
| { |
| if (defaultDBModule && SECMOD_GetDefaultModDBFlag(newModule)) { |
| SECMOD_DestroyModule(defaultDBModule); |
| defaultDBModule = SECMOD_ReferenceModule(newModule); |
| } else if (defaultDBModule == NULL) { |
| defaultDBModule = SECMOD_ReferenceModule(newModule); |
| } |
| return secmod_AddModuleToList(&modulesDB, newModule); |
| } |
| |
| SECStatus |
| SECMOD_AddModuleToUnloadList(SECMODModule *newModule) |
| { |
| return secmod_AddModuleToList(&modulesUnload, newModule); |
| } |
| |
| /* |
| * get the list of PKCS11 modules that are available. |
| */ |
| SECMODModuleList * |
| SECMOD_GetDefaultModuleList() |
| { |
| return modules; |
| } |
| SECMODModuleList * |
| SECMOD_GetDeadModuleList() |
| { |
| return modulesUnload; |
| } |
| SECMODModuleList * |
| SECMOD_GetDBModuleList() |
| { |
| return modulesDB; |
| } |
| |
| /* |
| * This lock protects the global module lists. |
| * it also protects changes to the slot array (module->slots[]) and slot count |
| * (module->slotCount) in each module. It is a read/write lock with multiple |
| * readers or one writer. Writes are uncommon. |
| * Because of legacy considerations protection of the slot array and count is |
| * only necessary in applications if the application calls |
| * SECMOD_UpdateSlotList() or SECMOD_WaitForAnyTokenEvent(), though all new |
| * applications are encouraged to acquire this lock when reading the |
| * slot array information directly. |
| */ |
| SECMODListLock * |
| SECMOD_GetDefaultModuleListLock() |
| { |
| return moduleLock; |
| } |
| |
| /* |
| * find a module by name, and add a reference to it. |
| * return that module. |
| */ |
| SECMODModule * |
| SECMOD_FindModule(const char *name) |
| { |
| SECMODModuleList *mlp; |
| SECMODModule *module = NULL; |
| |
| if (!moduleLock) { |
| PORT_SetError(SEC_ERROR_NOT_INITIALIZED); |
| return module; |
| } |
| SECMOD_GetReadLock(moduleLock); |
| for (mlp = modules; mlp != NULL; mlp = mlp->next) { |
| if (PORT_Strcmp(name, mlp->module->commonName) == 0) { |
| module = mlp->module; |
| SECMOD_ReferenceModule(module); |
| break; |
| } |
| } |
| if (module) { |
| goto found; |
| } |
| for (mlp = modulesUnload; mlp != NULL; mlp = mlp->next) { |
| if (PORT_Strcmp(name, mlp->module->commonName) == 0) { |
| module = mlp->module; |
| SECMOD_ReferenceModule(module); |
| break; |
| } |
| } |
| |
| found: |
| SECMOD_ReleaseReadLock(moduleLock); |
| |
| return module; |
| } |
| |
| /* |
| * find a module by ID, and add a reference to it. |
| * return that module. |
| */ |
| SECMODModule * |
| SECMOD_FindModuleByID(SECMODModuleID id) |
| { |
| SECMODModuleList *mlp; |
| SECMODModule *module = NULL; |
| |
| if (!moduleLock) { |
| PORT_SetError(SEC_ERROR_NOT_INITIALIZED); |
| return module; |
| } |
| SECMOD_GetReadLock(moduleLock); |
| for (mlp = modules; mlp != NULL; mlp = mlp->next) { |
| if (id == mlp->module->moduleID) { |
| module = mlp->module; |
| SECMOD_ReferenceModule(module); |
| break; |
| } |
| } |
| SECMOD_ReleaseReadLock(moduleLock); |
| if (module == NULL) { |
| PORT_SetError(SEC_ERROR_NO_MODULE); |
| } |
| return module; |
| } |
| |
| /* |
| * find the function pointer. |
| */ |
| SECMODModule * |
| secmod_FindModuleByFuncPtr(void *funcPtr) |
| { |
| SECMODModuleList *mlp; |
| SECMODModule *module = NULL; |
| |
| SECMOD_GetReadLock(moduleLock); |
| for (mlp = modules; mlp != NULL; mlp = mlp->next) { |
| /* paranoia, shouldn't ever happen */ |
| if (!mlp->module) { |
| continue; |
| } |
| if (funcPtr == mlp->module->functionList) { |
| module = mlp->module; |
| SECMOD_ReferenceModule(module); |
| break; |
| } |
| } |
| SECMOD_ReleaseReadLock(moduleLock); |
| if (module == NULL) { |
| PORT_SetError(SEC_ERROR_NO_MODULE); |
| } |
| return module; |
| } |
| |
| /* |
| * Find the Slot based on ID and the module. |
| */ |
| PK11SlotInfo * |
| SECMOD_FindSlotByID(SECMODModule *module, CK_SLOT_ID slotID) |
| { |
| int i; |
| PK11SlotInfo *slot = NULL; |
| |
| if (!moduleLock) { |
| PORT_SetError(SEC_ERROR_NOT_INITIALIZED); |
| return slot; |
| } |
| SECMOD_GetReadLock(moduleLock); |
| for (i = 0; i < module->slotCount; i++) { |
| PK11SlotInfo *cSlot = module->slots[i]; |
| |
| if (cSlot->slotID == slotID) { |
| slot = PK11_ReferenceSlot(cSlot); |
| break; |
| } |
| } |
| SECMOD_ReleaseReadLock(moduleLock); |
| |
| if (slot == NULL) { |
| PORT_SetError(SEC_ERROR_NO_SLOT_SELECTED); |
| } |
| return slot; |
| } |
| |
| /* |
| * lookup the Slot module based on it's module ID and slot ID. |
| */ |
| PK11SlotInfo * |
| SECMOD_LookupSlot(SECMODModuleID moduleID, CK_SLOT_ID slotID) |
| { |
| SECMODModule *module; |
| PK11SlotInfo *slot; |
| |
| module = SECMOD_FindModuleByID(moduleID); |
| if (module == NULL) |
| return NULL; |
| |
| slot = SECMOD_FindSlotByID(module, slotID); |
| SECMOD_DestroyModule(module); |
| return slot; |
| } |
| |
| /* |
| * find a module by name or module pointer and delete it off the module list. |
| * optionally remove it from secmod.db. |
| */ |
| SECStatus |
| SECMOD_DeleteModuleEx(const char *name, SECMODModule *mod, |
| int *type, PRBool permdb) |
| { |
| SECMODModuleList *mlp; |
| SECMODModuleList **mlpp; |
| SECStatus rv = SECFailure; |
| |
| if (!moduleLock) { |
| PORT_SetError(SEC_ERROR_NOT_INITIALIZED); |
| return rv; |
| } |
| |
| *type = SECMOD_EXTERNAL; |
| |
| SECMOD_GetWriteLock(moduleLock); |
| for (mlpp = &modules, mlp = modules; |
| mlp != NULL; mlpp = &mlp->next, mlp = *mlpp) { |
| if ((name && (PORT_Strcmp(name, mlp->module->commonName) == 0)) || |
| mod == mlp->module) { |
| /* don't delete the internal module */ |
| if (!mlp->module->internal) { |
| SECMOD_RemoveList(mlpp, mlp); |
| /* delete it after we release the lock */ |
| rv = STAN_RemoveModuleFromDefaultTrustDomain(mlp->module); |
| } else if (mlp->module->isFIPS) { |
| *type = SECMOD_FIPS; |
| } else { |
| *type = SECMOD_INTERNAL; |
| } |
| break; |
| } |
| } |
| if (mlp) { |
| goto found; |
| } |
| /* not on the internal list, check the unload list */ |
| for (mlpp = &modulesUnload, mlp = modulesUnload; |
| mlp != NULL; mlpp = &mlp->next, mlp = *mlpp) { |
| if ((name && (PORT_Strcmp(name, mlp->module->commonName) == 0)) || |
| mod == mlp->module) { |
| /* don't delete the internal module */ |
| if (!mlp->module->internal) { |
| SECMOD_RemoveList(mlpp, mlp); |
| rv = SECSuccess; |
| } else if (mlp->module->isFIPS) { |
| *type = SECMOD_FIPS; |
| } else { |
| *type = SECMOD_INTERNAL; |
| } |
| break; |
| } |
| } |
| found: |
| SECMOD_ReleaseWriteLock(moduleLock); |
| |
| if (rv == SECSuccess) { |
| if (permdb) { |
| SECMOD_DeletePermDB(mlp->module); |
| } |
| SECMOD_DestroyModuleListElement(mlp); |
| } |
| return rv; |
| } |
| |
| /* |
| * find a module by name and delete it off the module list |
| */ |
| SECStatus |
| SECMOD_DeleteModule(const char *name, int *type) |
| { |
| return SECMOD_DeleteModuleEx(name, NULL, type, PR_TRUE); |
| } |
| |
| /* |
| * find a module by name and delete it off the module list |
| */ |
| SECStatus |
| SECMOD_DeleteInternalModule(const char *name) |
| { |
| SECMODModuleList *mlp; |
| SECMODModuleList **mlpp; |
| SECStatus rv = SECFailure; |
| |
| if (SECMOD_GetSystemFIPSEnabled() || pendingModule) { |
| PORT_SetError(SEC_ERROR_MODULE_STUCK); |
| return rv; |
| } |
| if (!moduleLock) { |
| PORT_SetError(SEC_ERROR_NOT_INITIALIZED); |
| return rv; |
| } |
| |
| #ifdef NSS_FIPS_DISABLED |
| PORT_SetError(PR_OPERATION_NOT_SUPPORTED_ERROR); |
| return rv; |
| #endif |
| |
| SECMOD_GetWriteLock(moduleLock); |
| for (mlpp = &modules, mlp = modules; |
| mlp != NULL; mlpp = &mlp->next, mlp = *mlpp) { |
| if (PORT_Strcmp(name, mlp->module->commonName) == 0) { |
| /* don't delete the internal module */ |
| if (mlp->module->internal) { |
| SECMOD_RemoveList(mlpp, mlp); |
| rv = STAN_RemoveModuleFromDefaultTrustDomain(mlp->module); |
| } |
| break; |
| } |
| } |
| SECMOD_ReleaseWriteLock(moduleLock); |
| |
| if (rv == SECSuccess) { |
| SECMODModule *newModule, *oldModule; |
| |
| if (mlp->module->isFIPS) { |
| newModule = SECMOD_CreateModule(NULL, SECMOD_INT_NAME, |
| NULL, SECMOD_INT_FLAGS); |
| } else { |
| newModule = SECMOD_CreateModule(NULL, SECMOD_FIPS_NAME, |
| NULL, SECMOD_FIPS_FLAGS); |
| } |
| if (newModule) { |
| PK11SlotInfo *slot; |
| newModule->libraryParams = |
| PORT_ArenaStrdup(newModule->arena, mlp->module->libraryParams); |
| /* if an explicit internal key slot has been set, reset it */ |
| slot = pk11_SwapInternalKeySlot(NULL); |
| if (slot) { |
| secmod_SetInternalKeySlotFlag(newModule, PR_TRUE); |
| } |
| rv = SECMOD_AddModule(newModule); |
| if (rv != SECSuccess) { |
| /* load failed, restore the internal key slot */ |
| pk11_SetInternalKeySlot(slot); |
| SECMOD_DestroyModule(newModule); |
| newModule = NULL; |
| } |
| /* free the old explicit internal key slot, we now have a new one */ |
| if (slot) { |
| PK11_FreeSlot(slot); |
| } |
| } |
| if (newModule == NULL) { |
| SECMODModuleList *last = NULL, *mlp2; |
| /* we're in pretty deep trouble if this happens...Security |
| * not going to work well... try to put the old module back on |
| * the list */ |
| SECMOD_GetWriteLock(moduleLock); |
| for (mlp2 = modules; mlp2 != NULL; mlp2 = mlp->next) { |
| last = mlp2; |
| } |
| |
| if (last == NULL) { |
| modules = mlp; |
| } else { |
| SECMOD_AddList(last, mlp, NULL); |
| } |
| SECMOD_ReleaseWriteLock(moduleLock); |
| return SECFailure; |
| } |
| pendingModule = oldModule = internalModule; |
| internalModule = NULL; |
| SECMOD_DestroyModule(oldModule); |
| SECMOD_DeletePermDB(mlp->module); |
| SECMOD_DestroyModuleListElement(mlp); |
| internalModule = newModule; /* adopt the module */ |
| } |
| return rv; |
| } |
| |
| SECStatus |
| SECMOD_AddModule(SECMODModule *newModule) |
| { |
| SECStatus rv; |
| SECMODModule *oldModule; |
| |
| /* Test if a module w/ the same name already exists */ |
| /* and return SECWouldBlock if so. */ |
| /* We should probably add a new return value such as */ |
| /* SECDublicateModule, but to minimize ripples, I'll */ |
| /* give SECWouldBlock a new meaning */ |
| if ((oldModule = SECMOD_FindModule(newModule->commonName)) != NULL) { |
| SECMOD_DestroyModule(oldModule); |
| return SECWouldBlock; |
| /* module already exists. */ |
| } |
| |
| rv = secmod_LoadPKCS11Module(newModule, NULL); |
| if (rv != SECSuccess) { |
| return rv; |
| } |
| |
| if (newModule->parent == NULL) { |
| newModule->parent = SECMOD_ReferenceModule(defaultDBModule); |
| } |
| |
| SECMOD_AddPermDB(newModule); |
| SECMOD_AddModuleToList(newModule); |
| |
| rv = STAN_AddModuleToDefaultTrustDomain(newModule); |
| |
| return rv; |
| } |
| |
| PK11SlotInfo * |
| SECMOD_FindSlot(SECMODModule *module, const char *name) |
| { |
| int i; |
| char *string; |
| PK11SlotInfo *retSlot = NULL; |
| |
| if (!moduleLock) { |
| PORT_SetError(SEC_ERROR_NOT_INITIALIZED); |
| return retSlot; |
| } |
| SECMOD_GetReadLock(moduleLock); |
| for (i = 0; i < module->slotCount; i++) { |
| PK11SlotInfo *slot = module->slots[i]; |
| |
| if (PK11_IsPresent(slot)) { |
| string = PK11_GetTokenName(slot); |
| } else { |
| string = PK11_GetSlotName(slot); |
| } |
| if (PORT_Strcmp(name, string) == 0) { |
| retSlot = PK11_ReferenceSlot(slot); |
| break; |
| } |
| } |
| SECMOD_ReleaseReadLock(moduleLock); |
| |
| if (retSlot == NULL) { |
| PORT_SetError(SEC_ERROR_NO_SLOT_SELECTED); |
| } |
| return retSlot; |
| } |
| |
| SECStatus |
| PK11_GetModInfo(SECMODModule *mod, CK_INFO *info) |
| { |
| CK_RV crv; |
| |
| if (mod->functionList == NULL) |
| return SECFailure; |
| crv = PK11_GETTAB(mod)->C_GetInfo(info); |
| if (crv != CKR_OK) { |
| PORT_SetError(PK11_MapError(crv)); |
| } |
| return (crv == CKR_OK) ? SECSuccess : SECFailure; |
| } |
| |
| char * |
| PK11_GetModuleURI(SECMODModule *mod) |
| { |
| CK_INFO info; |
| PK11URI *uri; |
| char *ret = NULL; |
| PK11URIAttribute attrs[3]; |
| size_t nattrs = 0; |
| char libraryManufacturer[32 + 1], libraryDescription[32 + 1], libraryVersion[8]; |
| |
| if (PK11_GetModInfo(mod, &info) == SECFailure) { |
| return NULL; |
| } |
| |
| PK11_MakeString(NULL, libraryManufacturer, (char *)info.manufacturerID, |
| sizeof(info.manufacturerID)); |
| if (*libraryManufacturer != '\0') { |
| attrs[nattrs].name = PK11URI_PATTR_LIBRARY_MANUFACTURER; |
| attrs[nattrs].value = libraryManufacturer; |
| nattrs++; |
| } |
| |
| PK11_MakeString(NULL, libraryDescription, (char *)info.libraryDescription, |
| sizeof(info.libraryDescription)); |
| if (*libraryDescription != '\0') { |
| attrs[nattrs].name = PK11URI_PATTR_LIBRARY_DESCRIPTION; |
| attrs[nattrs].value = libraryDescription; |
| nattrs++; |
| } |
| |
| PR_snprintf(libraryVersion, sizeof(libraryVersion), "%d.%d", |
| info.libraryVersion.major, info.libraryVersion.minor); |
| attrs[nattrs].name = PK11URI_PATTR_LIBRARY_VERSION; |
| attrs[nattrs].value = libraryVersion; |
| nattrs++; |
| |
| uri = PK11URI_CreateURI(attrs, nattrs, NULL, 0); |
| if (uri == NULL) { |
| PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); |
| return NULL; |
| } |
| |
| ret = PK11URI_FormatURI(NULL, uri); |
| PK11URI_DestroyURI(uri); |
| if (ret == NULL) { |
| PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); |
| return NULL; |
| } |
| |
| return ret; |
| } |
| |
| /* Determine if we have the FIP's module loaded as the default |
| * module to trigger other bogus FIPS requirements in PKCS #12 and |
| * SSL |
| */ |
| PRBool |
| PK11_IsFIPS(void) |
| { |
| SECMODModule *mod = SECMOD_GetInternalModule(); |
| |
| if (mod && mod->internal) { |
| return mod->isFIPS; |
| } |
| |
| return PR_FALSE; |
| } |
| |
| /* combines NewModule() & AddModule */ |
| /* give a string for the module name & the full-path for the dll, */ |
| /* installs the PKCS11 module & update registry */ |
| SECStatus |
| SECMOD_AddNewModuleEx(const char *moduleName, const char *dllPath, |
| unsigned long defaultMechanismFlags, |
| unsigned long cipherEnableFlags, |
| char *modparms, char *nssparms) |
| { |
| SECMODModule *module; |
| SECStatus result = SECFailure; |
| int s, i; |
| PK11SlotInfo *slot; |
| |
| PR_SetErrorText(0, NULL); |
| if (!moduleLock) { |
| PORT_SetError(SEC_ERROR_NOT_INITIALIZED); |
| return result; |
| } |
| |
| module = SECMOD_CreateModule(dllPath, moduleName, modparms, nssparms); |
| |
| if (module == NULL) { |
| return result; |
| } |
| |
| if (module->dllName != NULL) { |
| if (module->dllName[0] != 0) { |
| result = SECMOD_AddModule(module); |
| if (result == SECSuccess) { |
| /* turn on SSL cipher enable flags */ |
| module->ssl[0] = cipherEnableFlags; |
| |
| SECMOD_GetReadLock(moduleLock); |
| /* check each slot to turn on appropriate mechanisms */ |
| for (s = 0; s < module->slotCount; s++) { |
| slot = (module->slots)[s]; |
| /* for each possible mechanism */ |
| for (i = 0; i < num_pk11_default_mechanisms; i++) { |
| /* we are told to turn it on by default ? */ |
| PRBool add = |
| (PK11_DefaultArray[i].flag & defaultMechanismFlags) ? PR_TRUE : PR_FALSE; |
| result = PK11_UpdateSlotAttribute(slot, |
| &(PK11_DefaultArray[i]), add); |
| if (result != SECSuccess) { |
| SECMOD_ReleaseReadLock(moduleLock); |
| SECMOD_DestroyModule(module); |
| return result; |
| } |
| } /* for each mechanism */ |
| /* disable each slot if the defaultFlags say so */ |
| if (defaultMechanismFlags & PK11_DISABLE_FLAG) { |
| PK11_UserDisableSlot(slot); |
| } |
| } /* for each slot of this module */ |
| SECMOD_ReleaseReadLock(moduleLock); |
| |
| /* delete and re-add module in order to save changes |
| * to the module */ |
| result = SECMOD_UpdateModule(module); |
| } |
| } |
| } |
| SECMOD_DestroyModule(module); |
| return result; |
| } |
| |
| SECStatus |
| SECMOD_AddNewModule(const char *moduleName, const char *dllPath, |
| unsigned long defaultMechanismFlags, |
| unsigned long cipherEnableFlags) |
| { |
| return SECMOD_AddNewModuleEx(moduleName, dllPath, defaultMechanismFlags, |
| cipherEnableFlags, |
| NULL, NULL); /* don't pass module or nss params */ |
| } |
| |
| SECStatus |
| SECMOD_UpdateModule(SECMODModule *module) |
| { |
| SECStatus result; |
| |
| result = SECMOD_DeletePermDB(module); |
| |
| if (result == SECSuccess) { |
| result = SECMOD_AddPermDB(module); |
| } |
| return result; |
| } |
| |
| /* Public & Internal(Security Library) representation of |
| * encryption mechanism flags conversion */ |
| |
| /* Currently, the only difference is that internal representation |
| * puts RANDOM_FLAG at bit 31 (Most-significant bit), but |
| * public representation puts this bit at bit 28 |
| */ |
| unsigned long |
| SECMOD_PubMechFlagstoInternal(unsigned long publicFlags) |
| { |
| unsigned long internalFlags = publicFlags; |
| |
| if (publicFlags & PUBLIC_MECH_RANDOM_FLAG) { |
| internalFlags &= ~PUBLIC_MECH_RANDOM_FLAG; |
| internalFlags |= SECMOD_RANDOM_FLAG; |
| } |
| return internalFlags; |
| } |
| |
| unsigned long |
| SECMOD_InternaltoPubMechFlags(unsigned long internalFlags) |
| { |
| unsigned long publicFlags = internalFlags; |
| |
| if (internalFlags & SECMOD_RANDOM_FLAG) { |
| publicFlags &= ~SECMOD_RANDOM_FLAG; |
| publicFlags |= PUBLIC_MECH_RANDOM_FLAG; |
| } |
| return publicFlags; |
| } |
| |
| /* Public & Internal(Security Library) representation of */ |
| /* cipher flags conversion */ |
| /* Note: currently they are just stubs */ |
| unsigned long |
| SECMOD_PubCipherFlagstoInternal(unsigned long publicFlags) |
| { |
| return publicFlags; |
| } |
| |
| unsigned long |
| SECMOD_InternaltoPubCipherFlags(unsigned long internalFlags) |
| { |
| return internalFlags; |
| } |
| |
| /* Funtion reports true if module of modType is installed/configured */ |
| PRBool |
| SECMOD_IsModulePresent(unsigned long int pubCipherEnableFlags) |
| { |
| PRBool result = PR_FALSE; |
| SECMODModuleList *mods; |
| |
| if (!moduleLock) { |
| PORT_SetError(SEC_ERROR_NOT_INITIALIZED); |
| return result; |
| } |
| SECMOD_GetReadLock(moduleLock); |
| mods = SECMOD_GetDefaultModuleList(); |
| for (; mods != NULL; mods = mods->next) { |
| if (mods->module->ssl[0] & |
| SECMOD_PubCipherFlagstoInternal(pubCipherEnableFlags)) { |
| result = PR_TRUE; |
| } |
| } |
| |
| SECMOD_ReleaseReadLock(moduleLock); |
| return result; |
| } |
| |
| /* create a new ModuleListElement */ |
| SECMODModuleList * |
| SECMOD_NewModuleListElement(void) |
| { |
| SECMODModuleList *newModList; |
| |
| newModList = (SECMODModuleList *)PORT_Alloc(sizeof(SECMODModuleList)); |
| if (newModList) { |
| newModList->next = NULL; |
| newModList->module = NULL; |
| } |
| return newModList; |
| } |
| |
| /* |
| * make a new reference to a module so It doesn't go away on us |
| */ |
| SECMODModule * |
| SECMOD_ReferenceModule(SECMODModule *module) |
| { |
| PZ_Lock(module->refLock); |
| PORT_Assert(module->refCount > 0); |
| |
| module->refCount++; |
| PZ_Unlock(module->refLock); |
| return module; |
| } |
| |
| /* destroy an existing module */ |
| void |
| SECMOD_DestroyModule(SECMODModule *module) |
| { |
| PRBool willfree = PR_FALSE; |
| int slotCount; |
| int i; |
| |
| PZ_Lock(module->refLock); |
| if (module->refCount-- == 1) { |
| willfree = PR_TRUE; |
| } |
| PORT_Assert(willfree || (module->refCount > 0)); |
| PZ_Unlock(module->refLock); |
| |
| if (!willfree) { |
| return; |
| } |
| |
| if (module->parent != NULL) { |
| SECMODModule *parent = module->parent; |
| /* paranoia, don't loop forever if the modules are looped */ |
| module->parent = NULL; |
| SECMOD_DestroyModule(parent); |
| } |
| |
| /* slots can't really disappear until our module starts freeing them, |
| * so this check is safe */ |
| slotCount = module->slotCount; |
| if (slotCount == 0) { |
| SECMOD_SlotDestroyModule(module, PR_FALSE); |
| return; |
| } |
| |
| /* now free all out slots, when they are done, they will cause the |
| * module to disappear altogether */ |
| for (i = 0; i < slotCount; i++) { |
| if (!module->slots[i]->disabled) { |
| PK11_ClearSlotList(module->slots[i]); |
| } |
| PK11_FreeSlot(module->slots[i]); |
| } |
| /* WARNING: once the last slot has been freed is it possible (even likely) |
| * that module is no more... touching it now is a good way to go south */ |
| } |
| |
| /* we can only get here if we've destroyed the module, or some one has |
| * erroneously freed a slot that wasn't referenced. */ |
| void |
| SECMOD_SlotDestroyModule(SECMODModule *module, PRBool fromSlot) |
| { |
| PRBool willfree = PR_FALSE; |
| if (fromSlot) { |
| PORT_Assert(module->refCount == 0); |
| PZ_Lock(module->refLock); |
| if (module->slotCount-- == 1) { |
| willfree = PR_TRUE; |
| } |
| PORT_Assert(willfree || (module->slotCount > 0)); |
| PZ_Unlock(module->refLock); |
| if (!willfree) |
| return; |
| } |
| |
| if (module == pendingModule) { |
| pendingModule = NULL; |
| } |
| |
| if (module->loaded) { |
| SECMOD_UnloadModule(module); |
| } |
| PZ_DestroyLock(module->refLock); |
| PORT_FreeArena(module->arena, PR_FALSE); |
| secmod_PrivateModuleCount--; |
| } |
| |
| /* destroy a list element |
| * this destroys a single element, and returns the next element |
| * on the chain. It makes it easy to implement for loops to delete |
| * the chain. It also make deleting a single element easy */ |
| SECMODModuleList * |
| SECMOD_DestroyModuleListElement(SECMODModuleList *element) |
| { |
| SECMODModuleList *next = element->next; |
| |
| if (element->module) { |
| SECMOD_DestroyModule(element->module); |
| element->module = NULL; |
| } |
| PORT_Free(element); |
| return next; |
| } |
| |
| /* |
| * Destroy an entire module list |
| */ |
| void |
| SECMOD_DestroyModuleList(SECMODModuleList *list) |
| { |
| SECMODModuleList *lp; |
| |
| for (lp = list; lp != NULL; lp = SECMOD_DestroyModuleListElement(lp)) |
| ; |
| } |
| |
| PRBool |
| SECMOD_CanDeleteInternalModule(void) |
| { |
| #ifdef NSS_FIPS_DISABLED |
| return PR_FALSE; |
| #else |
| return (PRBool)((pendingModule == NULL) && !SECMOD_GetSystemFIPSEnabled()); |
| #endif |
| } |
| |
| /* |
| * check to see if the module has added new slots. PKCS 11 v2.20 allows for |
| * modules to add new slots, but never remove them. Slots cannot be added |
| * between a call to C_GetSlotLlist(Flag, NULL, &count) and the subsequent |
| * C_GetSlotList(flag, &data, &count) so that the array doesn't accidently |
| * grow on the caller. It is permissible for the slots to increase between |
| * successive calls with NULL to get the size. |
| */ |
| SECStatus |
| SECMOD_UpdateSlotList(SECMODModule *mod) |
| { |
| CK_RV crv; |
| CK_ULONG count; |
| CK_ULONG i, oldCount; |
| PRBool freeRef = PR_FALSE; |
| void *mark = NULL; |
| CK_ULONG *slotIDs = NULL; |
| PK11SlotInfo **newSlots = NULL; |
| PK11SlotInfo **oldSlots = NULL; |
| |
| if (!moduleLock) { |
| PORT_SetError(SEC_ERROR_NOT_INITIALIZED); |
| return SECFailure; |
| } |
| |
| /* C_GetSlotList is not a session function, make sure |
| * calls are serialized */ |
| PZ_Lock(mod->refLock); |
| freeRef = PR_TRUE; |
| /* see if the number of slots have changed */ |
| crv = PK11_GETTAB(mod)->C_GetSlotList(PR_FALSE, NULL, &count); |
| if (crv != CKR_OK) { |
| PORT_SetError(PK11_MapError(crv)); |
| goto loser; |
| } |
| /* nothing new, blow out early, we want this function to be quick |
| * and cheap in the normal case */ |
| if (count == mod->slotCount) { |
| PZ_Unlock(mod->refLock); |
| return SECSuccess; |
| } |
| if (count < (CK_ULONG)mod->slotCount) { |
| /* shouldn't happen with a properly functioning PKCS #11 module */ |
| PORT_SetError(SEC_ERROR_INCOMPATIBLE_PKCS11); |
| goto loser; |
| } |
| |
| /* get the new slot list */ |
| slotIDs = PORT_NewArray(CK_SLOT_ID, count); |
| if (slotIDs == NULL) { |
| goto loser; |
| } |
| |
| crv = PK11_GETTAB(mod)->C_GetSlotList(PR_FALSE, slotIDs, &count); |
| if (crv != CKR_OK) { |
| PORT_SetError(PK11_MapError(crv)); |
| goto loser; |
| } |
| freeRef = PR_FALSE; |
| PZ_Unlock(mod->refLock); |
| mark = PORT_ArenaMark(mod->arena); |
| if (mark == NULL) { |
| goto loser; |
| } |
| newSlots = PORT_ArenaZNewArray(mod->arena, PK11SlotInfo *, count); |
| |
| /* walk down the new slot ID list returned from the module. We keep |
| * the old slots which match a returned ID, and we initialize the new |
| * slots. */ |
| for (i = 0; i < count; i++) { |
| PK11SlotInfo *slot = SECMOD_FindSlotByID(mod, slotIDs[i]); |
| |
| if (!slot) { |
| /* we have a new slot create a new slot data structure */ |
| slot = PK11_NewSlotInfo(mod); |
| if (!slot) { |
| goto loser; |
| } |
| PK11_InitSlot(mod, slotIDs[i], slot); |
| STAN_InitTokenForSlotInfo(NULL, slot); |
| } |
| newSlots[i] = slot; |
| } |
| STAN_ResetTokenInterator(NULL); |
| PORT_Free(slotIDs); |
| slotIDs = NULL; |
| PORT_ArenaUnmark(mod->arena, mark); |
| |
| /* until this point we're still using the old slot list. Now we update |
| * module slot list. We update the slots (array) first then the count, |
| * since we've already guarrenteed that count has increased (just in case |
| * someone is looking at the slots field of module without holding the |
| * moduleLock */ |
| SECMOD_GetWriteLock(moduleLock); |
| oldCount = mod->slotCount; |
| oldSlots = mod->slots; |
| mod->slots = newSlots; /* typical arena 'leak'... old mod->slots is |
| * allocated out of the module arena and won't |
| * be freed until the module is freed */ |
| mod->slotCount = count; |
| SECMOD_ReleaseWriteLock(moduleLock); |
| /* free our old references before forgetting about oldSlot*/ |
| for (i = 0; i < oldCount; i++) { |
| PK11_FreeSlot(oldSlots[i]); |
| } |
| return SECSuccess; |
| |
| loser: |
| if (freeRef) { |
| PZ_Unlock(mod->refLock); |
| } |
| if (slotIDs) { |
| PORT_Free(slotIDs); |
| } |
| /* free all the slots we allocated. newSlots are part of the |
| * mod arena. NOTE: the newSlots array contain both new and old |
| * slots, but we kept a reference to the old slots when we built the new |
| * array, so we need to free all the slots in newSlots array. */ |
| if (newSlots) { |
| for (i = 0; i < count; i++) { |
| if (newSlots[i] == NULL) { |
| break; /* hit the last one */ |
| } |
| PK11_FreeSlot(newSlots[i]); |
| } |
| } |
| /* must come after freeing newSlots */ |
| if (mark) { |
| PORT_ArenaRelease(mod->arena, mark); |
| } |
| return SECFailure; |
| } |
| |
| /* |
| * this handles modules that do not support C_WaitForSlotEvent(). |
| * The internal flags are stored. Note that C_WaitForSlotEvent() does not |
| * have a timeout, so we don't have one for handleWaitForSlotEvent() either. |
| */ |
| PK11SlotInfo * |
| secmod_HandleWaitForSlotEvent(SECMODModule *mod, unsigned long flags, |
| PRIntervalTime latency) |
| { |
| PRBool removableSlotsFound = PR_FALSE; |
| int i; |
| int error = SEC_ERROR_NO_EVENT; |
| |
| if (!moduleLock) { |
| PORT_SetError(SEC_ERROR_NOT_INITIALIZED); |
| return NULL; |
| } |
| PZ_Lock(mod->refLock); |
| if (mod->evControlMask & SECMOD_END_WAIT) { |
| mod->evControlMask &= ~SECMOD_END_WAIT; |
| PZ_Unlock(mod->refLock); |
| PORT_SetError(SEC_ERROR_NO_EVENT); |
| return NULL; |
| } |
| mod->evControlMask |= SECMOD_WAIT_SIMULATED_EVENT; |
| while (mod->evControlMask & SECMOD_WAIT_SIMULATED_EVENT) { |
| PZ_Unlock(mod->refLock); |
| /* now is a good time to see if new slots have been added */ |
| SECMOD_UpdateSlotList(mod); |
| |
| /* loop through all the slots on a module */ |
| SECMOD_GetReadLock(moduleLock); |
| for (i = 0; i < mod->slotCount; i++) { |
| PK11SlotInfo *slot = mod->slots[i]; |
| PRUint16 series; |
| PRBool present; |
| |
| /* perm modules do not change */ |
| if (slot->isPerm) { |
| continue; |
| } |
| removableSlotsFound = PR_TRUE; |
| /* simulate the PKCS #11 module flags. are the flags different |
| * from the last time we called? */ |
| series = slot->series; |
| present = PK11_IsPresent(slot); |
| if ((slot->flagSeries != series) || (slot->flagState != present)) { |
| slot->flagState = present; |
| slot->flagSeries = series; |
| SECMOD_ReleaseReadLock(moduleLock); |
| PZ_Lock(mod->refLock); |
| mod->evControlMask &= ~SECMOD_END_WAIT; |
| PZ_Unlock(mod->refLock); |
| return PK11_ReferenceSlot(slot); |
| } |
| } |
| SECMOD_ReleaseReadLock(moduleLock); |
| /* if everything was perm modules, don't lock up forever */ |
| if ((mod->slotCount != 0) && !removableSlotsFound) { |
| error = SEC_ERROR_NO_SLOT_SELECTED; |
| PZ_Lock(mod->refLock); |
| break; |
| } |
| if (flags & CKF_DONT_BLOCK) { |
| PZ_Lock(mod->refLock); |
| break; |
| } |
| PR_Sleep(latency); |
| PZ_Lock(mod->refLock); |
| } |
| mod->evControlMask &= ~SECMOD_END_WAIT; |
| PZ_Unlock(mod->refLock); |
| PORT_SetError(error); |
| return NULL; |
| } |
| |
| /* |
| * this function waits for a token event on any slot of a given module |
| * This function should not be called from more than one thread of the |
| * same process (though other threads can make other library calls |
| * on this module while this call is blocked). |
| */ |
| PK11SlotInfo * |
| SECMOD_WaitForAnyTokenEvent(SECMODModule *mod, unsigned long flags, |
| PRIntervalTime latency) |
| { |
| CK_SLOT_ID id; |
| CK_RV crv; |
| PK11SlotInfo *slot; |
| |
| if (!pk11_getFinalizeModulesOption() || |
| ((mod->cryptokiVersion.major == 2) && |
| (mod->cryptokiVersion.minor < 1))) { |
| /* if we are sharing the module with other software in our |
| * address space, we can't reliably use C_WaitForSlotEvent(), |
| * and if the module is version 2.0, C_WaitForSlotEvent() doesn't |
| * exist */ |
| return secmod_HandleWaitForSlotEvent(mod, flags, latency); |
| } |
| /* first the the PKCS #11 call */ |
| PZ_Lock(mod->refLock); |
| if (mod->evControlMask & SECMOD_END_WAIT) { |
| goto end_wait; |
| } |
| mod->evControlMask |= SECMOD_WAIT_PKCS11_EVENT; |
| PZ_Unlock(mod->refLock); |
| crv = PK11_GETTAB(mod)->C_WaitForSlotEvent(flags, &id, NULL); |
| PZ_Lock(mod->refLock); |
| mod->evControlMask &= ~SECMOD_WAIT_PKCS11_EVENT; |
| /* if we are in end wait, short circuit now, don't even risk |
| * going into secmod_HandleWaitForSlotEvent */ |
| if (mod->evControlMask & SECMOD_END_WAIT) { |
| goto end_wait; |
| } |
| PZ_Unlock(mod->refLock); |
| if (crv == CKR_FUNCTION_NOT_SUPPORTED) { |
| /* module doesn't support that call, simulate it */ |
| return secmod_HandleWaitForSlotEvent(mod, flags, latency); |
| } |
| if (crv != CKR_OK) { |
| /* we can get this error if finalize was called while we were |
| * still running. This is the only way to force a C_WaitForSlotEvent() |
| * to return in PKCS #11. In this case, just return that there |
| * was no event. */ |
| if (crv == CKR_CRYPTOKI_NOT_INITIALIZED) { |
| PORT_SetError(SEC_ERROR_NO_EVENT); |
| } else { |
| PORT_SetError(PK11_MapError(crv)); |
| } |
| return NULL; |
| } |
| slot = SECMOD_FindSlotByID(mod, id); |
| if (slot == NULL) { |
| /* possibly a new slot that was added? */ |
| SECMOD_UpdateSlotList(mod); |
| slot = SECMOD_FindSlotByID(mod, id); |
| } |
| /* if we are in the delay period for the "isPresent" call, reset |
| * the delay since we know things have probably changed... */ |
| if (slot && slot->nssToken && slot->nssToken->slot) { |
| nssSlot_ResetDelay(slot->nssToken->slot); |
| } |
| return slot; |
| |
| /* must be called with the lock on. */ |
| end_wait: |
| mod->evControlMask &= ~SECMOD_END_WAIT; |
| PZ_Unlock(mod->refLock); |
| PORT_SetError(SEC_ERROR_NO_EVENT); |
| return NULL; |
| } |
| |
| /* |
| * This function "wakes up" WaitForAnyTokenEvent. It's a pretty drastic |
| * function, possibly bringing down the pkcs #11 module in question. This |
| * should be OK because 1) it does reinitialize, and 2) it should only be |
| * called when we are on our way to tear the whole system down anyway. |
| */ |
| SECStatus |
| SECMOD_CancelWait(SECMODModule *mod) |
| { |
| unsigned long controlMask; |
| SECStatus rv = SECSuccess; |
| CK_RV crv; |
| |
| PZ_Lock(mod->refLock); |
| mod->evControlMask |= SECMOD_END_WAIT; |
| controlMask = mod->evControlMask; |
| if (controlMask & SECMOD_WAIT_PKCS11_EVENT) { |
| if (!pk11_getFinalizeModulesOption()) { |
| /* can't get here unless pk11_getFinalizeModulesOption is set */ |
| PORT_Assert(0); |
| PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); |
| rv = SECFailure; |
| goto loser; |
| } |
| /* NOTE: this call will drop all transient keys, in progress |
| * operations, and any authentication. This is the only documented |
| * way to get WaitForSlotEvent to return. Also note: for non-thread |
| * safe tokens, we need to hold the module lock, this is not yet at |
| * system shutdown/startup time, so we need to protect these calls */ |
| crv = PK11_GETTAB(mod)->C_Finalize(NULL); |
| /* ok, we slammed the module down, now we need to reinit it in case |
| * we intend to use it again */ |
| if (CKR_OK == crv) { |
| PRBool alreadyLoaded; |
| secmod_ModuleInit(mod, NULL, &alreadyLoaded); |
| } else { |
| /* Finalized failed for some reason, notify the application |
| * so maybe it has a prayer of recovering... */ |
| PORT_SetError(PK11_MapError(crv)); |
| rv = SECFailure; |
| } |
| } else if (controlMask & SECMOD_WAIT_SIMULATED_EVENT) { |
| mod->evControlMask &= ~SECMOD_WAIT_SIMULATED_EVENT; |
| /* Simulated events will eventually timeout |
| * and wake up in the loop */ |
| } |
| loser: |
| PZ_Unlock(mod->refLock); |
| return rv; |
| } |
| |
| /* |
| * check to see if the module has removable slots that we may need to |
| * watch for. |
| */ |
| PRBool |
| SECMOD_HasRemovableSlots(SECMODModule *mod) |
| { |
| int i; |
| PRBool ret = PR_FALSE; |
| |
| if (!moduleLock) { |
| PORT_SetError(SEC_ERROR_NOT_INITIALIZED); |
| return ret; |
| } |
| SECMOD_GetReadLock(moduleLock); |
| for (i = 0; i < mod->slotCount; i++) { |
| PK11SlotInfo *slot = mod->slots[i]; |
| /* perm modules are not inserted or removed */ |
| if (slot->isPerm) { |
| continue; |
| } |
| ret = PR_TRUE; |
| break; |
| } |
| if (mod->slotCount == 0) { |
| ret = PR_TRUE; |
| } |
| SECMOD_ReleaseReadLock(moduleLock); |
| return ret; |
| } |
| |
| /* |
| * helper function to actually create and destroy user defined slots |
| */ |
| static SECStatus |
| secmod_UserDBOp(PK11SlotInfo *slot, CK_OBJECT_CLASS objClass, |
| const char *sendSpec) |
| { |
| CK_OBJECT_HANDLE dummy; |
| CK_ATTRIBUTE template[2]; |
| CK_ATTRIBUTE *attrs = template; |
| CK_RV crv; |
| |
| PK11_SETATTRS(attrs, CKA_CLASS, &objClass, sizeof(objClass)); |
| attrs++; |
| PK11_SETATTRS(attrs, CKA_NSS_MODULE_SPEC, (unsigned char *)sendSpec, |
| strlen(sendSpec) + 1); |
| attrs++; |
| |
| PORT_Assert(attrs - template <= 2); |
| |
| PK11_EnterSlotMonitor(slot); |
| crv = PK11_CreateNewObject(slot, slot->session, |
| template, attrs - template, PR_FALSE, &dummy); |
| PK11_ExitSlotMonitor(slot); |
| |
| if (crv != CKR_OK) { |
| PORT_SetError(PK11_MapError(crv)); |
| return SECFailure; |
| } |
| return SECMOD_UpdateSlotList(slot->module); |
| } |
| |
| /* |
| * return true if the selected slot ID is not present or doesn't exist |
| */ |
| static PRBool |
| secmod_SlotIsEmpty(SECMODModule *mod, CK_SLOT_ID slotID) |
| { |
| PK11SlotInfo *slot = SECMOD_LookupSlot(mod->moduleID, slotID); |
| if (slot) { |
| PRBool present = PK11_IsPresent(slot); |
| PK11_FreeSlot(slot); |
| if (present) { |
| return PR_FALSE; |
| } |
| } |
| /* it doesn't exist or isn't present, it's available */ |
| return PR_TRUE; |
| } |
| |
| /* |
| * Find an unused slot id in module. |
| */ |
| static CK_SLOT_ID |
| secmod_FindFreeSlot(SECMODModule *mod) |
| { |
| CK_SLOT_ID i, minSlotID, maxSlotID; |
| |
| /* look for a free slot id on the internal module */ |
| if (mod->internal && mod->isFIPS) { |
| minSlotID = SFTK_MIN_FIPS_USER_SLOT_ID; |
| maxSlotID = SFTK_MAX_FIPS_USER_SLOT_ID; |
| } else { |
| minSlotID = SFTK_MIN_USER_SLOT_ID; |
| maxSlotID = SFTK_MAX_USER_SLOT_ID; |
| } |
| for (i = minSlotID; i < maxSlotID; i++) { |
| if (secmod_SlotIsEmpty(mod, i)) { |
| return i; |
| } |
| } |
| PORT_SetError(SEC_ERROR_NO_SLOT_SELECTED); |
| return (CK_SLOT_ID)-1; |
| } |
| |
| /* |
| * Attempt to open a new slot. |
| * |
| * This works the same os OpenUserDB except it can be called against |
| * any module that understands the softoken protocol for opening new |
| * slots, not just the softoken itself. If the selected module does not |
| * understand the protocol, C_CreateObject will fail with |
| * CKR_INVALID_ATTRIBUTE, and SECMOD_OpenNewSlot will return NULL and set |
| * SEC_ERROR_BAD_DATA. |
| * |
| * NewSlots can be closed with SECMOD_CloseUserDB(); |
| * |
| * Modulespec is module dependent. |
| */ |
| PK11SlotInfo * |
| SECMOD_OpenNewSlot(SECMODModule *mod, const char *moduleSpec) |
| { |
| CK_SLOT_ID slotID = 0; |
| PK11SlotInfo *slot; |
| char *escSpec; |
| char *sendSpec; |
| SECStatus rv; |
| |
| slotID = secmod_FindFreeSlot(mod); |
| if (slotID == (CK_SLOT_ID)-1) { |
| return NULL; |
| } |
| |
| if (mod->slotCount == 0) { |
| return NULL; |
| } |
| |
| /* just grab the first slot in the module, any present slot should work */ |
| slot = PK11_ReferenceSlot(mod->slots[0]); |
| if (slot == NULL) { |
| return NULL; |
| } |
| |
| /* we've found the slot, now build the moduleSpec */ |
| escSpec = NSSUTIL_DoubleEscape(moduleSpec, '>', ']'); |
| if (escSpec == NULL) { |
| PK11_FreeSlot(slot); |
| return NULL; |
| } |
| sendSpec = PR_smprintf("tokens=[0x%x=<%s>]", slotID, escSpec); |
| PORT_Free(escSpec); |
| |
| if (sendSpec == NULL) { |
| /* PR_smprintf does not set SEC_ERROR_NO_MEMORY on failure. */ |
| PK11_FreeSlot(slot); |
| PORT_SetError(SEC_ERROR_NO_MEMORY); |
| return NULL; |
| } |
| rv = secmod_UserDBOp(slot, CKO_NSS_NEWSLOT, sendSpec); |
| PR_smprintf_free(sendSpec); |
| PK11_FreeSlot(slot); |
| if (rv != SECSuccess) { |
| return NULL; |
| } |
| |
| slot = SECMOD_FindSlotByID(mod, slotID); |
| if (slot) { |
| /* if we are in the delay period for the "isPresent" call, reset |
| * the delay since we know things have probably changed... */ |
| if (slot->nssToken && slot->nssToken->slot) { |
| nssSlot_ResetDelay(slot->nssToken->slot); |
| } |
| /* force the slot info structures to properly reset */ |
| (void)PK11_IsPresent(slot); |
| } |
| return slot; |
| } |
| |
| /* |
| * given a module spec, find the slot in the module for it. |
| */ |
| PK11SlotInfo * |
| secmod_FindSlotFromModuleSpec(const char *moduleSpec, SECMODModule *module) |
| { |
| CK_SLOT_ID slot_id = secmod_GetSlotIDFromModuleSpec(moduleSpec, module); |
| if (slot_id == -1) { |
| return NULL; |
| } |
| |
| return SECMOD_FindSlotByID(module, slot_id); |
| } |
| |
| /* |
| * Open a new database using the softoken. The caller is responsible for making |
| * sure the module spec is correct and usable. The caller should ask for one |
| * new database per call if the caller wants to get meaningful information |
| * about the new database. |
| * |
| * moduleSpec is the same data that you would pass to softoken at |
| * initialization time under the 'tokens' options. For example, if you were |
| * to specify tokens=<0x4=[configdir='./mybackup' tokenDescription='Backup']> |
| * You would specify "configdir='./mybackup' tokenDescription='Backup'" as your |
| * module spec here. The slot ID will be calculated for you by |
| * SECMOD_OpenUserDB(). |
| * |
| * Typical parameters here are configdir, tokenDescription and flags. |
| * |
| * a Full list is below: |
| * |
| * |
| * configDir - The location of the databases for this token. If configDir is |
| * not specified, and noCertDB and noKeyDB is not specified, the load |
| * will fail. |
| * certPrefix - Cert prefix for this token. |
| * keyPrefix - Prefix for the key database for this token. (if not specified, |
| * certPrefix will be used). |
| * tokenDescription - The label value for this token returned in the |
| * CK_TOKEN_INFO structure with an internationalize string (UTF8). |
| * This value will be truncated at 32 bytes (no NULL, partial UTF8 |
| * characters dropped). You should specify a user friendly name here |
| * as this is the value the token will be referred to in most |
| * application UI's. You should make sure tokenDescription is unique. |
| * slotDescription - The slotDescription value for this token returned |
| * in the CK_SLOT_INFO structure with an internationalize string |
| * (UTF8). This value will be truncated at 64 bytes (no NULL, partial |
| * UTF8 characters dropped). This name will not change after the |
| * database is closed. It should have some number to make this unique. |
| * minPWLen - minimum password length for this token. |
| * flags - comma separated list of flag values, parsed case-insensitive. |
| * Valid flags are: |
| * readOnly - Databases should be opened read only. |
| * noCertDB - Don't try to open a certificate database. |
| * noKeyDB - Don't try to open a key database. |
| * forceOpen - Don't fail to initialize the token if the |
| * databases could not be opened. |
| * passwordRequired - zero length passwords are not acceptable |
| * (valid only if there is a keyDB). |
| * optimizeSpace - allocate smaller hash tables and lock tables. |
| * When this flag is not specified, Softoken will allocate |
| * large tables to prevent lock contention. |
| */ |
| PK11SlotInfo * |
| SECMOD_OpenUserDB(const char *moduleSpec) |
| { |
| SECMODModule *mod; |
| SECMODConfigList *conflist = NULL; |
| int count = 0; |
| |
| if (moduleSpec == NULL) { |
| return NULL; |
| } |
| |
| /* NOTE: unlike most PK11 function, this does not return a reference |
| * to the module */ |
| mod = SECMOD_GetInternalModule(); |
| if (!mod) { |
| /* shouldn't happen */ |
| PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); |
| return NULL; |
| } |
| |
| /* make sure we don't open the same database twice. We only understand |
| * the moduleSpec for internal databases well enough to do this, so only |
| * do this in OpenUserDB */ |
| conflist = secmod_GetConfigList(mod->isFIPS, mod->libraryParams, &count); |
| if (conflist) { |
| PK11SlotInfo *slot = NULL; |
| if (secmod_MatchConfigList(moduleSpec, conflist, count)) { |
| slot = secmod_FindSlotFromModuleSpec(moduleSpec, mod); |
| } |
| secmod_FreeConfigList(conflist, count); |
| if (slot) { |
| return slot; |
| } |
| } |
| return SECMOD_OpenNewSlot(mod, moduleSpec); |
| } |
| |
| /* |
| * close an already opened user database. NOTE: the database must be |
| * in the internal token, and must be one created with SECMOD_OpenUserDB(). |
| * Once the database is closed, the slot will remain as an empty slot |
| * until it's used again with SECMOD_OpenUserDB() or SECMOD_OpenNewSlot(). |
| */ |
| SECStatus |
| SECMOD_CloseUserDB(PK11SlotInfo *slot) |
| { |
| SECStatus rv; |
| char *sendSpec; |
| |
| sendSpec = PR_smprintf("tokens=[0x%x=<>]", slot->slotID); |
| if (sendSpec == NULL) { |
| /* PR_smprintf does not set no memory error */ |
| PORT_SetError(SEC_ERROR_NO_MEMORY); |
| return SECFailure; |
| } |
| rv = secmod_UserDBOp(slot, CKO_NSS_DELSLOT, sendSpec); |
| PR_smprintf_free(sendSpec); |
| /* if we are in the delay period for the "isPresent" call, reset |
| * the delay since we know things have probably changed... */ |
| if (slot->nssToken && slot->nssToken->slot) { |
| nssSlot_ResetDelay(slot->nssToken->slot); |
| /* force the slot info structures to properly reset */ |
| (void)PK11_IsPresent(slot); |
| } |
| return rv; |
| } |
| |
| /* |
| * Restart PKCS #11 modules after a fork(). See secmod.h for more information. |
| */ |
| SECStatus |
| SECMOD_RestartModules(PRBool force) |
| { |
| SECMODModuleList *mlp; |
| SECStatus rrv = SECSuccess; |
| int lastError = 0; |
| |
| if (!moduleLock) { |
| PORT_SetError(SEC_ERROR_NOT_INITIALIZED); |
| return SECFailure; |
| } |
| |
| /* Only need to restart the PKCS #11 modules that were initialized */ |
| SECMOD_GetReadLock(moduleLock); |
| for (mlp = modules; mlp != NULL; mlp = mlp->next) { |
| SECMODModule *mod = mlp->module; |
| CK_ULONG count; |
| SECStatus rv; |
| int i; |
| |
| /* If the module needs to be reset, do so */ |
| if (force || (PK11_GETTAB(mod)->C_GetSlotList(CK_FALSE, NULL, &count) != CKR_OK)) { |
| PRBool alreadyLoaded; |
| /* first call Finalize. This is not required by PKCS #11, but some |
| * older modules require it, and it doesn't hurt (compliant modules |
| * will return CKR_NOT_INITIALIZED */ |
| (void)PK11_GETTAB(mod)->C_Finalize(NULL); |
| /* now initialize the module, this function reinitializes |
| * a module in place, preserving existing slots (even if they |
| * no longer exist) */ |
| rv = secmod_ModuleInit(mod, NULL, &alreadyLoaded); |
| if (rv != SECSuccess) { |
| /* save the last error code */ |
| lastError = PORT_GetError(); |
| rrv = rv; |
| /* couldn't reinit the module, disable all its slots */ |
| for (i = 0; i < mod->slotCount; i++) { |
| mod->slots[i]->disabled = PR_TRUE; |
| mod->slots[i]->reason = PK11_DIS_COULD_NOT_INIT_TOKEN; |
| } |
| continue; |
| } |
| for (i = 0; i < mod->slotCount; i++) { |
| /* get new token sessions, bump the series up so that |
| * we refresh other old sessions. This will tell much of |
| * NSS to flush cached handles it may hold as well */ |
| rv = PK11_InitToken(mod->slots[i], PR_TRUE); |
| /* PK11_InitToken could fail if the slot isn't present. |
| * If it is present, though, something is wrong and we should |
| * disable the slot and let the caller know. */ |
| if (rv != SECSuccess && PK11_IsPresent(mod->slots[i])) { |
| /* save the last error code */ |
| lastError = PORT_GetError(); |
| rrv = rv; |
| /* disable the token */ |
| mod->slots[i]->disabled = PR_TRUE; |
| mod->slots[i]->reason = PK11_DIS_COULD_NOT_INIT_TOKEN; |
| } |
| } |
| } |
| } |
| SECMOD_ReleaseReadLock(moduleLock); |
| |
| /* |
| * on multiple failures, we are only returning the lastError. The caller |
| * can determine which slots are bad by calling PK11_IsDisabled(). |
| */ |
| if (rrv != SECSuccess) { |
| /* restore the last error code */ |
| PORT_SetError(lastError); |
| } |
| |
| return rrv; |
| } |