security/nss/lib/nss/nssinit.c

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial