1 /*
2  * NSS utility functions
3  *
4  * This Source Code Form is subject to the terms of the Mozilla Public
5  * License, v. 2.0. If a copy of the MPL was not distributed with this
6  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 
8 #include <ctype.h>
9 #include <string.h>
10 #include "seccomon.h"
11 #include "prinit.h"
12 #include "prprf.h"
13 #include "prmem.h"
14 #include "cert.h"
15 #include "keyhi.h"
16 #include "secmod.h"
17 #include "secoid.h"
18 #include "nss.h"
19 #include "pk11func.h"
20 #include "secerr.h"
21 #include "nssbase.h"
22 #include "nssutil.h"
23 
24 #ifndef NSS_DISABLE_LIBPKIX
25 #include "pkixt.h"
26 #include "pkix.h"
27 #include "pkix_tools.h"
28 #endif /* NSS_DISABLE_LIBPKIX */
29 
30 #include "pki3hack.h"
31 #include "certi.h"
32 #include "secmodi.h"
33 #include "ocspti.h"
34 #include "ocspi.h"
35 #include "utilpars.h"
36 
37 /*
38  * On Windows nss3.dll needs to export the symbol 'mktemp' to be
39  * fully backward compatible with the nss3.dll in NSS 3.2.x and
40  * 3.3.x.  This symbol was unintentionally exported and its
41  * definition (in DBM) was moved from nss3.dll to softokn3.dll
42  * in NSS 3.4.  See bug 142575.
43  */
44 #ifdef WIN32_NSS3_DLL_COMPAT
45 #include <io.h>
46 
47 /* exported as 'mktemp' */
48 char *
nss_mktemp(char * path)49 nss_mktemp(char *path)
50 {
51     return _mktemp(path);
52 }
53 #endif
54 
55 #define NSS_MAX_FLAG_SIZE sizeof("readOnly") + sizeof("noCertDB") +                                  \
56                               sizeof("noModDB") + sizeof("forceOpen") + sizeof("passwordRequired") + \
57                               sizeof("optimizeSpace") + sizeof("printPolicyFeedback")
58 #define NSS_DEFAULT_MOD_NAME "NSS Internal Module"
59 
60 static char *
nss_makeFlags(PRBool readOnly,PRBool noCertDB,PRBool noModDB,PRBool forceOpen,PRBool passwordRequired,PRBool optimizeSpace)61 nss_makeFlags(PRBool readOnly, PRBool noCertDB,
62               PRBool noModDB, PRBool forceOpen,
63               PRBool passwordRequired, PRBool optimizeSpace)
64 {
65     char *flags = (char *)PORT_Alloc(NSS_MAX_FLAG_SIZE);
66     PRBool first = PR_TRUE;
67 
68     PORT_Memset(flags, 0, NSS_MAX_FLAG_SIZE);
69     if (readOnly) {
70         PORT_Strcat(flags, "readOnly");
71         first = PR_FALSE;
72     }
73     if (noCertDB) {
74         if (!first)
75             PORT_Strcat(flags, ",");
76         PORT_Strcat(flags, "noCertDB");
77         first = PR_FALSE;
78     }
79     if (noModDB) {
80         if (!first)
81             PORT_Strcat(flags, ",");
82         PORT_Strcat(flags, "noModDB");
83         first = PR_FALSE;
84     }
85     if (forceOpen) {
86         if (!first)
87             PORT_Strcat(flags, ",");
88         PORT_Strcat(flags, "forceOpen");
89         first = PR_FALSE;
90     }
91     if (passwordRequired) {
92         if (!first)
93             PORT_Strcat(flags, ",");
94         PORT_Strcat(flags, "passwordRequired");
95         first = PR_FALSE;
96     }
97     if (optimizeSpace) {
98         if (!first)
99             PORT_Strcat(flags, ",");
100         PORT_Strcat(flags, "optimizeSpace");
101     }
102     return flags;
103 }
104 
105 /*
106  * build config string from individual internationalized strings
107  */
108 char *
nss_MkConfigString(const char * man,const char * libdesc,const char * tokdesc,const char * ptokdesc,const char * slotdesc,const char * pslotdesc,const char * fslotdesc,const char * fpslotdesc,int minPwd)109 nss_MkConfigString(const char *man, const char *libdesc, const char *tokdesc,
110                    const char *ptokdesc, const char *slotdesc, const char *pslotdesc,
111                    const char *fslotdesc, const char *fpslotdesc, int minPwd)
112 {
113     char *strings = NULL;
114     char *newStrings;
115 
116     /* make sure the internationalization was done correctly... */
117     strings = PR_smprintf("");
118     if (strings == NULL)
119         return NULL;
120 
121     if (man) {
122         newStrings = PR_smprintf("%s manufacturerID='%s'", strings, man);
123         PR_smprintf_free(strings);
124         strings = newStrings;
125     }
126     if (strings == NULL)
127         return NULL;
128 
129     if (libdesc) {
130         newStrings = PR_smprintf("%s libraryDescription='%s'", strings, libdesc);
131         PR_smprintf_free(strings);
132         strings = newStrings;
133     }
134     if (strings == NULL)
135         return NULL;
136 
137     if (tokdesc) {
138         newStrings = PR_smprintf("%s cryptoTokenDescription='%s'", strings,
139                                  tokdesc);
140         PR_smprintf_free(strings);
141         strings = newStrings;
142     }
143     if (strings == NULL)
144         return NULL;
145 
146     if (ptokdesc) {
147         newStrings = PR_smprintf("%s dbTokenDescription='%s'", strings, ptokdesc);
148         PR_smprintf_free(strings);
149         strings = newStrings;
150     }
151     if (strings == NULL)
152         return NULL;
153 
154     if (slotdesc) {
155         newStrings = PR_smprintf("%s cryptoSlotDescription='%s'", strings,
156                                  slotdesc);
157         PR_smprintf_free(strings);
158         strings = newStrings;
159     }
160     if (strings == NULL)
161         return NULL;
162 
163     if (pslotdesc) {
164         newStrings = PR_smprintf("%s dbSlotDescription='%s'", strings, pslotdesc);
165         PR_smprintf_free(strings);
166         strings = newStrings;
167     }
168     if (strings == NULL)
169         return NULL;
170 
171     if (fslotdesc) {
172         newStrings = PR_smprintf("%s FIPSSlotDescription='%s'",
173                                  strings, fslotdesc);
174         PR_smprintf_free(strings);
175         strings = newStrings;
176     }
177     if (strings == NULL)
178         return NULL;
179 
180     if (fpslotdesc) {
181         newStrings = PR_smprintf("%s FIPSTokenDescription='%s'",
182                                  strings, fpslotdesc);
183         PR_smprintf_free(strings);
184         strings = newStrings;
185     }
186     if (strings == NULL)
187         return NULL;
188 
189     newStrings = PR_smprintf("%s minPS=%d", strings, minPwd);
190     PR_smprintf_free(strings);
191     strings = newStrings;
192 
193     return (strings);
194 }
195 
196 /*
197  * statics to remember the PK11_ConfigurePKCS11()
198  * info.
199  */
200 static char *pk11_config_strings = NULL;
201 static char *pk11_config_name = NULL;
202 static PRBool pk11_password_required = PR_FALSE;
203 
204 /*
205  * this is a legacy configuration function which used to be part of
206  * the PKCS #11 internal token.
207  */
208 void
PK11_ConfigurePKCS11(const char * man,const char * libdesc,const char * tokdesc,const char * ptokdesc,const char * slotdesc,const char * pslotdesc,const char * fslotdesc,const char * fpslotdesc,int minPwd,int pwRequired)209 PK11_ConfigurePKCS11(const char *man, const char *libdesc, const char *tokdesc,
210                      const char *ptokdesc, const char *slotdesc, const char *pslotdesc,
211                      const char *fslotdesc, const char *fpslotdesc, int minPwd,
212                      int pwRequired)
213 {
214     char *strings;
215 
216     strings = nss_MkConfigString(man, libdesc, tokdesc, ptokdesc, slotdesc,
217                                  pslotdesc, fslotdesc, fpslotdesc, minPwd);
218     if (strings == NULL) {
219         return;
220     }
221 
222     if (libdesc) {
223         if (pk11_config_name != NULL) {
224             PORT_Free(pk11_config_name);
225         }
226         pk11_config_name = PORT_Strdup(libdesc);
227     }
228 
229     if (pk11_config_strings != NULL) {
230         PR_smprintf_free(pk11_config_strings);
231     }
232     pk11_config_strings = strings;
233     pk11_password_required = pwRequired;
234 
235     return;
236 }
237 
238 void
PK11_UnconfigurePKCS11(void)239 PK11_UnconfigurePKCS11(void)
240 {
241     if (pk11_config_strings != NULL) {
242         PR_smprintf_free(pk11_config_strings);
243         pk11_config_strings = NULL;
244     }
245     if (pk11_config_name) {
246         PORT_Free(pk11_config_name);
247         pk11_config_name = NULL;
248     }
249 }
250 
251 /*
252  * The following code is an attempt to automagically find the external root
253  * module.
254  * Note: Keep the #if-defined chunks in order. HPUX must select before UNIX.
255  */
256 
257 static const char *dllname =
258 #if defined(XP_WIN32) || defined(XP_OS2)
259     "nssckbi.dll";
260 #elif defined(HPUX) && !defined(__ia64) /* HP-UX PA-RISC */
261     "libnssckbi.sl";
262 #elif defined(DARWIN)
263     "libnssckbi.dylib";
264 #elif defined(XP_UNIX) || defined(XP_BEOS)
265     "libnssckbi.so";
266 #else
267 #error "Uh! Oh! I don't know about this platform."
268 #endif
269 
270 /* Should we have platform ifdefs here??? */
271 #define FILE_SEP '/'
272 
273 static void
nss_FindExternalRootPaths(const char * dbpath,const char * secmodprefix,char ** retoldpath,char ** retnewpath)274 nss_FindExternalRootPaths(const char *dbpath,
275                           const char *secmodprefix,
276                           char **retoldpath, char **retnewpath)
277 {
278     char *path, *oldpath = NULL, *lastsep;
279     int len, path_len, secmod_len, dll_len;
280 
281     path_len = PORT_Strlen(dbpath);
282     secmod_len = secmodprefix ? PORT_Strlen(secmodprefix) : 0;
283     dll_len = PORT_Strlen(dllname);
284     len = path_len + secmod_len + dll_len + 2; /* FILE_SEP + NULL */
285 
286     path = PORT_Alloc(len);
287     if (path == NULL)
288         return;
289 
290     /* back up to the top of the directory */
291     PORT_Memcpy(path, dbpath, path_len);
292     if (path[path_len - 1] != FILE_SEP) {
293         path[path_len++] = FILE_SEP;
294     }
295     PORT_Strcpy(&path[path_len], dllname);
296     if (secmod_len > 0) {
297         lastsep = PORT_Strrchr(secmodprefix, FILE_SEP);
298         if (lastsep) {
299             int secmoddir_len = lastsep - secmodprefix + 1; /* FILE_SEP */
300             oldpath = PORT_Alloc(len);
301             if (oldpath == NULL) {
302                 PORT_Free(path);
303                 return;
304             }
305             PORT_Memcpy(oldpath, path, path_len);
306             PORT_Memcpy(&oldpath[path_len], secmodprefix, secmoddir_len);
307             PORT_Strcpy(&oldpath[path_len + secmoddir_len], dllname);
308         }
309     }
310     *retoldpath = oldpath;
311     *retnewpath = path;
312     return;
313 }
314 
315 static void
nss_FreeExternalRootPaths(char * oldpath,char * path)316 nss_FreeExternalRootPaths(char *oldpath, char *path)
317 {
318     if (path) {
319         PORT_Free(path);
320     }
321     if (oldpath) {
322         PORT_Free(oldpath);
323     }
324 }
325 
326 static void
nss_FindExternalRoot(const char * dbpath,const char * secmodprefix)327 nss_FindExternalRoot(const char *dbpath, const char *secmodprefix)
328 {
329     char *path = NULL;
330     char *oldpath = NULL;
331     PRBool hasrootcerts = PR_FALSE;
332 
333     /*
334      * 'oldpath' is the external root path in NSS 3.3.x or older.
335      * For backward compatibility we try to load the root certs
336      * module with the old path first.
337      */
338     nss_FindExternalRootPaths(dbpath, secmodprefix, &oldpath, &path);
339     if (oldpath) {
340         (void)SECMOD_AddNewModule("Root Certs", oldpath, 0, 0);
341         hasrootcerts = SECMOD_HasRootCerts();
342     }
343     if (path && !hasrootcerts) {
344         (void)SECMOD_AddNewModule("Root Certs", path, 0, 0);
345     }
346     nss_FreeExternalRootPaths(oldpath, path);
347     return;
348 }
349 
350 /*
351  * see nss_Init for definitions of the various options.
352  *
353  * this function builds a moduleSpec string from the options and previously
354  * set statics (from PKCS11_Configure, for instance), and uses it to kick off
355  * the loading of the various PKCS #11 modules.
356  */
357 static SECMODModule *
nss_InitModules(const char * configdir,const char * certPrefix,const char * keyPrefix,const char * secmodName,const char * updateDir,const char * updCertPrefix,const char * updKeyPrefix,const char * updateID,const char * updateName,char * configName,char * configStrings,PRBool pwRequired,PRBool readOnly,PRBool noCertDB,PRBool noModDB,PRBool forceOpen,PRBool optimizeSpace,PRBool isContextInit)358 nss_InitModules(const char *configdir, const char *certPrefix,
359                 const char *keyPrefix, const char *secmodName,
360                 const char *updateDir, const char *updCertPrefix,
361                 const char *updKeyPrefix, const char *updateID,
362                 const char *updateName, char *configName, char *configStrings,
363                 PRBool pwRequired, PRBool readOnly, PRBool noCertDB,
364                 PRBool noModDB, PRBool forceOpen, PRBool optimizeSpace,
365                 PRBool isContextInit)
366 {
367     SECMODModule *module = NULL;
368     char *moduleSpec = NULL;
369     char *flags = NULL;
370     char *lconfigdir = NULL;
371     char *lcertPrefix = NULL;
372     char *lkeyPrefix = NULL;
373     char *lsecmodName = NULL;
374     char *lupdateDir = NULL;
375     char *lupdCertPrefix = NULL;
376     char *lupdKeyPrefix = NULL;
377     char *lupdateID = NULL;
378     char *lupdateName = NULL;
379 
380     if (NSS_InitializePRErrorTable() != SECSuccess) {
381         PORT_SetError(SEC_ERROR_NO_MEMORY);
382         return NULL;
383     }
384 
385     flags = nss_makeFlags(readOnly, noCertDB, noModDB, forceOpen,
386                           pwRequired, optimizeSpace);
387     if (flags == NULL)
388         return NULL;
389 
390     /*
391      * configdir is double nested, and Windows uses the same character
392      * for file seps as we use for escapes! (sigh).
393      */
394     lconfigdir = NSSUTIL_DoubleEscape(configdir, '\'', '\"');
395     if (lconfigdir == NULL) {
396         goto loser;
397     }
398     lcertPrefix = NSSUTIL_DoubleEscape(certPrefix, '\'', '\"');
399     if (lcertPrefix == NULL) {
400         goto loser;
401     }
402     lkeyPrefix = NSSUTIL_DoubleEscape(keyPrefix, '\'', '\"');
403     if (lkeyPrefix == NULL) {
404         goto loser;
405     }
406     lsecmodName = NSSUTIL_DoubleEscape(secmodName, '\'', '\"');
407     if (lsecmodName == NULL) {
408         goto loser;
409     }
410     lupdateDir = NSSUTIL_DoubleEscape(updateDir, '\'', '\"');
411     if (lupdateDir == NULL) {
412         goto loser;
413     }
414     lupdCertPrefix = NSSUTIL_DoubleEscape(updCertPrefix, '\'', '\"');
415     if (lupdCertPrefix == NULL) {
416         goto loser;
417     }
418     lupdKeyPrefix = NSSUTIL_DoubleEscape(updKeyPrefix, '\'', '\"');
419     if (lupdKeyPrefix == NULL) {
420         goto loser;
421     }
422     lupdateID = NSSUTIL_DoubleEscape(updateID, '\'', '\"');
423     if (lupdateID == NULL) {
424         goto loser;
425     }
426     lupdateName = NSSUTIL_DoubleEscape(updateName, '\'', '\"');
427     if (lupdateName == NULL) {
428         goto loser;
429     }
430 
431     moduleSpec = PR_smprintf(
432         "name=\"%s\" parameters=\"configdir='%s' certPrefix='%s' keyPrefix='%s' "
433         "secmod='%s' flags=%s updatedir='%s' updateCertPrefix='%s' "
434         "updateKeyPrefix='%s' updateid='%s' updateTokenDescription='%s' %s\" "
435         "NSS=\"flags=internal,moduleDB,moduleDBOnly,critical%s\"",
436         configName ? configName : NSS_DEFAULT_MOD_NAME,
437         lconfigdir, lcertPrefix, lkeyPrefix, lsecmodName, flags,
438         lupdateDir, lupdCertPrefix, lupdKeyPrefix, lupdateID,
439         lupdateName, configStrings ? configStrings : "",
440         isContextInit ? "" : ",defaultModDB,internalKeySlot");
441 
442 loser:
443     PORT_Free(flags);
444     if (lconfigdir)
445         PORT_Free(lconfigdir);
446     if (lcertPrefix)
447         PORT_Free(lcertPrefix);
448     if (lkeyPrefix)
449         PORT_Free(lkeyPrefix);
450     if (lsecmodName)
451         PORT_Free(lsecmodName);
452     if (lupdateDir)
453         PORT_Free(lupdateDir);
454     if (lupdCertPrefix)
455         PORT_Free(lupdCertPrefix);
456     if (lupdKeyPrefix)
457         PORT_Free(lupdKeyPrefix);
458     if (lupdateID)
459         PORT_Free(lupdateID);
460     if (lupdateName)
461         PORT_Free(lupdateName);
462 
463     if (moduleSpec) {
464         module = SECMOD_LoadModule(moduleSpec, NULL, PR_TRUE);
465         PR_smprintf_free(moduleSpec);
466         if (module && !module->loaded) {
467             SECMOD_DestroyModule(module);
468             return NULL;
469         }
470     }
471     return module;
472 }
473 
474 /*
475  * OK there are now lots of options here, lets go through them all:
476  *
477  * configdir - base directory where all the cert, key, and module datbases live.
478  * certPrefix - prefix added to the beginning of the cert database example: "
479  *             "https-server1-"
480  * keyPrefix - prefix added to the beginning of the key database example: "
481  *             "https-server1-"
482  * secmodName - name of the security module database (usually "secmod.db").
483  * updateDir - used in initMerge, old directory to update from.
484  * updateID - used in initMerge, unique ID to represent the updated directory.
485  * updateName - used in initMerge, token name when updating.
486  * initContextPtr -  used in initContext, pointer to return a unique context
487  *            value.
488  * readOnly - Boolean: true if the databases are to be opened read only.
489  * nocertdb - Don't open the cert DB and key DB's, just initialize the
490  *             Volatile certdb.
491  * nomoddb - Don't open the security module DB, just initialize the
492  *             PKCS #11 module.
493  * forceOpen - Continue to force initializations even if the databases cannot
494  *             be opened.
495  * noRootInit - don't try to automatically load the root cert store if one is
496  *           not found.
497  * optimizeSpace - tell NSS to use fewer hash table buckets.
498  *
499  * The next three options are used in an attempt to share PKCS #11 modules
500  * with other loaded, running libraries. PKCS #11 was not designed with this
501  * sort of sharing in mind, so use of these options may lead to questionable
502  * results. These options are may be incompatible with NSS_LoadContext() calls.
503  *
504  * noSingleThreadedModules - don't load modules that are not thread safe (many
505  *           smart card tokens will not work).
506  * allowAlreadyInitializedModules - if a module has already been loaded and
507  *           initialize try to use it.
508  * don'tFinalizeModules -  dont shutdown modules we may have loaded.
509  */
510 
511 static PRBool nssIsInitted = PR_FALSE;
512 static NSSInitContext *nssInitContextList = NULL;
513 
514 #ifndef NSS_DISABLE_LIBPKIX
515 static void *plContext = NULL;
516 #endif /* NSS_DISABLE_LIBPKIX */
517 
518 struct NSSInitContextStr {
519     NSSInitContext *next;
520     PRUint32 magic;
521 };
522 
523 #define NSS_INIT_MAGIC 0x1413A91C
524 static SECStatus nss_InitShutdownList(void);
525 
526 /* All initialized to zero in BSS */
527 static PRCallOnceType nssInitOnce;
528 static PZLock *nssInitLock;
529 static PZCondVar *nssInitCondition;
530 static int nssIsInInit;
531 
532 static PRStatus
nss_doLockInit(void)533 nss_doLockInit(void)
534 {
535     nssInitLock = PZ_NewLock(nssILockOther);
536     if (nssInitLock == NULL) {
537         return PR_FAILURE;
538     }
539     nssInitCondition = PZ_NewCondVar(nssInitLock);
540     if (nssInitCondition == NULL) {
541         return PR_FAILURE;
542     }
543     return PR_SUCCESS;
544 }
545 
546 static SECStatus
nss_Init(const char * configdir,const char * certPrefix,const char * keyPrefix,const char * secmodName,const char * updateDir,const char * updCertPrefix,const char * updKeyPrefix,const char * updateID,const char * updateName,NSSInitContext ** initContextPtr,NSSInitParameters * initParams,PRBool readOnly,PRBool noCertDB,PRBool noModDB,PRBool forceOpen,PRBool noRootInit,PRBool optimizeSpace,PRBool noSingleThreadedModules,PRBool allowAlreadyInitializedModules,PRBool dontFinalizeModules)547 nss_Init(const char *configdir, const char *certPrefix, const char *keyPrefix,
548          const char *secmodName, const char *updateDir,
549          const char *updCertPrefix, const char *updKeyPrefix,
550          const char *updateID, const char *updateName,
551          NSSInitContext **initContextPtr,
552          NSSInitParameters *initParams,
553          PRBool readOnly, PRBool noCertDB,
554          PRBool noModDB, PRBool forceOpen, PRBool noRootInit,
555          PRBool optimizeSpace, PRBool noSingleThreadedModules,
556          PRBool allowAlreadyInitializedModules,
557          PRBool dontFinalizeModules)
558 {
559     SECMODModule *parent = NULL;
560 #ifndef NSS_DISABLE_LIBPKIX
561     PKIX_UInt32 actualMinorVersion = 0;
562     PKIX_Error *pkixError = NULL;
563 #endif /* NSS_DISABLE_LIBPKIX */
564     PRBool isReallyInitted;
565     char *configStrings = NULL;
566     char *configName = NULL;
567     PRBool passwordRequired = PR_FALSE;
568 #ifdef POLICY_FILE
569     char *ignoreVar;
570 #endif
571 
572     /* if we are trying to init with a traditional NSS_Init call, maintain
573      * the traditional idempotent behavior. */
574     if (!initContextPtr && nssIsInitted) {
575         return SECSuccess;
576     }
577 
578     /* make sure our lock and condition variable are initialized one and only
579      * one time */
580     if (PR_CallOnce(&nssInitOnce, nss_doLockInit) != PR_SUCCESS) {
581         return SECFailure;
582     }
583 
584     /*
585      * if we haven't done basic initialization, single thread the
586      * initializations.
587      */
588     PZ_Lock(nssInitLock);
589     isReallyInitted = NSS_IsInitialized();
590     if (!isReallyInitted) {
591         while (!isReallyInitted && nssIsInInit) {
592             PZ_WaitCondVar(nssInitCondition, PR_INTERVAL_NO_TIMEOUT);
593             isReallyInitted = NSS_IsInitialized();
594         }
595         /* once we've completed basic initialization, we can allow more than
596          * one process initialize NSS at a time. */
597     }
598     nssIsInInit++;
599     PZ_Unlock(nssInitLock);
600 
601     /* this tells us whether or not some library has already initialized us.
602      * if so, we don't want to double call some of the basic initialization
603      * functions */
604 
605     if (!isReallyInitted) {
606 #ifdef DEBUG
607         CERTCertificate dummyCert;
608         /* New option bits must not change the size of CERTCertificate. */
609         PORT_Assert(sizeof(dummyCert.options) == sizeof(void *));
610 #endif
611 
612         if (SECSuccess != cert_InitLocks()) {
613             goto loser;
614         }
615 
616         if (SECSuccess != InitCRLCache()) {
617             goto loser;
618         }
619 
620         if (SECSuccess != OCSP_InitGlobal()) {
621             goto loser;
622         }
623     }
624 
625     if (noSingleThreadedModules || allowAlreadyInitializedModules ||
626         dontFinalizeModules) {
627         pk11_setGlobalOptions(noSingleThreadedModules,
628                               allowAlreadyInitializedModules,
629                               dontFinalizeModules);
630     }
631 
632     if (initContextPtr) {
633         *initContextPtr = PORT_ZNew(NSSInitContext);
634         if (*initContextPtr == NULL) {
635             goto loser;
636         }
637         /*
638          * For traditional NSS_Init, we used the PK11_Configure() call to set
639          * globals. with InitContext, we pass those strings in as parameters.
640          *
641          * This allows old NSS_Init calls to work as before, while at the same
642          * time new calls and old calls will not interfere with each other.
643          */
644         if (initParams) {
645             if (initParams->length < sizeof(NSSInitParameters)) {
646                 PORT_SetError(SEC_ERROR_INVALID_ARGS);
647                 goto loser;
648             }
649             configStrings = nss_MkConfigString(initParams->manufactureID,
650                                                initParams->libraryDescription,
651                                                initParams->cryptoTokenDescription,
652                                                initParams->dbTokenDescription,
653                                                initParams->cryptoSlotDescription,
654                                                initParams->dbSlotDescription,
655                                                initParams->FIPSSlotDescription,
656                                                initParams->FIPSTokenDescription,
657                                                initParams->minPWLen);
658             if (configStrings == NULL) {
659                 PORT_SetError(SEC_ERROR_NO_MEMORY);
660                 goto loser;
661             }
662             configName = initParams->libraryDescription;
663             passwordRequired = initParams->passwordRequired;
664         }
665 
666         /* If we're NSS_ContextInit, we're probably a library. It could be
667          * possible that the application initialized NSS then forked(). The
668          * library would have no knowledge of that. If we call
669          * SECMOD_RestartModules() here, we will be able to continue on with
670          * NSS as normal. SECMOD_RestartModules() does have the side affect
671          * of losing all our PKCS #11 objects in the new process, but only if
672          * the module needs to be reinited. If it needs to be reinit those
673          * objects are inaccessible anyway, it's always save to call
674          * SECMOD_RestartModules(PR_FALSE).
675          */
676         /* NOTE: We could call SECMOD_Init() here, but if we aren't already
677          * inited, then there's no modules to restart, so SECMOD_RestartModules
678          * will return immediately */
679         SECMOD_RestartModules(PR_FALSE);
680     } else {
681         configStrings = pk11_config_strings;
682         configName = pk11_config_name;
683         passwordRequired = pk11_password_required;
684     }
685 
686     /* Skip the module init if we are already initted and we are trying
687      * to init with noCertDB and noModDB */
688     if (!(isReallyInitted && noCertDB && noModDB)) {
689         parent = nss_InitModules(configdir, certPrefix, keyPrefix, secmodName,
690                                  updateDir, updCertPrefix, updKeyPrefix, updateID,
691                                  updateName, configName, configStrings, passwordRequired,
692                                  readOnly, noCertDB, noModDB, forceOpen, optimizeSpace,
693                                  (initContextPtr != NULL));
694 
695         if (parent == NULL) {
696             goto loser;
697         }
698     }
699 
700     /* finish up initialization */
701     if (!isReallyInitted) {
702         if (SECOID_Init() != SECSuccess) {
703             goto loser;
704         }
705 #ifdef POLICY_FILE
706         /* Load the system crypto policy file if it exists,
707          * unless the NSS_IGNORE_SYSTEM_POLICY environment
708          * variable has been set to 1. */
709         ignoreVar = PR_GetEnvSecure("NSS_IGNORE_SYSTEM_POLICY");
710         if (ignoreVar == NULL || strncmp(ignoreVar, "1", sizeof("1")) != 0) {
711             if (PR_Access(POLICY_PATH "/" POLICY_FILE, PR_ACCESS_READ_OK) == PR_SUCCESS) {
712                 SECMODModule *module = SECMOD_LoadModule(
713                     "name=\"Policy File\" "
714                     "parameters=\"configdir='sql:" POLICY_PATH "' "
715                     "secmod='" POLICY_FILE "' "
716                     "flags=readOnly,noCertDB,forceSecmodChoice,forceOpen\" "
717                     "NSS=\"flags=internal,moduleDB,skipFirst,moduleDBOnly,critical\"",
718                     parent, PR_TRUE);
719                 if (module) {
720                     PRBool isLoaded = module->loaded;
721                     SECMOD_DestroyModule(module);
722                     if (!isLoaded) {
723                         goto loser;
724                     }
725                 }
726             }
727         }
728 #endif
729         if (STAN_LoadDefaultNSS3TrustDomain() != PR_SUCCESS) {
730             goto loser;
731         }
732         if (nss_InitShutdownList() != SECSuccess) {
733             goto loser;
734         }
735         CERT_SetDefaultCertDB((CERTCertDBHandle *)
736                                   STAN_GetDefaultTrustDomain());
737         if ((!noModDB) && (!noCertDB) && (!noRootInit)) {
738             if (!SECMOD_HasRootCerts()) {
739                 const char *dbpath = configdir;
740                 /* handle supported database modifiers */
741                 if (strncmp(dbpath, "sql:", 4) == 0) {
742                     dbpath += 4;
743                 } else if (strncmp(dbpath, "dbm:", 4) == 0) {
744                     dbpath += 4;
745                 } else if (strncmp(dbpath, "extern:", 7) == 0) {
746                     dbpath += 7;
747                 } else if (strncmp(dbpath, "rdb:", 4) == 0) {
748                     /* if rdb: is specified, the configdir isn't really a
749                * path. Skip it */
750                     dbpath = NULL;
751                 }
752                 if (dbpath) {
753                     nss_FindExternalRoot(dbpath, secmodName);
754                 }
755             }
756         }
757         pk11sdr_Init();
758         cert_CreateSubjectKeyIDHashTable();
759 
760 #ifndef NSS_DISABLE_LIBPKIX
761         pkixError = PKIX_Initialize(PKIX_FALSE, PKIX_MAJOR_VERSION, PKIX_MINOR_VERSION,
762                                     PKIX_MINOR_VERSION, &actualMinorVersion, &plContext);
763 
764         if (pkixError != NULL) {
765             goto loser;
766         } else {
767             char *ev = PR_GetEnvSecure("NSS_ENABLE_PKIX_VERIFY");
768             if (ev && ev[0]) {
769                 CERT_SetUsePKIXForValidation(PR_TRUE);
770             }
771         }
772 #endif /* NSS_DISABLE_LIBPKIX */
773     }
774 
775     /*
776      * Now mark the appropriate init state. If initContextPtr was passed
777      * in, then return the new context pointer and add it to the
778      * nssInitContextList. Otherwise set the global nss_isInitted flag
779      */
780     PZ_Lock(nssInitLock);
781     if (!initContextPtr) {
782         nssIsInitted = PR_TRUE;
783     } else {
784         (*initContextPtr)->magic = NSS_INIT_MAGIC;
785         (*initContextPtr)->next = nssInitContextList;
786         nssInitContextList = (*initContextPtr);
787     }
788     nssIsInInit--;
789     /* now that we are inited, all waiters can move forward */
790     PZ_NotifyAllCondVar(nssInitCondition);
791     PZ_Unlock(nssInitLock);
792 
793     if (initContextPtr && configStrings) {
794         PR_smprintf_free(configStrings);
795     }
796     if (parent) {
797         SECMOD_DestroyModule(parent);
798     }
799 
800     return SECSuccess;
801 
802 loser:
803     if (initContextPtr && *initContextPtr) {
804         PORT_Free(*initContextPtr);
805         *initContextPtr = NULL;
806         if (configStrings) {
807             PR_smprintf_free(configStrings);
808         }
809     }
810     PZ_Lock(nssInitLock);
811     nssIsInInit--;
812     /* We failed to init, allow one to move forward */
813     PZ_NotifyCondVar(nssInitCondition);
814     PZ_Unlock(nssInitLock);
815     if (parent) {
816         SECMOD_DestroyModule(parent);
817     }
818     return SECFailure;
819 }
820 
821 SECStatus
NSS_Init(const char * configdir)822 NSS_Init(const char *configdir)
823 {
824     return nss_Init(configdir, "", "", SECMOD_DB, "", "", "", "", "", NULL,
825                     NULL, PR_TRUE, PR_FALSE, PR_FALSE, PR_FALSE, PR_FALSE,
826                     PR_TRUE, PR_FALSE, PR_FALSE, PR_FALSE);
827 }
828 
829 SECStatus
NSS_InitReadWrite(const char * configdir)830 NSS_InitReadWrite(const char *configdir)
831 {
832     return nss_Init(configdir, "", "", SECMOD_DB, "", "", "", "", "", NULL,
833                     NULL, PR_FALSE, PR_FALSE, PR_FALSE, PR_FALSE, PR_FALSE,
834                     PR_TRUE, PR_FALSE, PR_FALSE, PR_FALSE);
835 }
836 
837 /*
838  * OK there are now lots of options here, lets go through them all:
839  *
840  * configdir - base directory where all the cert, key, and module datbases live.
841  * certPrefix - prefix added to the beginning of the cert database example: "
842  *             "https-server1-"
843  * keyPrefix - prefix added to the beginning of the key database example: "
844  *             "https-server1-"
845  * secmodName - name of the security module database (usually "secmod.db").
846  * flags - change the open options of NSS_Initialize as follows:
847  *   NSS_INIT_READONLY - Open the databases read only.
848  *   NSS_INIT_NOCERTDB - Don't open the cert DB and key DB's, just
849  *             initialize the volatile certdb.
850  *   NSS_INIT_NOMODDB  - Don't open the security module DB, just
851  *             initialize the      PKCS #11 module.
852  *      NSS_INIT_FORCEOPEN - Continue to force initializations even if the
853  *             databases cannot be opened.
854  *      NSS_INIT_PK11THREADSAFE - only load PKCS#11 modules that are
855  *                      thread-safe, ie. that support locking - either OS
856  *                      locking or NSS-provided locks . If a PKCS#11
857  *                      module isn't thread-safe, don't serialize its
858  *                      calls; just don't load it instead. This is necessary
859  *                      if another piece of code is using the same PKCS#11
860  *                      modules that NSS is accessing without going through
861  *                      NSS, for example the Java SunPKCS11 provider.
862  *      NSS_INIT_PK11RELOAD - ignore the CKR_CRYPTOKI_ALREADY_INITIALIZED
863  *                      error when loading PKCS#11 modules. This is necessary
864  *                      if another piece of code is using the same PKCS#11
865  *                      modules that NSS is accessing without going through
866  *                      NSS, for example Java SunPKCS11 provider.
867  *      NSS_INIT_NOPK11FINALIZE - never call C_Finalize on any
868  *                      PKCS#11 module. This may be necessary in order to
869  *                      ensure continuous operation and proper shutdown
870  *                      sequence if another piece of code is using the same
871  *                      PKCS#11 modules that NSS is accessing without going
872  *                      through NSS, for example Java SunPKCS11 provider.
873  *                      The following limitation applies when this is set :
874  *                      SECMOD_WaitForAnyTokenEvent will not use
875  *                      C_WaitForSlotEvent, in order to prevent the need for
876  *                      C_Finalize. This call will be emulated instead.
877  *      NSS_INIT_RESERVED - Currently has no effect, but may be used in the
878  *                      future to trigger better cooperation between PKCS#11
879  *                      modules used by both NSS and the Java SunPKCS11
880  *                      provider. This should occur after a new flag is defined
881  *                      for C_Initialize by the PKCS#11 working group.
882  *      NSS_INIT_COOPERATE - Sets 4 recommended options for applications that
883  *                      use both NSS and the Java SunPKCS11 provider.
884  */
885 SECStatus
NSS_Initialize(const char * configdir,const char * certPrefix,const char * keyPrefix,const char * secmodName,PRUint32 flags)886 NSS_Initialize(const char *configdir, const char *certPrefix,
887                const char *keyPrefix, const char *secmodName, PRUint32 flags)
888 {
889     return nss_Init(configdir, certPrefix, keyPrefix, secmodName,
890                     "", "", "", "", "", NULL, NULL,
891                     ((flags & NSS_INIT_READONLY) == NSS_INIT_READONLY),
892                     ((flags & NSS_INIT_NOCERTDB) == NSS_INIT_NOCERTDB),
893                     ((flags & NSS_INIT_NOMODDB) == NSS_INIT_NOMODDB),
894                     ((flags & NSS_INIT_FORCEOPEN) == NSS_INIT_FORCEOPEN),
895                     ((flags & NSS_INIT_NOROOTINIT) == NSS_INIT_NOROOTINIT),
896                     ((flags & NSS_INIT_OPTIMIZESPACE) == NSS_INIT_OPTIMIZESPACE),
897                     ((flags & NSS_INIT_PK11THREADSAFE) == NSS_INIT_PK11THREADSAFE),
898                     ((flags & NSS_INIT_PK11RELOAD) == NSS_INIT_PK11RELOAD),
899                     ((flags & NSS_INIT_NOPK11FINALIZE) == NSS_INIT_NOPK11FINALIZE));
900 }
901 
902 NSSInitContext *
NSS_InitContext(const char * configdir,const char * certPrefix,const char * keyPrefix,const char * secmodName,NSSInitParameters * initParams,PRUint32 flags)903 NSS_InitContext(const char *configdir, const char *certPrefix,
904                 const char *keyPrefix, const char *secmodName,
905                 NSSInitParameters *initParams, PRUint32 flags)
906 {
907     SECStatus rv;
908     NSSInitContext *context;
909 
910     rv = nss_Init(configdir, certPrefix, keyPrefix, secmodName,
911                   "", "", "", "", "", &context, initParams,
912                   ((flags & NSS_INIT_READONLY) == NSS_INIT_READONLY),
913                   ((flags & NSS_INIT_NOCERTDB) == NSS_INIT_NOCERTDB),
914                   ((flags & NSS_INIT_NOMODDB) == NSS_INIT_NOMODDB),
915                   ((flags & NSS_INIT_FORCEOPEN) == NSS_INIT_FORCEOPEN), PR_TRUE,
916                   ((flags & NSS_INIT_OPTIMIZESPACE) == NSS_INIT_OPTIMIZESPACE),
917                   ((flags & NSS_INIT_PK11THREADSAFE) == NSS_INIT_PK11THREADSAFE),
918                   ((flags & NSS_INIT_PK11RELOAD) == NSS_INIT_PK11RELOAD),
919                   ((flags & NSS_INIT_NOPK11FINALIZE) == NSS_INIT_NOPK11FINALIZE));
920     return (rv == SECSuccess) ? context : NULL;
921 }
922 
923 SECStatus
NSS_InitWithMerge(const char * configdir,const char * certPrefix,const char * keyPrefix,const char * secmodName,const char * updateDir,const char * updCertPrefix,const char * updKeyPrefix,const char * updateID,const char * updateName,PRUint32 flags)924 NSS_InitWithMerge(const char *configdir, const char *certPrefix,
925                   const char *keyPrefix, const char *secmodName,
926                   const char *updateDir, const char *updCertPrefix,
927                   const char *updKeyPrefix, const char *updateID,
928                   const char *updateName, PRUint32 flags)
929 {
930     return nss_Init(configdir, certPrefix, keyPrefix, secmodName,
931                     updateDir, updCertPrefix, updKeyPrefix, updateID, updateName,
932                     NULL, NULL,
933                     ((flags & NSS_INIT_READONLY) == NSS_INIT_READONLY),
934                     ((flags & NSS_INIT_NOCERTDB) == NSS_INIT_NOCERTDB),
935                     ((flags & NSS_INIT_NOMODDB) == NSS_INIT_NOMODDB),
936                     ((flags & NSS_INIT_FORCEOPEN) == NSS_INIT_FORCEOPEN),
937                     ((flags & NSS_INIT_NOROOTINIT) == NSS_INIT_NOROOTINIT),
938                     ((flags & NSS_INIT_OPTIMIZESPACE) == NSS_INIT_OPTIMIZESPACE),
939                     ((flags & NSS_INIT_PK11THREADSAFE) == NSS_INIT_PK11THREADSAFE),
940                     ((flags & NSS_INIT_PK11RELOAD) == NSS_INIT_PK11RELOAD),
941                     ((flags & NSS_INIT_NOPK11FINALIZE) == NSS_INIT_NOPK11FINALIZE));
942 }
943 
944 /*
945  * initialize NSS without a creating cert db's, key db's, or secmod db's.
946  */
947 SECStatus
NSS_NoDB_Init(const char * configdir)948 NSS_NoDB_Init(const char *configdir)
949 {
950     return nss_Init("", "", "", "", "", "", "", "", "", NULL, NULL,
951                     PR_TRUE, PR_TRUE, PR_TRUE, PR_TRUE, PR_TRUE, PR_TRUE,
952                     PR_FALSE, PR_FALSE, PR_FALSE);
953 }
954 
955 #define NSS_SHUTDOWN_STEP 10
956 
957 struct NSSShutdownFuncPair {
958     NSS_ShutdownFunc func;
959     void *appData;
960 };
961 
962 static struct NSSShutdownListStr {
963     PZLock *lock;
964     int allocatedFuncs;
965     int peakFuncs;
966     struct NSSShutdownFuncPair *funcs;
967 } nssShutdownList = { 0 };
968 
969 /*
970  * find and existing shutdown function
971  */
972 static int
nss_GetShutdownEntry(NSS_ShutdownFunc sFunc,void * appData)973 nss_GetShutdownEntry(NSS_ShutdownFunc sFunc, void *appData)
974 {
975     int count, i;
976     count = nssShutdownList.peakFuncs;
977 
978     for (i = 0; i < count; i++) {
979         if ((nssShutdownList.funcs[i].func == sFunc) &&
980             (nssShutdownList.funcs[i].appData == appData)) {
981             return i;
982         }
983     }
984     return -1;
985 }
986 
987 /*
988  * register a callback to be called when NSS shuts down
989  */
990 SECStatus
NSS_RegisterShutdown(NSS_ShutdownFunc sFunc,void * appData)991 NSS_RegisterShutdown(NSS_ShutdownFunc sFunc, void *appData)
992 {
993     int i;
994 
995     /* make sure our lock and condition variable are initialized one and only
996      * one time */
997     if (PR_CallOnce(&nssInitOnce, nss_doLockInit) != PR_SUCCESS) {
998         return SECFailure;
999     }
1000 
1001     PZ_Lock(nssInitLock);
1002     if (!NSS_IsInitialized()) {
1003         PZ_Unlock(nssInitLock);
1004         PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
1005         return SECFailure;
1006     }
1007     PZ_Unlock(nssInitLock);
1008     if (sFunc == NULL) {
1009         PORT_SetError(SEC_ERROR_INVALID_ARGS);
1010         return SECFailure;
1011     }
1012 
1013     PORT_Assert(nssShutdownList.lock);
1014     PZ_Lock(nssShutdownList.lock);
1015 
1016     /* make sure we don't have a duplicate */
1017     i = nss_GetShutdownEntry(sFunc, appData);
1018     if (i >= 0) {
1019         PZ_Unlock(nssShutdownList.lock);
1020         PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
1021         return SECFailure;
1022     }
1023     /* find an empty slot */
1024     i = nss_GetShutdownEntry(NULL, NULL);
1025     if (i >= 0) {
1026         nssShutdownList.funcs[i].func = sFunc;
1027         nssShutdownList.funcs[i].appData = appData;
1028         PZ_Unlock(nssShutdownList.lock);
1029         return SECSuccess;
1030     }
1031     if (nssShutdownList.allocatedFuncs == nssShutdownList.peakFuncs) {
1032         struct NSSShutdownFuncPair *funcs =
1033             (struct NSSShutdownFuncPair *)PORT_Realloc(nssShutdownList.funcs,
1034                                                        (nssShutdownList.allocatedFuncs + NSS_SHUTDOWN_STEP) * sizeof(struct NSSShutdownFuncPair));
1035         if (!funcs) {
1036             PZ_Unlock(nssShutdownList.lock);
1037             return SECFailure;
1038         }
1039         nssShutdownList.funcs = funcs;
1040         nssShutdownList.allocatedFuncs += NSS_SHUTDOWN_STEP;
1041     }
1042     nssShutdownList.funcs[nssShutdownList.peakFuncs].func = sFunc;
1043     nssShutdownList.funcs[nssShutdownList.peakFuncs].appData = appData;
1044     nssShutdownList.peakFuncs++;
1045     PZ_Unlock(nssShutdownList.lock);
1046     return SECSuccess;
1047 }
1048 
1049 /*
1050  * unregister a callback so it won't get called on shutdown.
1051  */
1052 SECStatus
NSS_UnregisterShutdown(NSS_ShutdownFunc sFunc,void * appData)1053 NSS_UnregisterShutdown(NSS_ShutdownFunc sFunc, void *appData)
1054 {
1055     int i;
1056 
1057     /* make sure our lock and condition variable are initialized one and only
1058      * one time */
1059     if (PR_CallOnce(&nssInitOnce, nss_doLockInit) != PR_SUCCESS) {
1060         return SECFailure;
1061     }
1062     PZ_Lock(nssInitLock);
1063     if (!NSS_IsInitialized()) {
1064         PZ_Unlock(nssInitLock);
1065         PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
1066         return SECFailure;
1067     }
1068     PZ_Unlock(nssInitLock);
1069 
1070     PORT_Assert(nssShutdownList.lock);
1071     PZ_Lock(nssShutdownList.lock);
1072     i = nss_GetShutdownEntry(sFunc, appData);
1073     if (i >= 0) {
1074         nssShutdownList.funcs[i].func = NULL;
1075         nssShutdownList.funcs[i].appData = NULL;
1076     }
1077     PZ_Unlock(nssShutdownList.lock);
1078 
1079     if (i < 0) {
1080         PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
1081         return SECFailure;
1082     }
1083     return SECSuccess;
1084 }
1085 
1086 /*
1087  * bring up and shutdown the shutdown list
1088  */
1089 static SECStatus
nss_InitShutdownList(void)1090 nss_InitShutdownList(void)
1091 {
1092     if (nssShutdownList.lock != NULL) {
1093         return SECSuccess;
1094     }
1095     nssShutdownList.lock = PZ_NewLock(nssILockOther);
1096     if (nssShutdownList.lock == NULL) {
1097         return SECFailure;
1098     }
1099     nssShutdownList.funcs = PORT_ZNewArray(struct NSSShutdownFuncPair,
1100                                            NSS_SHUTDOWN_STEP);
1101     if (nssShutdownList.funcs == NULL) {
1102         PZ_DestroyLock(nssShutdownList.lock);
1103         nssShutdownList.lock = NULL;
1104         return SECFailure;
1105     }
1106     nssShutdownList.allocatedFuncs = NSS_SHUTDOWN_STEP;
1107     nssShutdownList.peakFuncs = 0;
1108 
1109     return SECSuccess;
1110 }
1111 
1112 static SECStatus
nss_ShutdownShutdownList(void)1113 nss_ShutdownShutdownList(void)
1114 {
1115     SECStatus rv = SECSuccess;
1116     int i;
1117 
1118     /* call all the registerd functions first */
1119     for (i = 0; i < nssShutdownList.peakFuncs; i++) {
1120         struct NSSShutdownFuncPair *funcPair = &nssShutdownList.funcs[i];
1121         if (funcPair->func) {
1122             if ((*funcPair->func)(funcPair->appData, NULL) != SECSuccess) {
1123                 rv = SECFailure;
1124             }
1125         }
1126     }
1127 
1128     nssShutdownList.peakFuncs = 0;
1129     nssShutdownList.allocatedFuncs = 0;
1130     PORT_Free(nssShutdownList.funcs);
1131     nssShutdownList.funcs = NULL;
1132     if (nssShutdownList.lock) {
1133         PZ_DestroyLock(nssShutdownList.lock);
1134     }
1135     nssShutdownList.lock = NULL;
1136     return rv;
1137 }
1138 
1139 extern const NSSError NSS_ERROR_BUSY;
1140 
1141 SECStatus
nss_Shutdown(void)1142 nss_Shutdown(void)
1143 {
1144     SECStatus shutdownRV = SECSuccess;
1145     SECStatus rv;
1146     PRStatus status;
1147     NSSInitContext *temp;
1148 
1149     rv = nss_ShutdownShutdownList();
1150     if (rv != SECSuccess) {
1151         shutdownRV = SECFailure;
1152     }
1153     cert_DestroyLocks();
1154     ShutdownCRLCache();
1155     OCSP_ShutdownGlobal();
1156 #ifndef NSS_DISABLE_LIBPKIX
1157     PKIX_Shutdown(plContext);
1158 #endif /* NSS_DISABLE_LIBPKIX */
1159     SECOID_Shutdown();
1160     status = STAN_Shutdown();
1161     cert_DestroySubjectKeyIDHashTable();
1162     pk11_SetInternalKeySlot(NULL);
1163     rv = SECMOD_Shutdown();
1164     if (rv != SECSuccess) {
1165         shutdownRV = SECFailure;
1166     }
1167     pk11sdr_Shutdown();
1168     nssArena_Shutdown();
1169     if (status == PR_FAILURE) {
1170         if (NSS_GetError() == NSS_ERROR_BUSY) {
1171             PORT_SetError(SEC_ERROR_BUSY);
1172         }
1173         shutdownRV = SECFailure;
1174     }
1175     /*
1176      * A thread's error stack is automatically destroyed when the thread
1177      * terminates, except for the primordial thread, whose error stack is
1178      * destroyed by PR_Cleanup.  Since NSS is usually shut down by the
1179      * primordial thread and many NSS-based apps don't call PR_Cleanup,
1180      * we destroy the calling thread's error stack here. This must be
1181      * done after any NSS_GetError call, otherwise NSS_GetError will
1182      * create the error stack again.
1183      */
1184     nss_DestroyErrorStack();
1185     nssIsInitted = PR_FALSE;
1186     temp = nssInitContextList;
1187     nssInitContextList = NULL;
1188     /* free the old list. This is necessary when we are called from
1189      * NSS_Shutdown(). */
1190     while (temp) {
1191         NSSInitContext *next = temp->next;
1192         temp->magic = 0;
1193         PORT_Free(temp);
1194         temp = next;
1195     }
1196     return shutdownRV;
1197 }
1198 
1199 SECStatus
NSS_Shutdown(void)1200 NSS_Shutdown(void)
1201 {
1202     SECStatus rv;
1203     /* make sure our lock and condition variable are initialized one and only
1204      * one time */
1205     if (PR_CallOnce(&nssInitOnce, nss_doLockInit) != PR_SUCCESS) {
1206         return SECFailure;
1207     }
1208     PZ_Lock(nssInitLock);
1209 
1210     if (!nssIsInitted) {
1211         PZ_Unlock(nssInitLock);
1212         PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
1213         return SECFailure;
1214     }
1215 
1216     /* If one or more threads are in the middle of init, wait for them
1217      * to complete */
1218     while (nssIsInInit) {
1219         PZ_WaitCondVar(nssInitCondition, PR_INTERVAL_NO_TIMEOUT);
1220     }
1221     rv = nss_Shutdown();
1222     PZ_Unlock(nssInitLock);
1223     return rv;
1224 }
1225 
1226 /*
1227  * remove the context from a list. return true if found, false if not
1228  */
1229 PRBool
nss_RemoveList(NSSInitContext * context)1230 nss_RemoveList(NSSInitContext *context)
1231 {
1232     NSSInitContext *this = nssInitContextList;
1233     NSSInitContext **last = &nssInitContextList;
1234 
1235     while (this) {
1236         if (this == context) {
1237             *last = this->next;
1238             this->magic = 0;
1239             PORT_Free(this);
1240             return PR_TRUE;
1241         }
1242         last = &this->next;
1243         this = this->next;
1244     }
1245     return PR_FALSE;
1246 }
1247 
1248 /*
1249  * This form of shutdown is safe in the case where we may have multiple
1250  * entities using NSS in a single process. Each entity calls shutdown with
1251  * it's own context. The application (which doesn't get a context), calls
1252  * shutdown with NULL. Once all users have 'checked in' NSS will shutdown.
1253  * This is different than NSS_Shutdown, where calling it will shutdown NSS
1254  * irreguardless of who else may have NSS open.
1255  */
1256 SECStatus
NSS_ShutdownContext(NSSInitContext * context)1257 NSS_ShutdownContext(NSSInitContext *context)
1258 {
1259     SECStatus rv = SECSuccess;
1260 
1261     /* make sure our lock and condition variable are initialized one and only
1262      * one time */
1263     if (PR_CallOnce(&nssInitOnce, nss_doLockInit) != PR_SUCCESS) {
1264         return SECFailure;
1265     }
1266     PZ_Lock(nssInitLock);
1267     /* If one or more threads are in the middle of init, wait for them
1268      * to complete */
1269     while (nssIsInInit) {
1270         PZ_WaitCondVar(nssInitCondition, PR_INTERVAL_NO_TIMEOUT);
1271     }
1272 
1273     /* OK, we are the only thread now either initializing or shutting down */
1274 
1275     if (!context) {
1276         if (!nssIsInitted) {
1277             PZ_Unlock(nssInitLock);
1278             PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
1279             return SECFailure;
1280         }
1281         nssIsInitted = 0;
1282     } else if (!nss_RemoveList(context)) {
1283         PZ_Unlock(nssInitLock);
1284         /* context was already freed or wasn't valid */
1285         PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
1286         return SECFailure;
1287     }
1288     if ((nssIsInitted == 0) && (nssInitContextList == NULL)) {
1289         rv = nss_Shutdown();
1290     }
1291 
1292     /* NOTE: we don't try to free the nssInitLocks to prevent races against
1293      * the locks. There may be a thread, right now, waiting in NSS_Init for us
1294      * to free the lock below. If we delete the locks, bad things would happen
1295      * to that thread */
1296     PZ_Unlock(nssInitLock);
1297 
1298     return rv;
1299 }
1300 
1301 PRBool
NSS_IsInitialized(void)1302 NSS_IsInitialized(void)
1303 {
1304     return (nssIsInitted) || (nssInitContextList != NULL);
1305 }
1306 
1307 extern const char __nss_base_version[];
1308 
1309 PRBool
NSS_VersionCheck(const char * importedVersion)1310 NSS_VersionCheck(const char *importedVersion)
1311 {
1312     /*
1313      * This is the secret handshake algorithm.
1314      *
1315      * This release has a simple version compatibility
1316      * check algorithm.  This release is not backward
1317      * compatible with previous major releases.  It is
1318      * not compatible with future major, minor, or
1319      * patch releases or builds.
1320      */
1321     int vmajor = 0, vminor = 0, vpatch = 0, vbuild = 0;
1322     const char *ptr = importedVersion;
1323 #define NSS_VERSION_VARIABLE __nss_base_version
1324 #include "verref.h"
1325 
1326     while (isdigit(*ptr)) {
1327         vmajor = 10 * vmajor + *ptr - '0';
1328         ptr++;
1329     }
1330     if (*ptr == '.') {
1331         ptr++;
1332         while (isdigit(*ptr)) {
1333             vminor = 10 * vminor + *ptr - '0';
1334             ptr++;
1335         }
1336         if (*ptr == '.') {
1337             ptr++;
1338             while (isdigit(*ptr)) {
1339                 vpatch = 10 * vpatch + *ptr - '0';
1340                 ptr++;
1341             }
1342             if (*ptr == '.') {
1343                 ptr++;
1344                 while (isdigit(*ptr)) {
1345                     vbuild = 10 * vbuild + *ptr - '0';
1346                     ptr++;
1347                 }
1348             }
1349         }
1350     }
1351 
1352     if (vmajor != NSS_VMAJOR) {
1353         return PR_FALSE;
1354     }
1355     if (vmajor == NSS_VMAJOR && vminor > NSS_VMINOR) {
1356         return PR_FALSE;
1357     }
1358     if (vmajor == NSS_VMAJOR && vminor == NSS_VMINOR && vpatch > NSS_VPATCH) {
1359         return PR_FALSE;
1360     }
1361     if (vmajor == NSS_VMAJOR && vminor == NSS_VMINOR &&
1362         vpatch == NSS_VPATCH && vbuild > NSS_VBUILD) {
1363         return PR_FALSE;
1364     }
1365     return PR_TRUE;
1366 }
1367 
1368 const char *
NSS_GetVersion(void)1369 NSS_GetVersion(void)
1370 {
1371     return NSS_VERSION;
1372 }
1373