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