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