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