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