diff nss/lib/pk11wrap/pk11load.c @ 0:1e5118fa0cb1

This is NSS with a Cmake Buildsyste To compile a static NSS library for Windows we've used the Chromium-NSS fork and added a Cmake buildsystem to compile it statically for Windows. See README.chromium for chromium changes and README.trustbridge for our modifications.
author Andre Heinecke <andre.heinecke@intevation.de>
date Mon, 28 Jul 2014 10:47:06 +0200
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/pk11wrap/pk11load.c	Mon Jul 28 10:47:06 2014 +0200
@@ -0,0 +1,662 @@
+/* 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;
+    }
+}
+
+#ifdef NSS_STATIC
+extern CK_RV NSC_GetFunctionList(CK_FUNCTION_LIST_PTR *pFunctionList);
+extern CK_RV FC_GetFunctionList(CK_FUNCTION_LIST_PTR *pFunctionList);
+extern char **NSC_ModuleDBFunc(unsigned long function,char *parameters, void *args);
+extern CK_RV builtinsC_GetFunctionList(CK_FUNCTION_LIST_PTR *pFunctionList);
+#else
+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;
+#endif  /* NSS_STATIC */
+
+#include "prio.h"
+#include "prprf.h"
+#include <stdio.h>
+#include "prsystem.h"
+
+#ifndef NSS_STATIC
+/* 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;
+}
+#endif  /* !NSS_STATIC */
+
+/*
+ * 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)) {
+#ifdef NSS_STATIC
+    if (mod->isFIPS) {
+        entry = FC_GetFunctionList;
+    } else {
+        entry = NSC_GetFunctionList;
+    }
+    if (mod->isModuleDB) {
+        mod->moduleDBFunc = NSC_ModuleDBFunc;
+    }
+#else
+    /*
+     * 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");
+    }
+#endif
+
+    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;
+	}
+#if defined(NSS_STATIC) && !defined(NSS_DISABLE_ROOT_CERTS)
+	if (strstr(mod->dllName, "nssckbi") != NULL) {
+	    mod->library = NULL;
+	    PORT_Assert(!mod->moduleDBOnly);
+	    entry = builtinsC_GetFunctionList;
+	    PORT_Assert(!mod->isModuleDB);
+	    goto library_loaded;
+	}
+#endif
+
+	/* 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 defined(NSS_STATIC) && !defined(NSS_DISABLE_ROOT_CERTS)
+library_loaded:
+#endif
+	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_GetEnv("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_GetEnv("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)) {
+#ifndef NSS_STATIC
+        if (0 == PR_ATOMIC_DECREMENT(&softokenLoadCount)) {
+          if (softokenLib) {
+              disableUnload = PR_GetEnv("NSS_DISABLE_UNLOAD");
+              if (!disableUnload) {
+                  PRStatus status = PR_UnloadLibrary(softokenLib);
+                  PORT_Assert(PR_SUCCESS == status);
+              }
+              softokenLib = NULL;
+          }
+          loadSoftokenOnce = pristineCallOnce;
+        }
+#endif
+	return SECSuccess;
+    }
+
+    library = (PRLibrary *)mod->library;
+    /* paranoia */
+    if (library == NULL) {
+#if defined(NSS_STATIC) && !defined(NSS_DISABLE_ROOT_CERTS)
+	if (strstr(mod->dllName, "nssckbi") != NULL) {
+	    return SECSuccess;
+	}
+#endif
+	return SECFailure;
+    }
+
+    disableUnload = PR_GetEnv("NSS_DISABLE_UNLOAD");
+    if (!disableUnload) {
+        PR_UnloadLibrary(library);
+    }
+    return SECSuccess;
+}
+
+void
+nss_DumpModuleLog(void)
+{
+#ifdef DEBUG_MODULE
+    if (modToDBG) {
+	print_final_statistics();
+    }
+#endif
+}
This site is hosted by Intevation GmbH (Datenschutzerklärung und Impressum | Privacy Policy and Imprint)