PageRenderTime 63ms CodeModel.GetById 3ms app.highlight 50ms RepoModel.GetById 1ms app.codeStats 0ms

/security/nss/lib/nss/nssinit.c

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