andre@0: /* This Source Code Form is subject to the terms of the Mozilla Public andre@0: * License, v. 2.0. If a copy of the MPL was not distributed with this andre@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ andre@0: /* andre@0: * The following handles the loading, unloading and management of andre@0: * various PCKS #11 modules andre@0: */ andre@0: #define FORCE_PR_LOG 1 andre@0: #include "seccomon.h" andre@0: #include "pkcs11.h" andre@0: #include "secmod.h" andre@0: #include "prlink.h" andre@0: #include "pk11func.h" andre@0: #include "secmodi.h" andre@0: #include "secmodti.h" andre@0: #include "nssilock.h" andre@0: #include "secerr.h" andre@0: #include "prenv.h" andre@0: #include "utilparst.h" andre@0: andre@0: #define DEBUG_MODULE 1 andre@0: andre@0: #ifdef DEBUG_MODULE andre@0: static char *modToDBG = NULL; andre@0: andre@0: #include "debug_module.c" andre@0: #endif andre@0: andre@0: /* build the PKCS #11 2.01 lock files */ andre@0: CK_RV PR_CALLBACK secmodCreateMutext(CK_VOID_PTR_PTR pmutex) { andre@0: *pmutex = (CK_VOID_PTR) PZ_NewLock(nssILockOther); andre@0: if ( *pmutex ) return CKR_OK; andre@0: return CKR_HOST_MEMORY; andre@0: } andre@0: andre@0: CK_RV PR_CALLBACK secmodDestroyMutext(CK_VOID_PTR mutext) { andre@0: PZ_DestroyLock((PZLock *)mutext); andre@0: return CKR_OK; andre@0: } andre@0: andre@0: CK_RV PR_CALLBACK secmodLockMutext(CK_VOID_PTR mutext) { andre@0: PZ_Lock((PZLock *)mutext); andre@0: return CKR_OK; andre@0: } andre@0: andre@0: CK_RV PR_CALLBACK secmodUnlockMutext(CK_VOID_PTR mutext) { andre@0: PZ_Unlock((PZLock *)mutext); andre@0: return CKR_OK; andre@0: } andre@0: andre@0: static SECMODModuleID nextModuleID = 1; andre@0: static const CK_C_INITIALIZE_ARGS secmodLockFunctions = { andre@0: secmodCreateMutext, secmodDestroyMutext, secmodLockMutext, andre@0: secmodUnlockMutext, CKF_LIBRARY_CANT_CREATE_OS_THREADS| andre@0: CKF_OS_LOCKING_OK andre@0: ,NULL andre@0: }; andre@0: static const CK_C_INITIALIZE_ARGS secmodNoLockArgs = { andre@0: NULL, NULL, NULL, NULL, andre@0: CKF_LIBRARY_CANT_CREATE_OS_THREADS andre@0: ,NULL andre@0: }; andre@0: andre@0: static PRBool loadSingleThreadedModules = PR_TRUE; andre@0: static PRBool enforceAlreadyInitializedError = PR_TRUE; andre@0: static PRBool finalizeModules = PR_TRUE; andre@0: andre@0: /* set global options for NSS PKCS#11 module loader */ andre@0: SECStatus pk11_setGlobalOptions(PRBool noSingleThreadedModules, andre@0: PRBool allowAlreadyInitializedModules, andre@0: PRBool dontFinalizeModules) andre@0: { andre@0: if (noSingleThreadedModules) { andre@0: loadSingleThreadedModules = PR_FALSE; andre@0: } else { andre@0: loadSingleThreadedModules = PR_TRUE; andre@0: } andre@0: if (allowAlreadyInitializedModules) { andre@0: enforceAlreadyInitializedError = PR_FALSE; andre@0: } else { andre@0: enforceAlreadyInitializedError = PR_TRUE; andre@0: } andre@0: if (dontFinalizeModules) { andre@0: finalizeModules = PR_FALSE; andre@0: } else { andre@0: finalizeModules = PR_TRUE; andre@0: } andre@0: return SECSuccess; andre@0: } andre@0: andre@0: PRBool pk11_getFinalizeModulesOption(void) andre@0: { andre@0: return finalizeModules; andre@0: } andre@0: andre@0: /* andre@0: * Allow specification loading the same module more than once at init time. andre@0: * This enables 2 things. andre@0: * andre@0: * 1) we can load additional databases by manipulating secmod.db/pkcs11.txt. andre@0: * 2) we can handle the case where some library has already initialized NSS andre@0: * before the main application. andre@0: * andre@0: * oldModule is the module we have already initialized. andre@0: * char *modulespec is the full module spec for the library we want to andre@0: * initialize. andre@0: */ andre@0: static SECStatus andre@0: secmod_handleReload(SECMODModule *oldModule, SECMODModule *newModule) andre@0: { andre@0: PK11SlotInfo *slot; andre@0: char *modulespec; andre@0: char *newModuleSpec; andre@0: char **children; andre@0: CK_SLOT_ID *ids; andre@0: SECMODConfigList *conflist = NULL; andre@0: SECStatus rv = SECFailure; andre@0: int count = 0; andre@0: andre@0: /* first look for tokens= key words from the module spec */ andre@0: modulespec = newModule->libraryParams; andre@0: newModuleSpec = secmod_ParseModuleSpecForTokens(PR_TRUE, andre@0: newModule->isFIPS, modulespec, &children, &ids); andre@0: if (!newModuleSpec) { andre@0: return SECFailure; andre@0: } andre@0: andre@0: /* andre@0: * We are now trying to open a new slot on an already loaded module. andre@0: * If that slot represents a cert/key database, we don't want to open andre@0: * multiple copies of that same database. Unfortunately we understand andre@0: * the softoken flags well enough to be able to do this, so we can only get andre@0: * the list of already loaded databases if we are trying to open another andre@0: * internal module. andre@0: */ andre@0: if (oldModule->internal) { andre@0: conflist = secmod_GetConfigList(oldModule->isFIPS, andre@0: oldModule->libraryParams, &count); andre@0: } andre@0: andre@0: andre@0: /* don't open multiple of the same db */ andre@0: if (conflist && secmod_MatchConfigList(newModuleSpec, conflist, count)) { andre@0: rv = SECSuccess; andre@0: goto loser; andre@0: } andre@0: slot = SECMOD_OpenNewSlot(oldModule, newModuleSpec); andre@0: if (slot) { andre@0: int newID; andre@0: char **thisChild; andre@0: CK_SLOT_ID *thisID; andre@0: char *oldModuleSpec; andre@0: andre@0: if (secmod_IsInternalKeySlot(newModule)) { andre@0: pk11_SetInternalKeySlotIfFirst(slot); andre@0: } andre@0: newID = slot->slotID; andre@0: PK11_FreeSlot(slot); andre@0: for (thisChild=children, thisID=ids; thisChild && *thisChild; andre@0: thisChild++,thisID++) { andre@0: if (conflist && andre@0: secmod_MatchConfigList(*thisChild, conflist, count)) { andre@0: *thisID = (CK_SLOT_ID) -1; andre@0: continue; andre@0: } andre@0: slot = SECMOD_OpenNewSlot(oldModule, *thisChild); andre@0: if (slot) { andre@0: *thisID = slot->slotID; andre@0: PK11_FreeSlot(slot); andre@0: } else { andre@0: *thisID = (CK_SLOT_ID) -1; andre@0: } andre@0: } andre@0: andre@0: /* update the old module initialization string in case we need to andre@0: * shutdown and reinit the whole mess (this is rare, but can happen andre@0: * when trying to stop smart card insertion/removal threads)... */ andre@0: oldModuleSpec = secmod_MkAppendTokensList(oldModule->arena, andre@0: oldModule->libraryParams, newModuleSpec, newID, andre@0: children, ids); andre@0: if (oldModuleSpec) { andre@0: oldModule->libraryParams = oldModuleSpec; andre@0: } andre@0: andre@0: rv = SECSuccess; andre@0: } andre@0: andre@0: loser: andre@0: secmod_FreeChildren(children, ids); andre@0: PORT_Free(newModuleSpec); andre@0: if (conflist) { andre@0: secmod_FreeConfigList(conflist, count); andre@0: } andre@0: return rv; andre@0: } andre@0: andre@0: /* andre@0: * collect the steps we need to initialize a module in a single function andre@0: */ andre@0: SECStatus andre@0: secmod_ModuleInit(SECMODModule *mod, SECMODModule **reload, andre@0: PRBool* alreadyLoaded) andre@0: { andre@0: CK_C_INITIALIZE_ARGS moduleArgs; andre@0: CK_VOID_PTR pInitArgs; andre@0: CK_RV crv; andre@0: andre@0: if (reload) { andre@0: *reload = NULL; andre@0: } andre@0: andre@0: if (!mod || !alreadyLoaded) { andre@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); andre@0: return SECFailure; andre@0: } andre@0: andre@0: if (mod->libraryParams == NULL) { andre@0: if (mod->isThreadSafe) { andre@0: pInitArgs = (void *) &secmodLockFunctions; andre@0: } else { andre@0: pInitArgs = NULL; andre@0: } andre@0: } else { andre@0: if (mod->isThreadSafe) { andre@0: moduleArgs = secmodLockFunctions; andre@0: } else { andre@0: moduleArgs = secmodNoLockArgs; andre@0: } andre@0: moduleArgs.LibraryParameters = (void *) mod->libraryParams; andre@0: pInitArgs = &moduleArgs; andre@0: } andre@0: crv = PK11_GETTAB(mod)->C_Initialize(pInitArgs); andre@0: if (CKR_CRYPTOKI_ALREADY_INITIALIZED == crv) { andre@0: SECMODModule *oldModule = NULL; andre@0: andre@0: /* Library has already been loaded once, if caller expects it, and it andre@0: * has additional configuration, try reloading it as well. */ andre@0: if (reload != NULL && mod->libraryParams) { andre@0: oldModule = secmod_FindModuleByFuncPtr(mod->functionList); andre@0: } andre@0: /* Library has been loaded by NSS. It means it may be capable of andre@0: * reloading */ andre@0: if (oldModule) { andre@0: SECStatus rv; andre@0: rv = secmod_handleReload(oldModule, mod); andre@0: if (rv == SECSuccess) { andre@0: /* This module should go away soon, since we've andre@0: * simply expanded the slots on the old module. andre@0: * When it goes away, it should not Finalize since andre@0: * that will close our old module as well. Setting andre@0: * the function list to NULL will prevent that close */ andre@0: mod->functionList = NULL; andre@0: *reload = oldModule; andre@0: return SECSuccess; andre@0: } andre@0: SECMOD_DestroyModule(oldModule); andre@0: } andre@0: /* reload not possible, fall back to old semantics */ andre@0: if (!enforceAlreadyInitializedError) { andre@0: *alreadyLoaded = PR_TRUE; andre@0: return SECSuccess; andre@0: } andre@0: } andre@0: if (crv != CKR_OK) { andre@0: if (!mod->isThreadSafe || andre@0: crv == CKR_NETSCAPE_CERTDB_FAILED || andre@0: crv == CKR_NETSCAPE_KEYDB_FAILED) { andre@0: PORT_SetError(PK11_MapError(crv)); andre@0: return SECFailure; andre@0: } andre@0: /* If we had attempted to init a single threaded module "with" andre@0: * parameters and it failed, should we retry "without" parameters? andre@0: * (currently we don't retry in this scenario) */ andre@0: andre@0: if (!loadSingleThreadedModules) { andre@0: PORT_SetError(SEC_ERROR_INCOMPATIBLE_PKCS11); andre@0: return SECFailure; andre@0: } andre@0: /* If we arrive here, the module failed a ThreadSafe init. */ andre@0: mod->isThreadSafe = PR_FALSE; andre@0: if (!mod->libraryParams) { andre@0: pInitArgs = NULL; andre@0: } else { andre@0: moduleArgs = secmodNoLockArgs; andre@0: moduleArgs.LibraryParameters = (void *) mod->libraryParams; andre@0: pInitArgs = &moduleArgs; andre@0: } andre@0: crv = PK11_GETTAB(mod)->C_Initialize(pInitArgs); andre@0: if ((CKR_CRYPTOKI_ALREADY_INITIALIZED == crv) && andre@0: (!enforceAlreadyInitializedError)) { andre@0: *alreadyLoaded = PR_TRUE; andre@0: return SECSuccess; andre@0: } andre@0: if (crv != CKR_OK) { andre@0: PORT_SetError(PK11_MapError(crv)); andre@0: return SECFailure; andre@0: } andre@0: } andre@0: return SECSuccess; andre@0: } andre@0: andre@0: /* andre@0: * set the hasRootCerts flags in the module so it can be stored back andre@0: * into the database. andre@0: */ andre@0: void andre@0: SECMOD_SetRootCerts(PK11SlotInfo *slot, SECMODModule *mod) { andre@0: PK11PreSlotInfo *psi = NULL; andre@0: int i; andre@0: andre@0: if (slot->hasRootCerts) { andre@0: for (i=0; i < mod->slotInfoCount; i++) { andre@0: if (slot->slotID == mod->slotInfo[i].slotID) { andre@0: psi = &mod->slotInfo[i]; andre@0: break; andre@0: } andre@0: } andre@0: if (psi == NULL) { andre@0: /* allocate more slots */ andre@0: PK11PreSlotInfo *psi_list = (PK11PreSlotInfo *) andre@0: PORT_ArenaAlloc(mod->arena, andre@0: (mod->slotInfoCount+1)* sizeof(PK11PreSlotInfo)); andre@0: /* copy the old ones */ andre@0: if (mod->slotInfoCount > 0) { andre@0: PORT_Memcpy(psi_list,mod->slotInfo, andre@0: (mod->slotInfoCount)*sizeof(PK11PreSlotInfo)); andre@0: } andre@0: /* assign psi to the last new slot */ andre@0: psi = &psi_list[mod->slotInfoCount]; andre@0: psi->slotID = slot->slotID; andre@0: psi->askpw = 0; andre@0: psi->timeout = 0; andre@0: psi ->defaultFlags = 0; andre@0: andre@0: /* increment module count & store new list */ andre@0: mod->slotInfo = psi_list; andre@0: mod->slotInfoCount++; andre@0: andre@0: } andre@0: psi->hasRootCerts = 1; andre@0: } andre@0: } andre@0: andre@0: #ifdef NSS_STATIC andre@0: extern CK_RV NSC_GetFunctionList(CK_FUNCTION_LIST_PTR *pFunctionList); andre@0: extern CK_RV FC_GetFunctionList(CK_FUNCTION_LIST_PTR *pFunctionList); andre@0: extern char **NSC_ModuleDBFunc(unsigned long function,char *parameters, void *args); andre@0: extern CK_RV builtinsC_GetFunctionList(CK_FUNCTION_LIST_PTR *pFunctionList); andre@0: #else andre@0: static const char* my_shlib_name = andre@0: SHLIB_PREFIX"nss"SHLIB_VERSION"."SHLIB_SUFFIX; andre@0: static const char* softoken_shlib_name = andre@0: SHLIB_PREFIX"softokn"SOFTOKEN_SHLIB_VERSION"."SHLIB_SUFFIX; andre@0: static const PRCallOnceType pristineCallOnce; andre@0: static PRCallOnceType loadSoftokenOnce; andre@0: static PRLibrary* softokenLib; andre@0: static PRInt32 softokenLoadCount; andre@0: #endif /* NSS_STATIC */ andre@0: andre@0: #include "prio.h" andre@0: #include "prprf.h" andre@0: #include andre@0: #include "prsystem.h" andre@0: andre@0: #ifndef NSS_STATIC andre@0: /* This function must be run only once. */ andre@0: /* determine if hybrid platform, then actually load the DSO. */ andre@0: static PRStatus andre@0: softoken_LoadDSO( void ) andre@0: { andre@0: PRLibrary * handle; andre@0: andre@0: handle = PORT_LoadLibraryFromOrigin(my_shlib_name, andre@0: (PRFuncPtr) &softoken_LoadDSO, andre@0: softoken_shlib_name); andre@0: if (handle) { andre@0: softokenLib = handle; andre@0: return PR_SUCCESS; andre@0: } andre@0: return PR_FAILURE; andre@0: } andre@0: #endif /* !NSS_STATIC */ andre@0: andre@0: /* andre@0: * load a new module into our address space and initialize it. andre@0: */ andre@0: SECStatus andre@0: secmod_LoadPKCS11Module(SECMODModule *mod, SECMODModule **oldModule) { andre@0: PRLibrary *library = NULL; andre@0: CK_C_GetFunctionList entry = NULL; andre@0: CK_INFO info; andre@0: CK_ULONG slotCount = 0; andre@0: SECStatus rv; andre@0: PRBool alreadyLoaded = PR_FALSE; andre@0: char *disableUnload = NULL; andre@0: andre@0: if (mod->loaded) return SECSuccess; andre@0: andre@0: /* intenal modules get loaded from their internal list */ andre@0: if (mod->internal && (mod->dllName == NULL)) { andre@0: #ifdef NSS_STATIC andre@0: if (mod->isFIPS) { andre@0: entry = FC_GetFunctionList; andre@0: } else { andre@0: entry = NSC_GetFunctionList; andre@0: } andre@0: if (mod->isModuleDB) { andre@0: mod->moduleDBFunc = NSC_ModuleDBFunc; andre@0: } andre@0: #else andre@0: /* andre@0: * Loads softoken as a dynamic library, andre@0: * even though the rest of NSS assumes this as the "internal" module. andre@0: */ andre@0: if (!softokenLib && andre@0: PR_SUCCESS != PR_CallOnce(&loadSoftokenOnce, &softoken_LoadDSO)) andre@0: return SECFailure; andre@0: andre@0: PR_ATOMIC_INCREMENT(&softokenLoadCount); andre@0: andre@0: if (mod->isFIPS) { andre@0: entry = (CK_C_GetFunctionList) andre@0: PR_FindSymbol(softokenLib, "FC_GetFunctionList"); andre@0: } else { andre@0: entry = (CK_C_GetFunctionList) andre@0: PR_FindSymbol(softokenLib, "NSC_GetFunctionList"); andre@0: } andre@0: andre@0: if (!entry) andre@0: return SECFailure; andre@0: andre@0: if (mod->isModuleDB) { andre@0: mod->moduleDBFunc = (CK_C_GetFunctionList) andre@0: PR_FindSymbol(softokenLib, "NSC_ModuleDBFunc"); andre@0: } andre@0: #endif andre@0: andre@0: if (mod->moduleDBOnly) { andre@0: mod->loaded = PR_TRUE; andre@0: return SECSuccess; andre@0: } andre@0: } else { andre@0: /* Not internal, load the DLL and look up C_GetFunctionList */ andre@0: if (mod->dllName == NULL) { andre@0: return SECFailure; andre@0: } andre@0: #if defined(NSS_STATIC) && !defined(NSS_DISABLE_ROOT_CERTS) andre@0: if (strstr(mod->dllName, "nssckbi") != NULL) { andre@0: mod->library = NULL; andre@0: PORT_Assert(!mod->moduleDBOnly); andre@0: entry = builtinsC_GetFunctionList; andre@0: PORT_Assert(!mod->isModuleDB); andre@0: goto library_loaded; andre@0: } andre@0: #endif andre@0: andre@0: /* load the library. If this succeeds, then we have to remember to andre@0: * unload the library if anything goes wrong from here on out... andre@0: */ andre@0: library = PR_LoadLibrary(mod->dllName); andre@0: mod->library = (void *)library; andre@0: andre@0: if (library == NULL) { andre@0: return SECFailure; andre@0: } andre@0: andre@0: /* andre@0: * now we need to get the entry point to find the function pointers andre@0: */ andre@0: if (!mod->moduleDBOnly) { andre@0: entry = (CK_C_GetFunctionList) andre@0: PR_FindSymbol(library, "C_GetFunctionList"); andre@0: } andre@0: if (mod->isModuleDB) { andre@0: mod->moduleDBFunc = (void *) andre@0: PR_FindSymbol(library, "NSS_ReturnModuleSpecData"); andre@0: } andre@0: #if defined(NSS_STATIC) && !defined(NSS_DISABLE_ROOT_CERTS) andre@0: library_loaded: andre@0: #endif andre@0: if (mod->moduleDBFunc == NULL) mod->isModuleDB = PR_FALSE; andre@0: if (entry == NULL) { andre@0: if (mod->isModuleDB) { andre@0: mod->loaded = PR_TRUE; andre@0: mod->moduleDBOnly = PR_TRUE; andre@0: return SECSuccess; andre@0: } andre@0: PR_UnloadLibrary(library); andre@0: return SECFailure; andre@0: } andre@0: } andre@0: andre@0: /* andre@0: * We need to get the function list andre@0: */ andre@0: if ((*entry)((CK_FUNCTION_LIST_PTR *)&mod->functionList) != CKR_OK) andre@0: goto fail; andre@0: andre@0: #ifdef DEBUG_MODULE andre@0: if (PR_TRUE) { andre@0: modToDBG = PR_GetEnv("NSS_DEBUG_PKCS11_MODULE"); andre@0: if (modToDBG && strcmp(mod->commonName, modToDBG) == 0) { andre@0: mod->functionList = (void *)nss_InsertDeviceLog( andre@0: (CK_FUNCTION_LIST_PTR)mod->functionList); andre@0: } andre@0: } andre@0: #endif andre@0: andre@0: mod->isThreadSafe = PR_TRUE; andre@0: andre@0: /* Now we initialize the module */ andre@0: rv = secmod_ModuleInit(mod, oldModule, &alreadyLoaded); andre@0: if (rv != SECSuccess) { andre@0: goto fail; andre@0: } andre@0: andre@0: /* module has been reloaded, this module itself is done, andre@0: * return to the caller */ andre@0: if (mod->functionList == NULL) { andre@0: mod->loaded = PR_TRUE; /* technically the module is loaded.. */ andre@0: return SECSuccess; andre@0: } andre@0: andre@0: /* check the version number */ andre@0: if (PK11_GETTAB(mod)->C_GetInfo(&info) != CKR_OK) goto fail2; andre@0: if (info.cryptokiVersion.major != 2) goto fail2; andre@0: /* all 2.0 are a priori *not* thread safe */ andre@0: if (info.cryptokiVersion.minor < 1) { andre@0: if (!loadSingleThreadedModules) { andre@0: PORT_SetError(SEC_ERROR_INCOMPATIBLE_PKCS11); andre@0: goto fail2; andre@0: } else { andre@0: mod->isThreadSafe = PR_FALSE; andre@0: } andre@0: } andre@0: mod->cryptokiVersion = info.cryptokiVersion; andre@0: andre@0: /* If we don't have a common name, get it from the PKCS 11 module */ andre@0: if ((mod->commonName == NULL) || (mod->commonName[0] == 0)) { andre@0: mod->commonName = PK11_MakeString(mod->arena,NULL, andre@0: (char *)info.libraryDescription, sizeof(info.libraryDescription)); andre@0: if (mod->commonName == NULL) goto fail2; andre@0: } andre@0: andre@0: andre@0: /* initialize the Slots */ andre@0: if (PK11_GETTAB(mod)->C_GetSlotList(CK_FALSE, NULL, &slotCount) == CKR_OK) { andre@0: CK_SLOT_ID *slotIDs; andre@0: int i; andre@0: CK_RV crv; andre@0: andre@0: mod->slots = (PK11SlotInfo **)PORT_ArenaAlloc(mod->arena, andre@0: sizeof(PK11SlotInfo *) * slotCount); andre@0: if (mod->slots == NULL) goto fail2; andre@0: andre@0: slotIDs = (CK_SLOT_ID *) PORT_Alloc(sizeof(CK_SLOT_ID)*slotCount); andre@0: if (slotIDs == NULL) { andre@0: goto fail2; andre@0: } andre@0: crv = PK11_GETTAB(mod)->C_GetSlotList(CK_FALSE, slotIDs, &slotCount); andre@0: if (crv != CKR_OK) { andre@0: PORT_Free(slotIDs); andre@0: goto fail2; andre@0: } andre@0: andre@0: /* Initialize each slot */ andre@0: for (i=0; i < (int)slotCount; i++) { andre@0: mod->slots[i] = PK11_NewSlotInfo(mod); andre@0: PK11_InitSlot(mod,slotIDs[i],mod->slots[i]); andre@0: /* look down the slot info table */ andre@0: PK11_LoadSlotList(mod->slots[i],mod->slotInfo,mod->slotInfoCount); andre@0: SECMOD_SetRootCerts(mod->slots[i],mod); andre@0: /* explicitly mark the internal slot as such if IsInternalKeySlot() andre@0: * is set */ andre@0: if (secmod_IsInternalKeySlot(mod) && (i == (mod->isFIPS ? 0 : 1))) { andre@0: pk11_SetInternalKeySlotIfFirst(mod->slots[i]); andre@0: } andre@0: } andre@0: mod->slotCount = slotCount; andre@0: mod->slotInfoCount = 0; andre@0: PORT_Free(slotIDs); andre@0: } andre@0: andre@0: mod->loaded = PR_TRUE; andre@0: mod->moduleID = nextModuleID++; andre@0: return SECSuccess; andre@0: fail2: andre@0: if (enforceAlreadyInitializedError || (!alreadyLoaded)) { andre@0: PK11_GETTAB(mod)->C_Finalize(NULL); andre@0: } andre@0: fail: andre@0: mod->functionList = NULL; andre@0: disableUnload = PR_GetEnv("NSS_DISABLE_UNLOAD"); andre@0: if (library && !disableUnload) { andre@0: PR_UnloadLibrary(library); andre@0: } andre@0: return SECFailure; andre@0: } andre@0: andre@0: SECStatus andre@0: SECMOD_UnloadModule(SECMODModule *mod) { andre@0: PRLibrary *library; andre@0: char *disableUnload = NULL; andre@0: andre@0: if (!mod->loaded) { andre@0: return SECFailure; andre@0: } andre@0: if (finalizeModules) { andre@0: if (mod->functionList &&!mod->moduleDBOnly) { andre@0: PK11_GETTAB(mod)->C_Finalize(NULL); andre@0: } andre@0: } andre@0: mod->moduleID = 0; andre@0: mod->loaded = PR_FALSE; andre@0: andre@0: /* do we want the semantics to allow unloading the internal library? andre@0: * if not, we should change this to SECFailure and move it above the andre@0: * mod->loaded = PR_FALSE; */ andre@0: if (mod->internal && (mod->dllName == NULL)) { andre@0: #ifndef NSS_STATIC andre@0: if (0 == PR_ATOMIC_DECREMENT(&softokenLoadCount)) { andre@0: if (softokenLib) { andre@0: disableUnload = PR_GetEnv("NSS_DISABLE_UNLOAD"); andre@0: if (!disableUnload) { andre@0: PRStatus status = PR_UnloadLibrary(softokenLib); andre@0: PORT_Assert(PR_SUCCESS == status); andre@0: } andre@0: softokenLib = NULL; andre@0: } andre@0: loadSoftokenOnce = pristineCallOnce; andre@0: } andre@0: #endif andre@0: return SECSuccess; andre@0: } andre@0: andre@0: library = (PRLibrary *)mod->library; andre@0: /* paranoia */ andre@0: if (library == NULL) { andre@0: #if defined(NSS_STATIC) && !defined(NSS_DISABLE_ROOT_CERTS) andre@0: if (strstr(mod->dllName, "nssckbi") != NULL) { andre@0: return SECSuccess; andre@0: } andre@0: #endif andre@0: return SECFailure; andre@0: } andre@0: andre@0: disableUnload = PR_GetEnv("NSS_DISABLE_UNLOAD"); andre@0: if (!disableUnload) { andre@0: PR_UnloadLibrary(library); andre@0: } andre@0: return SECSuccess; andre@0: } andre@0: andre@0: void andre@0: nss_DumpModuleLog(void) andre@0: { andre@0: #ifdef DEBUG_MODULE andre@0: if (modToDBG) { andre@0: print_final_statistics(); andre@0: } andre@0: #endif andre@0: }