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