security/nss/lib/pk11wrap/pk11load.c

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     4 /*
     5  * The following handles the loading, unloading and management of
     6  * various PCKS #11 modules
     7  */
     8 #define FORCE_PR_LOG 1
     9 #include "seccomon.h"
    10 #include "pkcs11.h"
    11 #include "secmod.h"
    12 #include "prlink.h"
    13 #include "pk11func.h"
    14 #include "secmodi.h"
    15 #include "secmodti.h"
    16 #include "nssilock.h"
    17 #include "secerr.h"
    18 #include "prenv.h"
    19 #include "utilparst.h"
    21 #define DEBUG_MODULE 1
    23 #ifdef DEBUG_MODULE
    24 static char *modToDBG = NULL;
    26 #include "debug_module.c"
    27 #endif
    29 /* build the PKCS #11 2.01 lock files */
    30 CK_RV PR_CALLBACK secmodCreateMutext(CK_VOID_PTR_PTR pmutex) {
    31     *pmutex = (CK_VOID_PTR) PZ_NewLock(nssILockOther);
    32     if ( *pmutex ) return CKR_OK;
    33     return CKR_HOST_MEMORY;
    34 }
    36 CK_RV PR_CALLBACK secmodDestroyMutext(CK_VOID_PTR mutext) {
    37     PZ_DestroyLock((PZLock *)mutext);
    38     return CKR_OK;
    39 }
    41 CK_RV PR_CALLBACK secmodLockMutext(CK_VOID_PTR mutext) {
    42     PZ_Lock((PZLock *)mutext);
    43     return CKR_OK;
    44 }
    46 CK_RV PR_CALLBACK secmodUnlockMutext(CK_VOID_PTR mutext) {
    47     PZ_Unlock((PZLock *)mutext);
    48     return CKR_OK;
    49 }
    51 static SECMODModuleID  nextModuleID = 1;
    52 static const CK_C_INITIALIZE_ARGS secmodLockFunctions = {
    53     secmodCreateMutext, secmodDestroyMutext, secmodLockMutext, 
    54     secmodUnlockMutext, CKF_LIBRARY_CANT_CREATE_OS_THREADS|
    55 	CKF_OS_LOCKING_OK
    56     ,NULL
    57 };
    58 static const CK_C_INITIALIZE_ARGS secmodNoLockArgs = {
    59     NULL, NULL, NULL, NULL,
    60     CKF_LIBRARY_CANT_CREATE_OS_THREADS
    61     ,NULL
    62 };
    64 static PRBool loadSingleThreadedModules = PR_TRUE;
    65 static PRBool enforceAlreadyInitializedError = PR_TRUE;
    66 static PRBool finalizeModules = PR_TRUE;
    68 /* set global options for NSS PKCS#11 module loader */
    69 SECStatus pk11_setGlobalOptions(PRBool noSingleThreadedModules,
    70                                 PRBool allowAlreadyInitializedModules,
    71                                 PRBool dontFinalizeModules)
    72 {
    73     if (noSingleThreadedModules) {
    74         loadSingleThreadedModules = PR_FALSE;
    75     } else {
    76         loadSingleThreadedModules = PR_TRUE;
    77     }
    78     if (allowAlreadyInitializedModules) {
    79         enforceAlreadyInitializedError = PR_FALSE;
    80     } else {
    81         enforceAlreadyInitializedError = PR_TRUE;
    82     }
    83     if (dontFinalizeModules) {
    84         finalizeModules = PR_FALSE;
    85     } else {
    86         finalizeModules = PR_TRUE;
    87     }
    88     return SECSuccess;
    89 }
    91 PRBool pk11_getFinalizeModulesOption(void)
    92 {
    93     return finalizeModules;
    94 }
    96 /*
    97  * Allow specification loading the same module more than once at init time.
    98  * This enables 2 things.
    99  *
   100  *    1) we can load additional databases by manipulating secmod.db/pkcs11.txt.
   101  *    2) we can handle the case where some library has already initialized NSS
   102  *    before the main application.
   103  *
   104  * oldModule is the module we have already initialized.
   105  * char *modulespec is the full module spec for the library we want to
   106  * initialize.
   107  */
   108 static SECStatus
   109 secmod_handleReload(SECMODModule *oldModule, SECMODModule *newModule)
   110 {
   111     PK11SlotInfo *slot;
   112     char *modulespec;
   113     char *newModuleSpec;
   114     char **children;
   115     CK_SLOT_ID *ids;
   116     SECMODConfigList *conflist = NULL;
   117     SECStatus         rv       = SECFailure;
   118     int               count    = 0;
   120     /* first look for tokens= key words from the module spec */
   121     modulespec = newModule->libraryParams;
   122     newModuleSpec = secmod_ParseModuleSpecForTokens(PR_TRUE,
   123 				newModule->isFIPS, modulespec, &children, &ids);
   124     if (!newModuleSpec) {
   125 	return SECFailure;
   126     }
   128     /* 
   129      * We are now trying to open a new slot on an already loaded module.
   130      * If that slot represents a cert/key database, we don't want to open
   131      * multiple copies of that same database. Unfortunately we understand
   132      * the softoken flags well enough to be able to do this, so we can only get
   133      * the list of already loaded databases if we are trying to open another
   134      * internal module. 
   135      */
   136     if (oldModule->internal) {
   137 	conflist = secmod_GetConfigList(oldModule->isFIPS, 
   138 					oldModule->libraryParams, &count);
   139     }
   142     /* don't open multiple of the same db */
   143     if (conflist && secmod_MatchConfigList(newModuleSpec, conflist, count)) { 
   144 	rv = SECSuccess;
   145 	goto loser;
   146     }
   147     slot = SECMOD_OpenNewSlot(oldModule, newModuleSpec);
   148     if (slot) {
   149 	int newID;
   150 	char **thisChild;
   151 	CK_SLOT_ID *thisID;
   152 	char *oldModuleSpec;
   154 	if (secmod_IsInternalKeySlot(newModule)) {
   155 	    pk11_SetInternalKeySlotIfFirst(slot);
   156 	} 
   157 	newID = slot->slotID;
   158 	PK11_FreeSlot(slot);
   159 	for (thisChild=children, thisID=ids; thisChild && *thisChild; 
   160 						thisChild++,thisID++) {
   161 	    if (conflist &&
   162 		       secmod_MatchConfigList(*thisChild, conflist, count)) {
   163 		*thisID = (CK_SLOT_ID) -1;
   164 		continue;
   165 	    }
   166 	    slot = SECMOD_OpenNewSlot(oldModule, *thisChild);
   167 	    if (slot) {
   168 		*thisID = slot->slotID;
   169 		PK11_FreeSlot(slot);
   170 	    } else {
   171 		*thisID = (CK_SLOT_ID) -1;
   172 	    }
   173 	}
   175 	/* update the old module initialization string in case we need to
   176 	 * shutdown and reinit the whole mess (this is rare, but can happen
   177 	 * when trying to stop smart card insertion/removal threads)... */
   178 	oldModuleSpec = secmod_MkAppendTokensList(oldModule->arena, 
   179 		oldModule->libraryParams, newModuleSpec, newID, 
   180 		children, ids);
   181 	if (oldModuleSpec) {
   182 	    oldModule->libraryParams = oldModuleSpec;
   183 	}
   185 	rv = SECSuccess;
   186     }
   188 loser:
   189     secmod_FreeChildren(children, ids);
   190     PORT_Free(newModuleSpec);
   191     if (conflist) {
   192 	secmod_FreeConfigList(conflist, count);
   193     }
   194     return rv;
   195 }
   197 /*
   198  * collect the steps we need to initialize a module in a single function
   199  */
   200 SECStatus
   201 secmod_ModuleInit(SECMODModule *mod, SECMODModule **reload, 
   202 		  PRBool* alreadyLoaded)
   203 {
   204     CK_C_INITIALIZE_ARGS moduleArgs;
   205     CK_VOID_PTR pInitArgs;
   206     CK_RV crv;
   208     if (reload) {
   209 	*reload = NULL;
   210     }
   212     if (!mod || !alreadyLoaded) {
   213         PORT_SetError(SEC_ERROR_INVALID_ARGS);
   214         return SECFailure;
   215     }
   217     if (mod->libraryParams == NULL) {
   218 	if (mod->isThreadSafe) {
   219 	    pInitArgs = (void *) &secmodLockFunctions;
   220 	} else {
   221 	    pInitArgs = NULL;
   222 	}
   223     } else {
   224 	if (mod->isThreadSafe) {
   225 	    moduleArgs = secmodLockFunctions;
   226 	} else {
   227 	    moduleArgs = secmodNoLockArgs;
   228 	}
   229 	moduleArgs.LibraryParameters = (void *) mod->libraryParams;
   230 	pInitArgs = &moduleArgs;
   231     }
   232     crv = PK11_GETTAB(mod)->C_Initialize(pInitArgs);
   233     if (CKR_CRYPTOKI_ALREADY_INITIALIZED == crv) {
   234 	SECMODModule *oldModule = NULL;
   236 	/* Library has already been loaded once, if caller expects it, and it
   237 	 * has additional configuration, try reloading it as well. */
   238 	if (reload != NULL && mod->libraryParams) {
   239 	    oldModule = secmod_FindModuleByFuncPtr(mod->functionList);
   240 	}
   241 	/* Library has been loaded by NSS. It means it may be capable of
   242 	 * reloading */
   243 	if (oldModule) {
   244 	    SECStatus rv;
   245 	    rv = secmod_handleReload(oldModule, mod);
   246 	    if (rv == SECSuccess) {
   247 		/* This module should go away soon, since we've
   248 		 * simply expanded the slots on the old module.
   249 		 * When it goes away, it should not Finalize since
   250 		 * that will close our old module as well. Setting
   251 		 * the function list to NULL will prevent that close */
   252 		mod->functionList = NULL;
   253 		*reload = oldModule;
   254 		return SECSuccess;
   255 	    }
   256 	    SECMOD_DestroyModule(oldModule);
   257 	}
   258 	/* reload not possible, fall back to old semantics */
   259 	if (!enforceAlreadyInitializedError) {
   260        	    *alreadyLoaded = PR_TRUE;
   261             return SECSuccess;
   262 	}
   263     }
   264     if (crv != CKR_OK) {
   265 	if (!mod->isThreadSafe ||
   266 		crv == CKR_NETSCAPE_CERTDB_FAILED ||
   267 		crv == CKR_NETSCAPE_KEYDB_FAILED) {
   268 	    PORT_SetError(PK11_MapError(crv));
   269 	    return SECFailure;
   270 	}
   271 	/* If we had attempted to init a single threaded module "with"
   272 	 * parameters and it failed, should we retry "without" parameters?
   273 	 * (currently we don't retry in this scenario) */
   275 	if (!loadSingleThreadedModules) {
   276 	    PORT_SetError(SEC_ERROR_INCOMPATIBLE_PKCS11);
   277 	    return SECFailure;
   278 	}
   279 	/* If we arrive here, the module failed a ThreadSafe init. */
   280 	mod->isThreadSafe = PR_FALSE;
   281 	if (!mod->libraryParams) {
   282 	    pInitArgs = NULL;
   283 	} else {
   284 	    moduleArgs = secmodNoLockArgs;
   285 	    moduleArgs.LibraryParameters = (void *) mod->libraryParams;
   286 	    pInitArgs = &moduleArgs;
   287 	}
   288     	crv = PK11_GETTAB(mod)->C_Initialize(pInitArgs);
   289 	if ((CKR_CRYPTOKI_ALREADY_INITIALIZED == crv) &&
   290 	    (!enforceAlreadyInitializedError)) {
   291 	    *alreadyLoaded = PR_TRUE;
   292 	    return SECSuccess;
   293 	}
   294     	if (crv != CKR_OK)  {
   295 	    PORT_SetError(PK11_MapError(crv));
   296 	    return SECFailure;
   297 	}
   298     }
   299     return SECSuccess;
   300 }
   302 /*
   303  * set the hasRootCerts flags in the module so it can be stored back
   304  * into the database.
   305  */
   306 void
   307 SECMOD_SetRootCerts(PK11SlotInfo *slot, SECMODModule *mod) {
   308     PK11PreSlotInfo *psi = NULL;
   309     int i;
   311     if (slot->hasRootCerts) {
   312 	for (i=0; i < mod->slotInfoCount; i++) {
   313 	    if (slot->slotID == mod->slotInfo[i].slotID) {
   314 		psi = &mod->slotInfo[i];
   315 		break;
   316 	    }
   317 	}
   318 	if (psi == NULL) {
   319 	   /* allocate more slots */
   320 	   PK11PreSlotInfo *psi_list = (PK11PreSlotInfo *)
   321 		PORT_ArenaAlloc(mod->arena,
   322 			(mod->slotInfoCount+1)* sizeof(PK11PreSlotInfo));
   323 	   /* copy the old ones */
   324 	   if (mod->slotInfoCount > 0) {
   325 		PORT_Memcpy(psi_list,mod->slotInfo,
   326 				(mod->slotInfoCount)*sizeof(PK11PreSlotInfo));
   327 	   }
   328 	   /* assign psi to the last new slot */
   329 	   psi = &psi_list[mod->slotInfoCount];
   330 	   psi->slotID = slot->slotID;
   331 	   psi->askpw = 0;
   332 	   psi->timeout = 0;
   333 	   psi ->defaultFlags = 0;
   335 	   /* increment module count & store new list */
   336 	   mod->slotInfo = psi_list;
   337 	   mod->slotInfoCount++;
   339 	}
   340 	psi->hasRootCerts = 1;
   341     }
   342 }
   344 static const char* my_shlib_name =
   345     SHLIB_PREFIX"nss"SHLIB_VERSION"."SHLIB_SUFFIX;
   346 static const char* softoken_shlib_name =
   347     SHLIB_PREFIX"softokn"SOFTOKEN_SHLIB_VERSION"."SHLIB_SUFFIX;
   348 static const PRCallOnceType pristineCallOnce;
   349 static PRCallOnceType loadSoftokenOnce;
   350 static PRLibrary* softokenLib;
   351 static PRInt32 softokenLoadCount;
   353 #include "prio.h"
   354 #include "prprf.h"
   355 #include <stdio.h>
   356 #include "prsystem.h"
   358 /* This function must be run only once. */
   359 /*  determine if hybrid platform, then actually load the DSO. */
   360 static PRStatus
   361 softoken_LoadDSO( void ) 
   362 {
   363   PRLibrary *  handle;
   365   handle = PORT_LoadLibraryFromOrigin(my_shlib_name,
   366                                       (PRFuncPtr) &softoken_LoadDSO,
   367                                       softoken_shlib_name);
   368   if (handle) {
   369     softokenLib = handle;
   370     return PR_SUCCESS;
   371   }
   372   return PR_FAILURE;
   373 }
   375 /*
   376  * load a new module into our address space and initialize it.
   377  */
   378 SECStatus
   379 secmod_LoadPKCS11Module(SECMODModule *mod, SECMODModule **oldModule) {
   380     PRLibrary *library = NULL;
   381     CK_C_GetFunctionList entry = NULL;
   382     CK_INFO info;
   383     CK_ULONG slotCount = 0;
   384     SECStatus rv;
   385     PRBool alreadyLoaded = PR_FALSE;
   386     char *disableUnload = NULL;
   388     if (mod->loaded) return SECSuccess;
   390     /* intenal modules get loaded from their internal list */
   391     if (mod->internal && (mod->dllName == NULL)) {
   392     /*
   393      * Loads softoken as a dynamic library,
   394      * even though the rest of NSS assumes this as the "internal" module.
   395      */
   396     if (!softokenLib && 
   397         PR_SUCCESS != PR_CallOnce(&loadSoftokenOnce, &softoken_LoadDSO))
   398         return SECFailure;
   400     PR_ATOMIC_INCREMENT(&softokenLoadCount);
   402     if (mod->isFIPS) {
   403         entry = (CK_C_GetFunctionList) 
   404                     PR_FindSymbol(softokenLib, "FC_GetFunctionList");
   405     } else {
   406         entry = (CK_C_GetFunctionList) 
   407                     PR_FindSymbol(softokenLib, "NSC_GetFunctionList");
   408     }
   410     if (!entry)
   411         return SECFailure;
   413     if (mod->isModuleDB) {
   414         mod->moduleDBFunc = (CK_C_GetFunctionList) 
   415                     PR_FindSymbol(softokenLib, "NSC_ModuleDBFunc");
   416     }
   418     if (mod->moduleDBOnly) {
   419         mod->loaded = PR_TRUE;
   420         return SECSuccess;
   421     }
   422     } else {
   423 	/* Not internal, load the DLL and look up C_GetFunctionList */
   424 	if (mod->dllName == NULL) {
   425 	    return SECFailure;
   426 	}
   428 	/* load the library. If this succeeds, then we have to remember to
   429 	 * unload the library if anything goes wrong from here on out...
   430 	 */
   431 	library = PR_LoadLibrary(mod->dllName);
   432 	mod->library = (void *)library;
   434 	if (library == NULL) {
   435 	    return SECFailure;
   436 	}
   438 	/*
   439 	 * now we need to get the entry point to find the function pointers
   440 	 */
   441 	if (!mod->moduleDBOnly) {
   442 	    entry = (CK_C_GetFunctionList)
   443 			PR_FindSymbol(library, "C_GetFunctionList");
   444 	}
   445 	if (mod->isModuleDB) {
   446 	    mod->moduleDBFunc = (void *)
   447 			PR_FindSymbol(library, "NSS_ReturnModuleSpecData");
   448 	}
   449 	if (mod->moduleDBFunc == NULL) mod->isModuleDB = PR_FALSE;
   450 	if (entry == NULL) {
   451 	    if (mod->isModuleDB) {
   452 		mod->loaded = PR_TRUE;
   453 		mod->moduleDBOnly = PR_TRUE;
   454 		return SECSuccess;
   455 	    }
   456 	    PR_UnloadLibrary(library);
   457 	    return SECFailure;
   458 	}
   459     }
   461     /*
   462      * We need to get the function list
   463      */
   464     if ((*entry)((CK_FUNCTION_LIST_PTR *)&mod->functionList) != CKR_OK) 
   465 								goto fail;
   467 #ifdef DEBUG_MODULE
   468     if (PR_TRUE) {
   469 	modToDBG = PR_GetEnv("NSS_DEBUG_PKCS11_MODULE");
   470 	if (modToDBG && strcmp(mod->commonName, modToDBG) == 0) {
   471 	    mod->functionList = (void *)nss_InsertDeviceLog(
   472 	                           (CK_FUNCTION_LIST_PTR)mod->functionList);
   473 	}
   474     }
   475 #endif
   477     mod->isThreadSafe = PR_TRUE;
   479     /* Now we initialize the module */
   480     rv = secmod_ModuleInit(mod, oldModule, &alreadyLoaded);
   481     if (rv != SECSuccess) {
   482 	goto fail;
   483     }
   485     /* module has been reloaded, this module itself is done, 
   486      * return to the caller */
   487     if (mod->functionList == NULL) {
   488 	mod->loaded = PR_TRUE; /* technically the module is loaded.. */
   489 	return SECSuccess;
   490     }
   492     /* check the version number */
   493     if (PK11_GETTAB(mod)->C_GetInfo(&info) != CKR_OK) goto fail2;
   494     if (info.cryptokiVersion.major != 2) goto fail2;
   495     /* all 2.0 are a priori *not* thread safe */
   496     if (info.cryptokiVersion.minor < 1) {
   497         if (!loadSingleThreadedModules) {
   498             PORT_SetError(SEC_ERROR_INCOMPATIBLE_PKCS11);
   499             goto fail2;
   500         } else {
   501             mod->isThreadSafe = PR_FALSE;
   502         }
   503     }
   504     mod->cryptokiVersion = info.cryptokiVersion;
   506     /* If we don't have a common name, get it from the PKCS 11 module */
   507     if ((mod->commonName == NULL) || (mod->commonName[0] == 0)) {
   508 	mod->commonName = PK11_MakeString(mod->arena,NULL,
   509 	   (char *)info.libraryDescription, sizeof(info.libraryDescription));
   510 	if (mod->commonName == NULL) goto fail2;
   511     }
   514     /* initialize the Slots */
   515     if (PK11_GETTAB(mod)->C_GetSlotList(CK_FALSE, NULL, &slotCount) == CKR_OK) {
   516 	CK_SLOT_ID *slotIDs;
   517 	int i;
   518 	CK_RV crv;
   520 	mod->slots = (PK11SlotInfo **)PORT_ArenaAlloc(mod->arena,
   521 					sizeof(PK11SlotInfo *) * slotCount);
   522 	if (mod->slots == NULL) goto fail2;
   524 	slotIDs = (CK_SLOT_ID *) PORT_Alloc(sizeof(CK_SLOT_ID)*slotCount);
   525 	if (slotIDs == NULL) {
   526 	    goto fail2;
   527 	}  
   528 	crv = PK11_GETTAB(mod)->C_GetSlotList(CK_FALSE, slotIDs, &slotCount);
   529 	if (crv != CKR_OK) {
   530 	    PORT_Free(slotIDs);
   531 	    goto fail2;
   532 	}
   534 	/* Initialize each slot */
   535 	for (i=0; i < (int)slotCount; i++) {
   536 	    mod->slots[i] = PK11_NewSlotInfo(mod);
   537 	    PK11_InitSlot(mod,slotIDs[i],mod->slots[i]);
   538 	    /* look down the slot info table */
   539 	    PK11_LoadSlotList(mod->slots[i],mod->slotInfo,mod->slotInfoCount);
   540 	    SECMOD_SetRootCerts(mod->slots[i],mod);
   541 	    /* explicitly mark the internal slot as such if IsInternalKeySlot()
   542 	     * is set */
   543 	    if (secmod_IsInternalKeySlot(mod) && (i == (mod->isFIPS ? 0 : 1))) {
   544 		pk11_SetInternalKeySlotIfFirst(mod->slots[i]);
   545 	    } 
   546 	}
   547 	mod->slotCount = slotCount;
   548 	mod->slotInfoCount = 0;
   549 	PORT_Free(slotIDs);
   550     }
   552     mod->loaded = PR_TRUE;
   553     mod->moduleID = nextModuleID++;
   554     return SECSuccess;
   555 fail2:
   556     if (enforceAlreadyInitializedError || (!alreadyLoaded)) {
   557         PK11_GETTAB(mod)->C_Finalize(NULL);
   558     }
   559 fail:
   560     mod->functionList = NULL;
   561     disableUnload = PR_GetEnv("NSS_DISABLE_UNLOAD");
   562     if (library && !disableUnload) {
   563         PR_UnloadLibrary(library);
   564     }
   565     return SECFailure;
   566 }
   568 SECStatus
   569 SECMOD_UnloadModule(SECMODModule *mod) {
   570     PRLibrary *library;
   571     char *disableUnload = NULL;
   573     if (!mod->loaded) {
   574 	return SECFailure;
   575     }
   576     if (finalizeModules) {
   577         if (mod->functionList &&!mod->moduleDBOnly) {
   578 	    PK11_GETTAB(mod)->C_Finalize(NULL);
   579 	}
   580     }
   581     mod->moduleID = 0;
   582     mod->loaded = PR_FALSE;
   584     /* do we want the semantics to allow unloading the internal library?
   585      * if not, we should change this to SECFailure and move it above the
   586      * mod->loaded = PR_FALSE; */
   587     if (mod->internal && (mod->dllName == NULL)) {
   588         if (0 == PR_ATOMIC_DECREMENT(&softokenLoadCount)) {
   589           if (softokenLib) {
   590               disableUnload = PR_GetEnv("NSS_DISABLE_UNLOAD");
   591               if (!disableUnload) {
   592                   PRStatus status = PR_UnloadLibrary(softokenLib);
   593                   PORT_Assert(PR_SUCCESS == status);
   594               }
   595               softokenLib = NULL;
   596           }
   597           loadSoftokenOnce = pristineCallOnce;
   598         }
   599 	return SECSuccess;
   600     }
   602     library = (PRLibrary *)mod->library;
   603     /* paranoia */
   604     if (library == NULL) {
   605 	return SECFailure;
   606     }
   608     disableUnload = PR_GetEnv("NSS_DISABLE_UNLOAD");
   609     if (!disableUnload) {
   610         PR_UnloadLibrary(library);
   611     }
   612     return SECSuccess;
   613 }
   615 void
   616 nss_DumpModuleLog(void)
   617 {
   618 #ifdef DEBUG_MODULE
   619     if (modToDBG) {
   620 	print_final_statistics();
   621     }
   622 #endif
   623 }

mercurial