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/. */
5 * The following handles the loading, unloading and management of
6 * various PCKS #11 modules
26 #define DEBUG_MODULE 1
29 static char *modToDBG
= NULL
;
31 #include "debug_module.c"
34 /* build the PKCS #11 2.01 lock files */
36 secmodCreateMutext(CK_VOID_PTR_PTR pmutex
)
38 *pmutex
= (CK_VOID_PTR
)PZ_NewLock(nssILockOther
);
41 return CKR_HOST_MEMORY
;
45 secmodDestroyMutext(CK_VOID_PTR mutext
)
47 PZ_DestroyLock((PZLock
*)mutext
);
52 secmodLockMutext(CK_VOID_PTR mutext
)
54 PZ_Lock((PZLock
*)mutext
);
59 secmodUnlockMutext(CK_VOID_PTR mutext
)
61 PZ_Unlock((PZLock
*)mutext
);
65 static SECMODModuleID nextModuleID
= 1;
66 static const CK_C_INITIALIZE_ARGS secmodLockFunctions
= {
67 secmodCreateMutext
, secmodDestroyMutext
, secmodLockMutext
,
68 secmodUnlockMutext
, CKF_LIBRARY_CANT_CREATE_OS_THREADS
| CKF_OS_LOCKING_OK
,
71 static const CK_C_INITIALIZE_ARGS secmodNoLockArgs
= {
72 NULL
, NULL
, NULL
, NULL
,
73 CKF_LIBRARY_CANT_CREATE_OS_THREADS
, NULL
76 static PRBool loadSingleThreadedModules
= PR_TRUE
;
77 static PRBool enforceAlreadyInitializedError
= PR_TRUE
;
78 static PRBool finalizeModules
= PR_TRUE
;
80 /* set global options for NSS PKCS#11 module loader */
82 pk11_setGlobalOptions(PRBool noSingleThreadedModules
,
83 PRBool allowAlreadyInitializedModules
,
84 PRBool dontFinalizeModules
)
86 if (noSingleThreadedModules
) {
87 loadSingleThreadedModules
= PR_FALSE
;
89 loadSingleThreadedModules
= PR_TRUE
;
91 if (allowAlreadyInitializedModules
) {
92 enforceAlreadyInitializedError
= PR_FALSE
;
94 enforceAlreadyInitializedError
= PR_TRUE
;
96 if (dontFinalizeModules
) {
97 finalizeModules
= PR_FALSE
;
99 finalizeModules
= PR_TRUE
;
105 pk11_getFinalizeModulesOption(void)
107 return finalizeModules
;
111 * Allow specification loading the same module more than once at init time.
112 * This enables 2 things.
114 * 1) we can load additional databases by manipulating secmod.db/pkcs11.txt.
115 * 2) we can handle the case where some library has already initialized NSS
116 * before the main application.
118 * oldModule is the module we have already initialized.
119 * char *modulespec is the full module spec for the library we want to
123 secmod_handleReload(SECMODModule
*oldModule
, SECMODModule
*newModule
)
130 SECMODConfigList
*conflist
= NULL
;
131 SECStatus rv
= SECFailure
;
134 /* first look for tokens= key words from the module spec */
135 modulespec
= newModule
->libraryParams
;
136 newModuleSpec
= secmod_ParseModuleSpecForTokens(PR_TRUE
,
137 newModule
->isFIPS
, modulespec
, &children
, &ids
);
138 if (!newModuleSpec
) {
143 * We are now trying to open a new slot on an already loaded module.
144 * If that slot represents a cert/key database, we don't want to open
145 * multiple copies of that same database. Unfortunately we understand
146 * the softoken flags well enough to be able to do this, so we can only get
147 * the list of already loaded databases if we are trying to open another
150 if (oldModule
->internal
) {
151 conflist
= secmod_GetConfigList(oldModule
->isFIPS
,
152 oldModule
->libraryParams
, &count
);
155 /* don't open multiple of the same db */
156 if (conflist
&& secmod_MatchConfigList(newModuleSpec
, conflist
, count
)) {
160 slot
= SECMOD_OpenNewSlot(oldModule
, newModuleSpec
);
167 if (secmod_IsInternalKeySlot(newModule
)) {
168 pk11_SetInternalKeySlotIfFirst(slot
);
170 newID
= slot
->slotID
;
172 for (thisChild
= children
, thisID
= ids
; thisChild
&& *thisChild
;
173 thisChild
++, thisID
++) {
175 secmod_MatchConfigList(*thisChild
, conflist
, count
)) {
176 *thisID
= (CK_SLOT_ID
)-1;
179 slot
= SECMOD_OpenNewSlot(oldModule
, *thisChild
);
181 *thisID
= slot
->slotID
;
184 *thisID
= (CK_SLOT_ID
)-1;
188 /* update the old module initialization string in case we need to
189 * shutdown and reinit the whole mess (this is rare, but can happen
190 * when trying to stop smart card insertion/removal threads)... */
191 oldModuleSpec
= secmod_MkAppendTokensList(oldModule
->arena
,
192 oldModule
->libraryParams
, newModuleSpec
, newID
,
195 oldModule
->libraryParams
= oldModuleSpec
;
202 secmod_FreeChildren(children
, ids
);
203 PORT_Free(newModuleSpec
);
205 secmod_FreeConfigList(conflist
, count
);
211 * collect the steps we need to initialize a module in a single function
214 secmod_ModuleInit(SECMODModule
*mod
, SECMODModule
**reload
,
215 PRBool
*alreadyLoaded
)
217 CK_C_INITIALIZE_ARGS moduleArgs
;
218 CK_VOID_PTR pInitArgs
;
225 if (!mod
|| !alreadyLoaded
) {
226 PORT_SetError(SEC_ERROR_INVALID_ARGS
);
230 if (mod
->libraryParams
== NULL
) {
231 if (mod
->isThreadSafe
) {
232 pInitArgs
= (void *)&secmodLockFunctions
;
237 if (mod
->isThreadSafe
) {
238 moduleArgs
= secmodLockFunctions
;
240 moduleArgs
= secmodNoLockArgs
;
242 moduleArgs
.LibraryParameters
= (void *)mod
->libraryParams
;
243 pInitArgs
= &moduleArgs
;
245 crv
= PK11_GETTAB(mod
)->C_Initialize(pInitArgs
);
246 if (CKR_CRYPTOKI_ALREADY_INITIALIZED
== crv
) {
247 SECMODModule
*oldModule
= NULL
;
249 /* Library has already been loaded once, if caller expects it, and it
250 * has additional configuration, try reloading it as well. */
251 if (reload
!= NULL
&& mod
->libraryParams
) {
252 oldModule
= secmod_FindModuleByFuncPtr(mod
->functionList
);
254 /* Library has been loaded by NSS. It means it may be capable of
258 rv
= secmod_handleReload(oldModule
, mod
);
259 if (rv
== SECSuccess
) {
260 /* This module should go away soon, since we've
261 * simply expanded the slots on the old module.
262 * When it goes away, it should not Finalize since
263 * that will close our old module as well. Setting
264 * the function list to NULL will prevent that close */
265 mod
->functionList
= NULL
;
269 SECMOD_DestroyModule(oldModule
);
271 /* reload not possible, fall back to old semantics */
272 if (!enforceAlreadyInitializedError
) {
273 *alreadyLoaded
= PR_TRUE
;
278 if (!mod
->isThreadSafe
||
279 crv
== CKR_NSS_CERTDB_FAILED
||
280 crv
== CKR_NSS_KEYDB_FAILED
) {
281 PORT_SetError(PK11_MapError(crv
));
284 /* If we had attempted to init a single threaded module "with"
285 * parameters and it failed, should we retry "without" parameters?
286 * (currently we don't retry in this scenario) */
288 if (!loadSingleThreadedModules
) {
289 PORT_SetError(SEC_ERROR_INCOMPATIBLE_PKCS11
);
292 /* If we arrive here, the module failed a ThreadSafe init. */
293 mod
->isThreadSafe
= PR_FALSE
;
294 if (!mod
->libraryParams
) {
297 moduleArgs
= secmodNoLockArgs
;
298 moduleArgs
.LibraryParameters
= (void *)mod
->libraryParams
;
299 pInitArgs
= &moduleArgs
;
301 crv
= PK11_GETTAB(mod
)->C_Initialize(pInitArgs
);
302 if ((CKR_CRYPTOKI_ALREADY_INITIALIZED
== crv
) &&
303 (!enforceAlreadyInitializedError
)) {
304 *alreadyLoaded
= PR_TRUE
;
308 PORT_SetError(PK11_MapError(crv
));
316 * set the hasRootCerts flags in the module so it can be stored back
320 SECMOD_SetRootCerts(PK11SlotInfo
*slot
, SECMODModule
*mod
)
322 PK11PreSlotInfo
*psi
= NULL
;
325 if (slot
->hasRootCerts
) {
326 for (i
= 0; i
< mod
->slotInfoCount
; i
++) {
327 if (slot
->slotID
== mod
->slotInfo
[i
].slotID
) {
328 psi
= &mod
->slotInfo
[i
];
333 /* allocate more slots */
334 PK11PreSlotInfo
*psi_list
= (PK11PreSlotInfo
*)
335 PORT_ArenaAlloc(mod
->arena
,
336 (mod
->slotInfoCount
+ 1) * sizeof(PK11PreSlotInfo
));
337 /* copy the old ones */
338 if (mod
->slotInfoCount
> 0) {
339 PORT_Memcpy(psi_list
, mod
->slotInfo
,
340 (mod
->slotInfoCount
) * sizeof(PK11PreSlotInfo
));
342 /* assign psi to the last new slot */
343 psi
= &psi_list
[mod
->slotInfoCount
];
344 psi
->slotID
= slot
->slotID
;
347 psi
->defaultFlags
= 0;
349 /* increment module count & store new list */
350 mod
->slotInfo
= psi_list
;
351 mod
->slotInfoCount
++;
353 psi
->hasRootCerts
= 1;
357 #ifndef NSS_STATIC_SOFTOKEN
358 static const char *my_shlib_name
=
359 SHLIB_PREFIX
"nss" NSS_SHLIB_VERSION
"." SHLIB_SUFFIX
;
360 static const char *softoken_shlib_name
=
361 SHLIB_PREFIX
"softokn" SOFTOKEN_SHLIB_VERSION
"." SHLIB_SUFFIX
;
362 static const PRCallOnceType pristineCallOnce
;
363 static PRCallOnceType loadSoftokenOnce
;
364 static PRLibrary
*softokenLib
;
365 static PRInt32 softokenLoadCount
;
367 /* This function must be run only once. */
368 /* determine if hybrid platform, then actually load the DSO. */
370 softoken_LoadDSO(void)
374 handle
= PORT_LoadLibraryFromOrigin(my_shlib_name
,
375 (PRFuncPtr
)&softoken_LoadDSO
,
376 softoken_shlib_name
);
378 softokenLib
= handle
;
384 CK_RV
NSC_GetInterface(CK_UTF8CHAR_PTR pInterfaceName
,
385 CK_VERSION_PTR pVersion
,
386 CK_INTERFACE_PTR_PTR
*ppInterface
, CK_FLAGS flags
);
387 char **NSC_ModuleDBFunc(unsigned long function
, char *parameters
, void *args
);
391 * load a new module into our address space and initialize it.
394 secmod_LoadPKCS11Module(SECMODModule
*mod
, SECMODModule
**oldModule
)
396 PRLibrary
*library
= NULL
;
397 CK_C_GetInterface ientry
= NULL
;
398 CK_C_GetFunctionList fentry
= NULL
;
400 CK_ULONG slotCount
= 0;
402 PRBool alreadyLoaded
= PR_FALSE
;
403 char *disableUnload
= NULL
;
404 #ifndef NSS_STATIC_SOFTOKEN
405 const char *nss_interface
;
406 const char *nss_function
;
408 CK_INTERFACE_PTR interface
;
413 mod
->fipsIndicator
= NULL
;
415 /* internal modules get loaded from their internal list */
416 if (mod
->internal
&& (mod
->dllName
== NULL
)) {
417 #ifdef NSS_STATIC_SOFTOKEN
418 ientry
= (CK_C_GetInterface
)NSC_GetInterface
;
421 * Loads softoken as a dynamic library,
422 * even though the rest of NSS assumes this as the "internal" module.
425 PR_SUCCESS
!= PR_CallOnce(&loadSoftokenOnce
, &softoken_LoadDSO
))
428 PR_ATOMIC_INCREMENT(&softokenLoadCount
);
431 nss_interface
= "FC_GetInterface";
432 nss_function
= "FC_GetFunctionList";
434 nss_interface
= "NSC_GetInterface";
435 nss_function
= "NSC_GetFunctionList";
438 ientry
= (CK_C_GetInterface
)
439 PR_FindSymbol(softokenLib
, nss_interface
);
441 fentry
= (CK_C_GetFunctionList
)
442 PR_FindSymbol(softokenLib
, nss_function
);
449 if (mod
->isModuleDB
) {
450 mod
->moduleDBFunc
= (CK_C_GetFunctionList
)
451 #ifdef NSS_STATIC_SOFTOKEN
454 PR_FindSymbol(softokenLib
, "NSC_ModuleDBFunc");
458 if (mod
->moduleDBOnly
) {
459 mod
->loaded
= PR_TRUE
;
463 /* Not internal, load the DLL and look up C_GetFunctionList */
464 if (mod
->dllName
== NULL
) {
468 /* load the library. If this succeeds, then we have to remember to
469 * unload the library if anything goes wrong from here on out...
472 if (nssUTF8_Length(mod
->dllName
, NULL
)) {
473 wchar_t *dllNameWide
= _NSSUTIL_UTF8ToWide(mod
->dllName
);
476 libSpec
.type
= PR_LibSpec_PathnameU
;
477 libSpec
.value
.pathname_u
= dllNameWide
;
478 library
= PR_LoadLibraryWithFlags(libSpec
, 0);
479 PORT_Free(dllNameWide
);
482 if (library
== NULL
) {
483 // fallback to system code page
484 library
= PR_LoadLibrary(mod
->dllName
);
487 library
= PR_LoadLibrary(mod
->dllName
);
488 #endif // defined(_WIN32)
489 mod
->library
= (void *)library
;
491 if (library
== NULL
) {
496 * now we need to get the entry point to find the function pointers
498 if (!mod
->moduleDBOnly
) {
499 ientry
= (CK_C_GetInterface
)
500 PR_FindSymbol(library
, "C_GetInterface");
502 fentry
= (CK_C_GetFunctionList
)
503 PR_FindSymbol(library
, "C_GetFunctionList");
506 if (mod
->isModuleDB
) {
507 mod
->moduleDBFunc
= (void *)
508 PR_FindSymbol(library
, "NSS_ReturnModuleSpecData");
510 if (mod
->moduleDBFunc
== NULL
)
511 mod
->isModuleDB
= PR_FALSE
;
512 if ((ientry
== NULL
) && (fentry
== NULL
)) {
513 if (mod
->isModuleDB
) {
514 mod
->loaded
= PR_TRUE
;
515 mod
->moduleDBOnly
= PR_TRUE
;
518 PR_UnloadLibrary(library
);
524 * We need to get the function list
527 /* we first try to get a FORK_SAFE interface */
528 if ((*ientry
)((CK_UTF8CHAR_PTR
) "PKCS 11", NULL
, &interface
,
529 CKF_INTERFACE_FORK_SAFE
) != CKR_OK
) {
530 /* one is not appearantly available, get a non-fork safe version */
531 if ((*ientry
)((CK_UTF8CHAR_PTR
) "PKCS 11", NULL
, &interface
, 0) != CKR_OK
) {
535 mod
->functionList
= interface
->pFunctionList
;
536 mod
->flags
= interface
->flags
;
537 /* if we have a fips indicator, grab it */
538 if ((*ientry
)((CK_UTF8CHAR_PTR
) "Vendor NSS FIPS Interface", NULL
,
539 &interface
, 0) == CKR_OK
) {
540 mod
->fipsIndicator
= ((CK_NSS_FIPS_FUNCTIONS
*)(interface
->pFunctionList
))->NSC_NSSGetFIPSStatus
;
543 if ((*fentry
)((CK_FUNCTION_LIST_PTR
*)&mod
->functionList
) != CKR_OK
)
549 modToDBG
= PR_GetEnvSecure("NSS_DEBUG_PKCS11_MODULE");
550 if (modToDBG
&& strcmp(mod
->commonName
, modToDBG
) == 0) {
551 mod
->functionList
= (void *)nss_InsertDeviceLog(
552 (CK_FUNCTION_LIST_3_0_PTR
)mod
->functionList
);
556 /* This test operation makes sure our locking system is
557 * consistent even if we are using non-thread safe tokens by
558 * simulating unsafe tokens with safe ones. */
559 mod
->isThreadSafe
= !PR_GetEnvSecure("NSS_FORCE_TOKEN_LOCK");
561 /* Now we initialize the module */
562 rv
= secmod_ModuleInit(mod
, oldModule
, &alreadyLoaded
);
563 if (rv
!= SECSuccess
) {
567 /* module has been reloaded, this module itself is done,
568 * return to the caller */
569 if (mod
->functionList
== NULL
) {
570 mod
->loaded
= PR_TRUE
; /* technically the module is loaded.. */
574 /* check the version number */
575 if (PK11_GETTAB(mod
)->C_GetInfo(&info
) != CKR_OK
)
577 if (info
.cryptokiVersion
.major
< 2)
579 /* all 2.0 are a priori *not* thread safe */
580 if ((info
.cryptokiVersion
.major
== 2) && (info
.cryptokiVersion
.minor
< 1)) {
581 if (!loadSingleThreadedModules
) {
582 PORT_SetError(SEC_ERROR_INCOMPATIBLE_PKCS11
);
585 mod
->isThreadSafe
= PR_FALSE
;
588 mod
->cryptokiVersion
= info
.cryptokiVersion
;
590 /* If we don't have a common name, get it from the PKCS 11 module */
591 if ((mod
->commonName
== NULL
) || (mod
->commonName
[0] == 0)) {
592 mod
->commonName
= PK11_MakeString(mod
->arena
, NULL
,
593 (char *)info
.libraryDescription
, sizeof(info
.libraryDescription
));
594 if (mod
->commonName
== NULL
)
598 /* initialize the Slots */
599 if (PK11_GETTAB(mod
)->C_GetSlotList(CK_FALSE
, NULL
, &slotCount
) == CKR_OK
) {
604 mod
->slots
= (PK11SlotInfo
**)PORT_ArenaAlloc(mod
->arena
,
605 sizeof(PK11SlotInfo
*) * slotCount
);
606 if (mod
->slots
== NULL
)
609 slotIDs
= (CK_SLOT_ID
*)PORT_Alloc(sizeof(CK_SLOT_ID
) * slotCount
);
610 if (slotIDs
== NULL
) {
613 crv
= PK11_GETTAB(mod
)->C_GetSlotList(CK_FALSE
, slotIDs
, &slotCount
);
619 /* Initialize each slot */
620 for (i
= 0; i
< (int)slotCount
; i
++) {
621 mod
->slots
[i
] = PK11_NewSlotInfo(mod
);
622 PK11_InitSlot(mod
, slotIDs
[i
], mod
->slots
[i
]);
623 /* look down the slot info table */
624 PK11_LoadSlotList(mod
->slots
[i
], mod
->slotInfo
, mod
->slotInfoCount
);
625 SECMOD_SetRootCerts(mod
->slots
[i
], mod
);
626 /* explicitly mark the internal slot as such if IsInternalKeySlot()
628 if (secmod_IsInternalKeySlot(mod
) && (i
== (mod
->isFIPS
? 0 : 1))) {
629 pk11_SetInternalKeySlotIfFirst(mod
->slots
[i
]);
632 mod
->slotCount
= slotCount
;
633 mod
->slotInfoCount
= 0;
637 mod
->loaded
= PR_TRUE
;
638 mod
->moduleID
= nextModuleID
++;
641 if (enforceAlreadyInitializedError
|| (!alreadyLoaded
)) {
642 PK11_GETTAB(mod
)->C_Finalize(NULL
);
645 mod
->functionList
= NULL
;
646 disableUnload
= PR_GetEnvSecure("NSS_DISABLE_UNLOAD");
647 if (library
&& !disableUnload
) {
648 PR_UnloadLibrary(library
);
654 SECMOD_UnloadModule(SECMODModule
*mod
)
657 char *disableUnload
= NULL
;
662 if (finalizeModules
) {
663 if (mod
->functionList
&& !mod
->moduleDBOnly
) {
664 PK11_GETTAB(mod
)->C_Finalize(NULL
);
668 mod
->loaded
= PR_FALSE
;
670 /* do we want the semantics to allow unloading the internal library?
671 * if not, we should change this to SECFailure and move it above the
672 * mod->loaded = PR_FALSE; */
673 if (mod
->internal
&& (mod
->dllName
== NULL
)) {
674 #ifndef NSS_STATIC_SOFTOKEN
675 if (0 == PR_ATOMIC_DECREMENT(&softokenLoadCount
)) {
677 disableUnload
= PR_GetEnvSecure("NSS_DISABLE_UNLOAD");
678 if (!disableUnload
) {
680 PRStatus status
= PR_UnloadLibrary(softokenLib
);
681 PORT_Assert(PR_SUCCESS
== status
);
683 PR_UnloadLibrary(softokenLib
);
688 loadSoftokenOnce
= pristineCallOnce
;
694 library
= (PRLibrary
*)mod
->library
;
696 if (library
== NULL
) {
700 disableUnload
= PR_GetEnvSecure("NSS_DISABLE_UNLOAD");
701 if (!disableUnload
) {
702 PR_UnloadLibrary(library
);
708 nss_DumpModuleLog(void)
712 print_final_statistics();