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