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