Source code

Revision control

Copy as Markdown

Other Tools

/*
* NSS utility functions
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <ctype.h>
#include <string.h>
#include "seccomon.h"
#include "prinit.h"
#include "prprf.h"
#include "prmem.h"
#include "cert.h"
#include "keyhi.h"
#include "secmod.h"
#include "secoid.h"
#include "nss.h"
#include "pk11func.h"
#include "secerr.h"
#include "nssbase.h"
#include "nssutil.h"
#ifndef NSS_DISABLE_LIBPKIX
#include "pkixt.h"
#include "pkix.h"
#include "pkix_tools.h"
#endif /* NSS_DISABLE_LIBPKIX */
#include "pki3hack.h"
#include "certi.h"
#include "secmodi.h"
#include "ocspti.h"
#include "ocspi.h"
#include "utilpars.h"
/*
* On Windows nss3.dll needs to export the symbol 'mktemp' to be
* fully backward compatible with the nss3.dll in NSS 3.2.x and
* 3.3.x. This symbol was unintentionally exported and its
* definition (in DBM) was moved from nss3.dll to softokn3.dll
* in NSS 3.4. See bug 142575.
*/
#ifdef WIN32_NSS3_DLL_COMPAT
#include <io.h>
/* exported as 'mktemp' */
char *
nss_mktemp(char *path)
{
return _mktemp(path);
}
#endif
#define NSS_MAX_FLAG_SIZE sizeof("readOnly") + sizeof("noCertDB") + \
sizeof("noModDB") + sizeof("forceOpen") + sizeof("passwordRequired") + \
sizeof("optimizeSpace") + sizeof("printPolicyFeedback")
#define NSS_DEFAULT_MOD_NAME "NSS Internal Module"
static char *
nss_makeFlags(PRBool readOnly, PRBool noCertDB,
PRBool noModDB, PRBool forceOpen,
PRBool passwordRequired, PRBool optimizeSpace)
{
char *flags = (char *)PORT_Alloc(NSS_MAX_FLAG_SIZE);
PRBool first = PR_TRUE;
PORT_Memset(flags, 0, NSS_MAX_FLAG_SIZE);
if (readOnly) {
PORT_Strcat(flags, "readOnly");
first = PR_FALSE;
}
if (noCertDB) {
if (!first)
PORT_Strcat(flags, ",");
PORT_Strcat(flags, "noCertDB");
first = PR_FALSE;
}
if (noModDB) {
if (!first)
PORT_Strcat(flags, ",");
PORT_Strcat(flags, "noModDB");
first = PR_FALSE;
}
if (forceOpen) {
if (!first)
PORT_Strcat(flags, ",");
PORT_Strcat(flags, "forceOpen");
first = PR_FALSE;
}
if (passwordRequired) {
if (!first)
PORT_Strcat(flags, ",");
PORT_Strcat(flags, "passwordRequired");
first = PR_FALSE;
}
if (optimizeSpace) {
if (!first)
PORT_Strcat(flags, ",");
PORT_Strcat(flags, "optimizeSpace");
}
return flags;
}
/*
* build config string from individual internationalized strings
*/
char *
nss_MkConfigString(const char *man, const char *libdesc, const char *tokdesc,
const char *ptokdesc, const char *slotdesc, const char *pslotdesc,
const char *fslotdesc, const char *fpslotdesc, int minPwd)
{
char *strings = NULL;
char *newStrings;
/* make sure the internationalization was done correctly... */
strings = PR_smprintf("");
if (strings == NULL)
return NULL;
if (man) {
newStrings = PR_smprintf("%s manufacturerID='%s'", strings, man);
PR_smprintf_free(strings);
strings = newStrings;
}
if (strings == NULL)
return NULL;
if (libdesc) {
newStrings = PR_smprintf("%s libraryDescription='%s'", strings, libdesc);
PR_smprintf_free(strings);
strings = newStrings;
}
if (strings == NULL)
return NULL;
if (tokdesc) {
newStrings = PR_smprintf("%s cryptoTokenDescription='%s'", strings,
tokdesc);
PR_smprintf_free(strings);
strings = newStrings;
}
if (strings == NULL)
return NULL;
if (ptokdesc) {
newStrings = PR_smprintf("%s dbTokenDescription='%s'", strings, ptokdesc);
PR_smprintf_free(strings);
strings = newStrings;
}
if (strings == NULL)
return NULL;
if (slotdesc) {
newStrings = PR_smprintf("%s cryptoSlotDescription='%s'", strings,
slotdesc);
PR_smprintf_free(strings);
strings = newStrings;
}
if (strings == NULL)
return NULL;
if (pslotdesc) {
newStrings = PR_smprintf("%s dbSlotDescription='%s'", strings, pslotdesc);
PR_smprintf_free(strings);
strings = newStrings;
}
if (strings == NULL)
return NULL;
if (fslotdesc) {
newStrings = PR_smprintf("%s FIPSSlotDescription='%s'",
strings, fslotdesc);
PR_smprintf_free(strings);
strings = newStrings;
}
if (strings == NULL)
return NULL;
if (fpslotdesc) {
newStrings = PR_smprintf("%s FIPSTokenDescription='%s'",
strings, fpslotdesc);
PR_smprintf_free(strings);
strings = newStrings;
}
if (strings == NULL)
return NULL;
newStrings = PR_smprintf("%s minPS=%d", strings, minPwd);
PR_smprintf_free(strings);
strings = newStrings;
return (strings);
}
/*
* statics to remember the PK11_ConfigurePKCS11()
* info.
*/
static char *pk11_config_strings = NULL;
static char *pk11_config_name = NULL;
static PRBool pk11_password_required = PR_FALSE;
/*
* this is a legacy configuration function which used to be part of
* the PKCS #11 internal token.
*/
void
PK11_ConfigurePKCS11(const char *man, const char *libdesc, const char *tokdesc,
const char *ptokdesc, const char *slotdesc, const char *pslotdesc,
const char *fslotdesc, const char *fpslotdesc, int minPwd,
int pwRequired)
{
char *strings;
strings = nss_MkConfigString(man, libdesc, tokdesc, ptokdesc, slotdesc,
pslotdesc, fslotdesc, fpslotdesc, minPwd);
if (strings == NULL) {
return;
}
if (libdesc) {
if (pk11_config_name != NULL) {
PORT_Free(pk11_config_name);
}
pk11_config_name = PORT_Strdup(libdesc);
}
if (pk11_config_strings != NULL) {
PR_smprintf_free(pk11_config_strings);
}
pk11_config_strings = strings;
pk11_password_required = pwRequired;
return;
}
void
PK11_UnconfigurePKCS11(void)
{
if (pk11_config_strings != NULL) {
PR_smprintf_free(pk11_config_strings);
pk11_config_strings = NULL;
}
if (pk11_config_name) {
PORT_Free(pk11_config_name);
pk11_config_name = NULL;
}
}
/*
* The following code is an attempt to automagically find the external root
* module.
* Note: Keep the #if-defined chunks in order. HPUX must select before UNIX.
*/
static const char *dllname =
#if defined(XP_WIN32) || defined(XP_OS2)
"nssckbi.dll";
#elif defined(HPUX) && !defined(__ia64) /* HP-UX PA-RISC */
"libnssckbi.sl";
#elif defined(DARWIN)
"libnssckbi.dylib";
#elif defined(XP_UNIX)
"libnssckbi.so";
#else
#error "Uh! Oh! I don't know about this platform."
#endif
/* Should we have platform ifdefs here??? */
#define FILE_SEP '/'
static void
nss_FindExternalRootPaths(const char *dbpath,
const char *secmodprefix,
char **retoldpath, char **retnewpath)
{
char *path, *oldpath = NULL, *lastsep;
int len, path_len, secmod_len, dll_len;
path_len = PORT_Strlen(dbpath);
secmod_len = secmodprefix ? PORT_Strlen(secmodprefix) : 0;
dll_len = PORT_Strlen(dllname);
len = path_len + secmod_len + dll_len + 2; /* FILE_SEP + NULL */
path = PORT_Alloc(len);
if (path == NULL)
return;
/* back up to the top of the directory */
PORT_Memcpy(path, dbpath, path_len);
if (path[path_len - 1] != FILE_SEP) {
path[path_len++] = FILE_SEP;
}
PORT_Strcpy(&path[path_len], dllname);
if (secmod_len > 0) {
lastsep = PORT_Strrchr(secmodprefix, FILE_SEP);
if (lastsep) {
int secmoddir_len = lastsep - secmodprefix + 1; /* FILE_SEP */
oldpath = PORT_Alloc(len);
if (oldpath == NULL) {
PORT_Free(path);
return;
}
PORT_Memcpy(oldpath, path, path_len);
PORT_Memcpy(&oldpath[path_len], secmodprefix, secmoddir_len);
PORT_Strcpy(&oldpath[path_len + secmoddir_len], dllname);
}
}
*retoldpath = oldpath;
*retnewpath = path;
return;
}
static void
nss_FreeExternalRootPaths(char *oldpath, char *path)
{
if (path) {
PORT_Free(path);
}
if (oldpath) {
PORT_Free(oldpath);
}
}
static void
nss_FindExternalRoot(const char *dbpath, const char *secmodprefix)
{
char *path = NULL;
char *oldpath = NULL;
PRBool hasrootcerts = PR_FALSE;
/*
* 'oldpath' is the external root path in NSS 3.3.x or older.
* For backward compatibility we try to load the root certs
* module with the old path first.
*/
nss_FindExternalRootPaths(dbpath, secmodprefix, &oldpath, &path);
if (oldpath) {
(void)SECMOD_AddNewModule("Root Certs", oldpath, 0, 0);
hasrootcerts = SECMOD_HasRootCerts();
}
if (path && !hasrootcerts) {
(void)SECMOD_AddNewModule("Root Certs", path, 0, 0);
}
nss_FreeExternalRootPaths(oldpath, path);
return;
}
/*
* see nss_Init for definitions of the various options.
*
* this function builds a moduleSpec string from the options and previously
* set statics (from PKCS11_Configure, for instance), and uses it to kick off
* the loading of the various PKCS #11 modules.
*/
static SECMODModule *
nss_InitModules(const char *configdir, const char *certPrefix,
const char *keyPrefix, const char *secmodName,
const char *updateDir, const char *updCertPrefix,
const char *updKeyPrefix, const char *updateID,
const char *updateName, char *configName, char *configStrings,
PRBool pwRequired, PRBool readOnly, PRBool noCertDB,
PRBool noModDB, PRBool forceOpen, PRBool optimizeSpace,
PRBool isContextInit)
{
SECMODModule *module = NULL;
char *moduleSpec = NULL;
char *flags = NULL;
char *lconfigdir = NULL;
char *lcertPrefix = NULL;
char *lkeyPrefix = NULL;
char *lsecmodName = NULL;
char *lupdateDir = NULL;
char *lupdCertPrefix = NULL;
char *lupdKeyPrefix = NULL;
char *lupdateID = NULL;
char *lupdateName = NULL;
if (NSS_InitializePRErrorTable() != SECSuccess) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
return NULL;
}
flags = nss_makeFlags(readOnly, noCertDB, noModDB, forceOpen,
pwRequired, optimizeSpace);
if (flags == NULL)
return NULL;
/*
* configdir is double nested, and Windows uses the same character
* for file seps as we use for escapes! (sigh).
*/
lconfigdir = NSSUTIL_DoubleEscape(configdir, '\'', '\"');
if (lconfigdir == NULL) {
goto loser;
}
lcertPrefix = NSSUTIL_DoubleEscape(certPrefix, '\'', '\"');
if (lcertPrefix == NULL) {
goto loser;
}
lkeyPrefix = NSSUTIL_DoubleEscape(keyPrefix, '\'', '\"');
if (lkeyPrefix == NULL) {
goto loser;
}
lsecmodName = NSSUTIL_DoubleEscape(secmodName, '\'', '\"');
if (lsecmodName == NULL) {
goto loser;
}
lupdateDir = NSSUTIL_DoubleEscape(updateDir, '\'', '\"');
if (lupdateDir == NULL) {
goto loser;
}
lupdCertPrefix = NSSUTIL_DoubleEscape(updCertPrefix, '\'', '\"');
if (lupdCertPrefix == NULL) {
goto loser;
}
lupdKeyPrefix = NSSUTIL_DoubleEscape(updKeyPrefix, '\'', '\"');
if (lupdKeyPrefix == NULL) {
goto loser;
}
lupdateID = NSSUTIL_DoubleEscape(updateID, '\'', '\"');
if (lupdateID == NULL) {
goto loser;
}
lupdateName = NSSUTIL_DoubleEscape(updateName, '\'', '\"');
if (lupdateName == NULL) {
goto loser;
}
moduleSpec = PR_smprintf(
"name=\"%s\" parameters=\"configdir='%s' certPrefix='%s' keyPrefix='%s' "
"secmod='%s' flags=%s updatedir='%s' updateCertPrefix='%s' "
"updateKeyPrefix='%s' updateid='%s' updateTokenDescription='%s' %s\" "
"NSS=\"flags=internal,moduleDB,moduleDBOnly,critical%s\"",
configName ? configName : NSS_DEFAULT_MOD_NAME,
lconfigdir, lcertPrefix, lkeyPrefix, lsecmodName, flags,
lupdateDir, lupdCertPrefix, lupdKeyPrefix, lupdateID,
lupdateName, configStrings ? configStrings : "",
isContextInit ? "" : ",defaultModDB,internalKeySlot");
loser:
PORT_Free(flags);
if (lconfigdir)
PORT_Free(lconfigdir);
if (lcertPrefix)
PORT_Free(lcertPrefix);
if (lkeyPrefix)
PORT_Free(lkeyPrefix);
if (lsecmodName)
PORT_Free(lsecmodName);
if (lupdateDir)
PORT_Free(lupdateDir);
if (lupdCertPrefix)
PORT_Free(lupdCertPrefix);
if (lupdKeyPrefix)
PORT_Free(lupdKeyPrefix);
if (lupdateID)
PORT_Free(lupdateID);
if (lupdateName)
PORT_Free(lupdateName);
if (moduleSpec) {
module = SECMOD_LoadModule(moduleSpec, NULL, PR_TRUE);
PR_smprintf_free(moduleSpec);
if (module && !module->loaded) {
SECMOD_DestroyModule(module);
return NULL;
}
}
return module;
}
/*
* OK there are now lots of options here, lets go through them all:
*
* configdir - base directory where all the cert, key, and module datbases live.
* certPrefix - prefix added to the beginning of the cert database example: "
* "https-server1-"
* keyPrefix - prefix added to the beginning of the key database example: "
* "https-server1-"
* secmodName - name of the security module database (usually "secmod.db").
* updateDir - used in initMerge, old directory to update from.
* updateID - used in initMerge, unique ID to represent the updated directory.
* updateName - used in initMerge, token name when updating.
* initContextPtr - used in initContext, pointer to return a unique context
* value.
* readOnly - Boolean: true if the databases are to be opened read only.
* nocertdb - Don't open the cert DB and key DB's, just initialize the
* Volatile certdb.
* nomoddb - Don't open the security module DB, just initialize the
* PKCS #11 module.
* forceOpen - Continue to force initializations even if the databases cannot
* be opened.
* noRootInit - don't try to automatically load the root cert store if one is
* not found.
* optimizeSpace - tell NSS to use fewer hash table buckets.
*
* The next three options are used in an attempt to share PKCS #11 modules
* with other loaded, running libraries. PKCS #11 was not designed with this
* sort of sharing in mind, so use of these options may lead to questionable
* results. These options are may be incompatible with NSS_LoadContext() calls.
*
* noSingleThreadedModules - don't load modules that are not thread safe (many
* smart card tokens will not work).
* allowAlreadyInitializedModules - if a module has already been loaded and
* initialize try to use it.
* don'tFinalizeModules - dont shutdown modules we may have loaded.
*/
static PRBool nssIsInitted = PR_FALSE;
static NSSInitContext *nssInitContextList = NULL;
#ifndef NSS_DISABLE_LIBPKIX
static void *plContext = NULL;
#endif /* NSS_DISABLE_LIBPKIX */
struct NSSInitContextStr {
NSSInitContext *next;
PRUint32 magic;
};
#define NSS_INIT_MAGIC 0x1413A91C
static SECStatus nss_InitShutdownList(void);
/* All initialized to zero in BSS */
static PRCallOnceType nssInitOnce;
static PZLock *nssInitLock;
static PZCondVar *nssInitCondition;
static int nssIsInInit;
static PRStatus
nss_doLockInit(void)
{
nssInitLock = PZ_NewLock(nssILockOther);
if (nssInitLock == NULL) {
return PR_FAILURE;
}
nssInitCondition = PZ_NewCondVar(nssInitLock);
if (nssInitCondition == NULL) {
return PR_FAILURE;
}
return PR_SUCCESS;
}
static SECStatus
nss_Init(const char *configdir, const char *certPrefix, const char *keyPrefix,
const char *secmodName, const char *updateDir,
const char *updCertPrefix, const char *updKeyPrefix,
const char *updateID, const char *updateName,
NSSInitContext **initContextPtr,
NSSInitParameters *initParams,
PRBool readOnly, PRBool noCertDB,
PRBool noModDB, PRBool forceOpen, PRBool noRootInit,
PRBool optimizeSpace, PRBool noSingleThreadedModules,
PRBool allowAlreadyInitializedModules,
PRBool dontFinalizeModules)
{
SECMODModule *parent = NULL;
#ifndef NSS_DISABLE_LIBPKIX
PKIX_UInt32 actualMinorVersion = 0;
PKIX_Error *pkixError = NULL;
#endif /* NSS_DISABLE_LIBPKIX */
PRBool isReallyInitted;
char *configStrings = NULL;
char *configName = NULL;
PRBool passwordRequired = PR_FALSE;
#ifdef POLICY_FILE
char *ignoreVar;
#endif
/* if we are trying to init with a traditional NSS_Init call, maintain
* the traditional idempotent behavior. */
if (!initContextPtr && nssIsInitted) {
return SECSuccess;
}
/* make sure our lock and condition variable are initialized one and only
* one time */
if (PR_CallOnce(&nssInitOnce, nss_doLockInit) != PR_SUCCESS) {
return SECFailure;
}
/*
* if we haven't done basic initialization, single thread the
* initializations.
*/
PZ_Lock(nssInitLock);
isReallyInitted = NSS_IsInitialized();
if (!isReallyInitted) {
while (!isReallyInitted && nssIsInInit) {
PZ_WaitCondVar(nssInitCondition, PR_INTERVAL_NO_TIMEOUT);
isReallyInitted = NSS_IsInitialized();
}
/* once we've completed basic initialization, we can allow more than
* one process initialize NSS at a time. */
}
nssIsInInit++;
PZ_Unlock(nssInitLock);
/* this tells us whether or not some library has already initialized us.
* if so, we don't want to double call some of the basic initialization
* functions */
if (!isReallyInitted) {
#ifdef DEBUG
CERTCertificate dummyCert;
/* New option bits must not change the size of CERTCertificate. */
PORT_Assert(sizeof(dummyCert.options) == sizeof(void *));
#endif
if (SECSuccess != cert_InitLocks()) {
goto loser;
}
if (SECSuccess != InitCRLCache()) {
goto loser;
}
if (SECSuccess != OCSP_InitGlobal()) {
goto loser;
}
}
if (noSingleThreadedModules || allowAlreadyInitializedModules ||
dontFinalizeModules) {
pk11_setGlobalOptions(noSingleThreadedModules,
allowAlreadyInitializedModules,
dontFinalizeModules);
}
if (initContextPtr) {
*initContextPtr = PORT_ZNew(NSSInitContext);
if (*initContextPtr == NULL) {
goto loser;
}
/*
* For traditional NSS_Init, we used the PK11_Configure() call to set
* globals. with InitContext, we pass those strings in as parameters.
*
* This allows old NSS_Init calls to work as before, while at the same
* time new calls and old calls will not interfere with each other.
*/
if (initParams) {
if (initParams->length < sizeof(NSSInitParameters)) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
goto loser;
}
configStrings = nss_MkConfigString(initParams->manufactureID,
initParams->libraryDescription,
initParams->cryptoTokenDescription,
initParams->dbTokenDescription,
initParams->cryptoSlotDescription,
initParams->dbSlotDescription,
initParams->FIPSSlotDescription,
initParams->FIPSTokenDescription,
initParams->minPWLen);
if (configStrings == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
configName = initParams->libraryDescription;
passwordRequired = initParams->passwordRequired;
}
/* If we're NSS_ContextInit, we're probably a library. It could be
* possible that the application initialized NSS then forked(). The
* library would have no knowledge of that. If we call
* SECMOD_RestartModules() here, we will be able to continue on with
* NSS as normal. SECMOD_RestartModules() does have the side affect
* of losing all our PKCS #11 objects in the new process, but only if
* the module needs to be reinited. If it needs to be reinit those
* objects are inaccessible anyway, it's always save to call
* SECMOD_RestartModules(PR_FALSE).
*/
/* NOTE: We could call SECMOD_Init() here, but if we aren't already
* inited, then there's no modules to restart, so SECMOD_RestartModules
* will return immediately */
SECMOD_RestartModules(PR_FALSE);
} else {
configStrings = pk11_config_strings;
configName = pk11_config_name;
passwordRequired = pk11_password_required;
}
/* Skip the module init if we are already initted and we are trying
* to init with noCertDB and noModDB */
if (!(isReallyInitted && noCertDB && noModDB)) {
parent = nss_InitModules(configdir, certPrefix, keyPrefix, secmodName,
updateDir, updCertPrefix, updKeyPrefix, updateID,
updateName, configName, configStrings, passwordRequired,
readOnly, noCertDB, noModDB, forceOpen, optimizeSpace,
(initContextPtr != NULL));
if (parent == NULL) {
goto loser;
}
}
/* finish up initialization */
if (!isReallyInitted) {
if (SECOID_Init() != SECSuccess) {
goto loser;
}
#ifdef POLICY_FILE
/* Load the system crypto policy file if it exists,
* unless the NSS_IGNORE_SYSTEM_POLICY environment
* variable has been set to 1. */
ignoreVar = PR_GetEnvSecure("NSS_IGNORE_SYSTEM_POLICY");
if (ignoreVar == NULL || strncmp(ignoreVar, "1", sizeof("1")) != 0) {
if (PR_Access(POLICY_PATH "/" POLICY_FILE, PR_ACCESS_READ_OK) == PR_SUCCESS) {
SECMODModule *module = SECMOD_LoadModule(
"name=\"Policy File\" "
"parameters=\"configdir='sql:" POLICY_PATH "' "
"secmod='" POLICY_FILE "' "
"flags=readOnly,noCertDB,forceSecmodChoice,forceOpen\" "
"NSS=\"flags=internal,moduleDB,skipFirst,moduleDBOnly,critical\"",
parent, PR_TRUE);
if (module) {
PRBool isLoaded = module->loaded;
SECMOD_DestroyModule(module);
if (!isLoaded) {
goto loser;
}
}
}
}
#endif
if (STAN_LoadDefaultNSS3TrustDomain() != PR_SUCCESS) {
goto loser;
}
if (nss_InitShutdownList() != SECSuccess) {
goto loser;
}
CERT_SetDefaultCertDB((CERTCertDBHandle *)
STAN_GetDefaultTrustDomain());
if ((!noModDB) && (!noCertDB) && (!noRootInit)) {
if (!SECMOD_HasRootCerts()) {
const char *dbpath = configdir;
/* handle supported database modifiers */
if (strncmp(dbpath, "sql:", 4) == 0) {
dbpath += 4;
} else if (strncmp(dbpath, "dbm:", 4) == 0) {
dbpath += 4;
} else if (strncmp(dbpath, "extern:", 7) == 0) {
dbpath += 7;
} else if (strncmp(dbpath, "rdb:", 4) == 0) {
/* if rdb: is specified, the configdir isn't really a
* path. Skip it */
dbpath = NULL;
}
if (dbpath) {
nss_FindExternalRoot(dbpath, secmodName);
}
}
}
pk11sdr_Init();
cert_CreateSubjectKeyIDHashTable();
#ifndef NSS_DISABLE_LIBPKIX
pkixError = PKIX_Initialize(PKIX_FALSE, PKIX_MAJOR_VERSION, PKIX_MINOR_VERSION,
PKIX_MINOR_VERSION, &actualMinorVersion, &plContext);
if (pkixError != NULL) {
goto loser;
} else {
char *ev = PR_GetEnvSecure("NSS_ENABLE_PKIX_VERIFY");
if (ev && ev[0]) {
CERT_SetUsePKIXForValidation(PR_TRUE);
}
}
#endif /* NSS_DISABLE_LIBPKIX */
}
/*
* Now mark the appropriate init state. If initContextPtr was passed
* in, then return the new context pointer and add it to the
* nssInitContextList. Otherwise set the global nss_isInitted flag
*/
PZ_Lock(nssInitLock);
if (!initContextPtr) {
nssIsInitted = PR_TRUE;
} else {
(*initContextPtr)->magic = NSS_INIT_MAGIC;
(*initContextPtr)->next = nssInitContextList;
nssInitContextList = (*initContextPtr);
}
nssIsInInit--;
/* now that we are inited, all waiters can move forward */
PZ_NotifyAllCondVar(nssInitCondition);
PZ_Unlock(nssInitLock);
if (initContextPtr && configStrings) {
PR_smprintf_free(configStrings);
}
if (parent) {
SECMOD_DestroyModule(parent);
}
return SECSuccess;
loser:
if (initContextPtr && *initContextPtr) {
PORT_Free(*initContextPtr);
*initContextPtr = NULL;
if (configStrings) {
PR_smprintf_free(configStrings);
}
}
PZ_Lock(nssInitLock);
nssIsInInit--;
/* We failed to init, allow one to move forward */
PZ_NotifyCondVar(nssInitCondition);
PZ_Unlock(nssInitLock);
if (parent) {
SECMOD_DestroyModule(parent);
}
return SECFailure;
}
SECStatus
NSS_Init(const char *configdir)
{
return nss_Init(configdir, "", "", SECMOD_DB, "", "", "", "", "", NULL,
NULL, PR_TRUE, PR_FALSE, PR_FALSE, PR_FALSE, PR_FALSE,
PR_TRUE, PR_FALSE, PR_FALSE, PR_FALSE);
}
SECStatus
NSS_InitReadWrite(const char *configdir)
{
return nss_Init(configdir, "", "", SECMOD_DB, "", "", "", "", "", NULL,
NULL, PR_FALSE, PR_FALSE, PR_FALSE, PR_FALSE, PR_FALSE,
PR_TRUE, PR_FALSE, PR_FALSE, PR_FALSE);
}
/*
* OK there are now lots of options here, lets go through them all:
*
* configdir - base directory where all the cert, key, and module datbases live.
* certPrefix - prefix added to the beginning of the cert database example: "
* "https-server1-"
* keyPrefix - prefix added to the beginning of the key database example: "
* "https-server1-"
* secmodName - name of the security module database (usually "secmod.db").
* flags - change the open options of NSS_Initialize as follows:
* NSS_INIT_READONLY - Open the databases read only.
* NSS_INIT_NOCERTDB - Don't open the cert DB and key DB's, just
* initialize the volatile certdb.
* NSS_INIT_NOMODDB - Don't open the security module DB, just
* initialize the PKCS #11 module.
* NSS_INIT_FORCEOPEN - Continue to force initializations even if the
* databases cannot be opened.
* NSS_INIT_PK11THREADSAFE - only load PKCS#11 modules that are
* thread-safe, ie. that support locking - either OS
* locking or NSS-provided locks . If a PKCS#11
* module isn't thread-safe, don't serialize its
* calls; just don't load it instead. This is necessary
* if another piece of code is using the same PKCS#11
* modules that NSS is accessing without going through
* NSS, for example the Java SunPKCS11 provider.
* NSS_INIT_PK11RELOAD - ignore the CKR_CRYPTOKI_ALREADY_INITIALIZED
* error when loading PKCS#11 modules. This is necessary
* if another piece of code is using the same PKCS#11
* modules that NSS is accessing without going through
* NSS, for example Java SunPKCS11 provider.
* NSS_INIT_NOPK11FINALIZE - never call C_Finalize on any
* PKCS#11 module. This may be necessary in order to
* ensure continuous operation and proper shutdown
* sequence if another piece of code is using the same
* PKCS#11 modules that NSS is accessing without going
* through NSS, for example Java SunPKCS11 provider.
* The following limitation applies when this is set :
* SECMOD_WaitForAnyTokenEvent will not use
* C_WaitForSlotEvent, in order to prevent the need for
* C_Finalize. This call will be emulated instead.
* NSS_INIT_RESERVED - Currently has no effect, but may be used in the
* future to trigger better cooperation between PKCS#11
* modules used by both NSS and the Java SunPKCS11
* provider. This should occur after a new flag is defined
* for C_Initialize by the PKCS#11 working group.
* NSS_INIT_COOPERATE - Sets 4 recommended options for applications that
* use both NSS and the Java SunPKCS11 provider.
*/
SECStatus
NSS_Initialize(const char *configdir, const char *certPrefix,
const char *keyPrefix, const char *secmodName, PRUint32 flags)
{
return nss_Init(configdir, certPrefix, keyPrefix, secmodName,
"", "", "", "", "", NULL, NULL,
((flags & NSS_INIT_READONLY) == NSS_INIT_READONLY),
((flags & NSS_INIT_NOCERTDB) == NSS_INIT_NOCERTDB),
((flags & NSS_INIT_NOMODDB) == NSS_INIT_NOMODDB),
((flags & NSS_INIT_FORCEOPEN) == NSS_INIT_FORCEOPEN),
((flags & NSS_INIT_NOROOTINIT) == NSS_INIT_NOROOTINIT),
((flags & NSS_INIT_OPTIMIZESPACE) == NSS_INIT_OPTIMIZESPACE),
((flags & NSS_INIT_PK11THREADSAFE) == NSS_INIT_PK11THREADSAFE),
((flags & NSS_INIT_PK11RELOAD) == NSS_INIT_PK11RELOAD),
((flags & NSS_INIT_NOPK11FINALIZE) == NSS_INIT_NOPK11FINALIZE));
}
NSSInitContext *
NSS_InitContext(const char *configdir, const char *certPrefix,
const char *keyPrefix, const char *secmodName,
NSSInitParameters *initParams, PRUint32 flags)
{
SECStatus rv;
NSSInitContext *context;
rv = nss_Init(configdir, certPrefix, keyPrefix, secmodName,
"", "", "", "", "", &context, initParams,
((flags & NSS_INIT_READONLY) == NSS_INIT_READONLY),
((flags & NSS_INIT_NOCERTDB) == NSS_INIT_NOCERTDB),
((flags & NSS_INIT_NOMODDB) == NSS_INIT_NOMODDB),
((flags & NSS_INIT_FORCEOPEN) == NSS_INIT_FORCEOPEN), PR_TRUE,
((flags & NSS_INIT_OPTIMIZESPACE) == NSS_INIT_OPTIMIZESPACE),
((flags & NSS_INIT_PK11THREADSAFE) == NSS_INIT_PK11THREADSAFE),
((flags & NSS_INIT_PK11RELOAD) == NSS_INIT_PK11RELOAD),
((flags & NSS_INIT_NOPK11FINALIZE) == NSS_INIT_NOPK11FINALIZE));
return (rv == SECSuccess) ? context : NULL;
}
SECStatus
NSS_InitWithMerge(const char *configdir, const char *certPrefix,
const char *keyPrefix, const char *secmodName,
const char *updateDir, const char *updCertPrefix,
const char *updKeyPrefix, const char *updateID,
const char *updateName, PRUint32 flags)
{
return nss_Init(configdir, certPrefix, keyPrefix, secmodName,
updateDir, updCertPrefix, updKeyPrefix, updateID, updateName,
NULL, NULL,
((flags & NSS_INIT_READONLY) == NSS_INIT_READONLY),
((flags & NSS_INIT_NOCERTDB) == NSS_INIT_NOCERTDB),
((flags & NSS_INIT_NOMODDB) == NSS_INIT_NOMODDB),
((flags & NSS_INIT_FORCEOPEN) == NSS_INIT_FORCEOPEN),
((flags & NSS_INIT_NOROOTINIT) == NSS_INIT_NOROOTINIT),
((flags & NSS_INIT_OPTIMIZESPACE) == NSS_INIT_OPTIMIZESPACE),
((flags & NSS_INIT_PK11THREADSAFE) == NSS_INIT_PK11THREADSAFE),
((flags & NSS_INIT_PK11RELOAD) == NSS_INIT_PK11RELOAD),
((flags & NSS_INIT_NOPK11FINALIZE) == NSS_INIT_NOPK11FINALIZE));
}
/*
* initialize NSS without a creating cert db's, key db's, or secmod db's.
*/
SECStatus
NSS_NoDB_Init(const char *configdir)
{
return nss_Init("", "", "", "", "", "", "", "", "", NULL, NULL,
PR_TRUE, PR_TRUE, PR_TRUE, PR_TRUE, PR_TRUE, PR_TRUE,
PR_FALSE, PR_FALSE, PR_FALSE);
}
#define NSS_SHUTDOWN_STEP 10
struct NSSShutdownFuncPair {
NSS_ShutdownFunc func;
void *appData;
};
static struct NSSShutdownListStr {
PZLock *lock;
int allocatedFuncs;
int peakFuncs;
struct NSSShutdownFuncPair *funcs;
} nssShutdownList = { 0 };
/*
* find and existing shutdown function
*/
static int
nss_GetShutdownEntry(NSS_ShutdownFunc sFunc, void *appData)
{
int count, i;
count = nssShutdownList.peakFuncs;
for (i = 0; i < count; i++) {
if ((nssShutdownList.funcs[i].func == sFunc) &&
(nssShutdownList.funcs[i].appData == appData)) {
return i;
}
}
return -1;
}
/*
* register a callback to be called when NSS shuts down
*/
SECStatus
NSS_RegisterShutdown(NSS_ShutdownFunc sFunc, void *appData)
{
int i;
/* make sure our lock and condition variable are initialized one and only
* one time */
if (PR_CallOnce(&nssInitOnce, nss_doLockInit) != PR_SUCCESS) {
return SECFailure;
}
PZ_Lock(nssInitLock);
if (!NSS_IsInitialized()) {
PZ_Unlock(nssInitLock);
PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
return SECFailure;
}
PZ_Unlock(nssInitLock);
if (sFunc == NULL) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
PORT_Assert(nssShutdownList.lock);
PZ_Lock(nssShutdownList.lock);
/* make sure we don't have a duplicate */
i = nss_GetShutdownEntry(sFunc, appData);
if (i >= 0) {
PZ_Unlock(nssShutdownList.lock);
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
return SECFailure;
}
/* find an empty slot */
i = nss_GetShutdownEntry(NULL, NULL);
if (i >= 0) {
nssShutdownList.funcs[i].func = sFunc;
nssShutdownList.funcs[i].appData = appData;
PZ_Unlock(nssShutdownList.lock);
return SECSuccess;
}
if (nssShutdownList.allocatedFuncs == nssShutdownList.peakFuncs) {
struct NSSShutdownFuncPair *funcs =
(struct NSSShutdownFuncPair *)PORT_Realloc(nssShutdownList.funcs,
(nssShutdownList.allocatedFuncs + NSS_SHUTDOWN_STEP) * sizeof(struct NSSShutdownFuncPair));
if (!funcs) {
PZ_Unlock(nssShutdownList.lock);
return SECFailure;
}
nssShutdownList.funcs = funcs;
nssShutdownList.allocatedFuncs += NSS_SHUTDOWN_STEP;
}
nssShutdownList.funcs[nssShutdownList.peakFuncs].func = sFunc;
nssShutdownList.funcs[nssShutdownList.peakFuncs].appData = appData;
nssShutdownList.peakFuncs++;
PZ_Unlock(nssShutdownList.lock);
return SECSuccess;
}
/*
* unregister a callback so it won't get called on shutdown.
*/
SECStatus
NSS_UnregisterShutdown(NSS_ShutdownFunc sFunc, void *appData)
{
int i;
/* make sure our lock and condition variable are initialized one and only
* one time */
if (PR_CallOnce(&nssInitOnce, nss_doLockInit) != PR_SUCCESS) {
return SECFailure;
}
PZ_Lock(nssInitLock);
if (!NSS_IsInitialized()) {
PZ_Unlock(nssInitLock);
PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
return SECFailure;
}
PZ_Unlock(nssInitLock);
PORT_Assert(nssShutdownList.lock);
PZ_Lock(nssShutdownList.lock);
i = nss_GetShutdownEntry(sFunc, appData);
if (i >= 0) {
nssShutdownList.funcs[i].func = NULL;
nssShutdownList.funcs[i].appData = NULL;
}
PZ_Unlock(nssShutdownList.lock);
if (i < 0) {
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
return SECFailure;
}
return SECSuccess;
}
/*
* bring up and shutdown the shutdown list
*/
static SECStatus
nss_InitShutdownList(void)
{
if (nssShutdownList.lock != NULL) {
return SECSuccess;
}
nssShutdownList.lock = PZ_NewLock(nssILockOther);
if (nssShutdownList.lock == NULL) {
return SECFailure;
}
nssShutdownList.funcs = PORT_ZNewArray(struct NSSShutdownFuncPair,
NSS_SHUTDOWN_STEP);
if (nssShutdownList.funcs == NULL) {
PZ_DestroyLock(nssShutdownList.lock);
nssShutdownList.lock = NULL;
return SECFailure;
}
nssShutdownList.allocatedFuncs = NSS_SHUTDOWN_STEP;
nssShutdownList.peakFuncs = 0;
return SECSuccess;
}
static SECStatus
nss_ShutdownShutdownList(void)
{
SECStatus rv = SECSuccess;
int i;
/* call all the registerd functions first */
for (i = 0; i < nssShutdownList.peakFuncs; i++) {
struct NSSShutdownFuncPair *funcPair = &nssShutdownList.funcs[i];
if (funcPair->func) {
if ((*funcPair->func)(funcPair->appData, NULL) != SECSuccess) {
rv = SECFailure;
}
}
}
nssShutdownList.peakFuncs = 0;
nssShutdownList.allocatedFuncs = 0;
PORT_Free(nssShutdownList.funcs);
nssShutdownList.funcs = NULL;
if (nssShutdownList.lock) {
PZ_DestroyLock(nssShutdownList.lock);
}
nssShutdownList.lock = NULL;
return rv;
}
extern const NSSError NSS_ERROR_BUSY;
SECStatus
nss_Shutdown(void)
{
SECStatus shutdownRV = SECSuccess;
SECStatus rv;
PRStatus status;
NSSInitContext *temp;
rv = nss_ShutdownShutdownList();
if (rv != SECSuccess) {
shutdownRV = SECFailure;
}
cert_DestroyLocks();
ShutdownCRLCache();
OCSP_ShutdownGlobal();
#ifndef NSS_DISABLE_LIBPKIX
PKIX_Shutdown(plContext);
#endif /* NSS_DISABLE_LIBPKIX */
SECOID_Shutdown();
status = STAN_Shutdown();
cert_DestroySubjectKeyIDHashTable();
pk11_SetInternalKeySlot(NULL);
rv = SECMOD_Shutdown();
if (rv != SECSuccess) {
shutdownRV = SECFailure;
}
pk11sdr_Shutdown();
nssArena_Shutdown();
if (status == PR_FAILURE) {
if (NSS_GetError() == NSS_ERROR_BUSY) {
PORT_SetError(SEC_ERROR_BUSY);
}
shutdownRV = SECFailure;
}
/*
* A thread's error stack is automatically destroyed when the thread
* terminates, except for the primordial thread, whose error stack is
* destroyed by PR_Cleanup. Since NSS is usually shut down by the
* primordial thread and many NSS-based apps don't call PR_Cleanup,
* we destroy the calling thread's error stack here. This must be
* done after any NSS_GetError call, otherwise NSS_GetError will
* create the error stack again.
*/
nss_DestroyErrorStack();
nssIsInitted = PR_FALSE;
temp = nssInitContextList;
nssInitContextList = NULL;
/* free the old list. This is necessary when we are called from
* NSS_Shutdown(). */
while (temp) {
NSSInitContext *next = temp->next;
temp->magic = 0;
PORT_Free(temp);
temp = next;
}
return shutdownRV;
}
SECStatus
NSS_Shutdown(void)
{
SECStatus rv;
/* make sure our lock and condition variable are initialized one and only
* one time */
if (PR_CallOnce(&nssInitOnce, nss_doLockInit) != PR_SUCCESS) {
return SECFailure;
}
PZ_Lock(nssInitLock);
if (!nssIsInitted) {
PZ_Unlock(nssInitLock);
PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
return SECFailure;
}
/* If one or more threads are in the middle of init, wait for them
* to complete */
while (nssIsInInit) {
PZ_WaitCondVar(nssInitCondition, PR_INTERVAL_NO_TIMEOUT);
}
rv = nss_Shutdown();
PZ_Unlock(nssInitLock);
return rv;
}
/*
* remove the context from a list. return true if found, false if not
*/
PRBool
nss_RemoveList(NSSInitContext *context)
{
NSSInitContext *this = nssInitContextList;
NSSInitContext **last = &nssInitContextList;
while (this) {
if (this == context) {
*last = this->next;
this->magic = 0;
PORT_Free(this);
return PR_TRUE;
}
last = &this->next;
this = this->next;
}
return PR_FALSE;
}
/*
* This form of shutdown is safe in the case where we may have multiple
* entities using NSS in a single process. Each entity calls shutdown with
* it's own context. The application (which doesn't get a context), calls
* shutdown with NULL. Once all users have 'checked in' NSS will shutdown.
* This is different than NSS_Shutdown, where calling it will shutdown NSS
* irreguardless of who else may have NSS open.
*/
SECStatus
NSS_ShutdownContext(NSSInitContext *context)
{
SECStatus rv = SECSuccess;
/* make sure our lock and condition variable are initialized one and only
* one time */
if (PR_CallOnce(&nssInitOnce, nss_doLockInit) != PR_SUCCESS) {
return SECFailure;
}
PZ_Lock(nssInitLock);
/* If one or more threads are in the middle of init, wait for them
* to complete */
while (nssIsInInit) {
PZ_WaitCondVar(nssInitCondition, PR_INTERVAL_NO_TIMEOUT);
}
/* OK, we are the only thread now either initializing or shutting down */
if (!context) {
if (!nssIsInitted) {
PZ_Unlock(nssInitLock);
PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
return SECFailure;
}
nssIsInitted = 0;
} else if (!nss_RemoveList(context)) {
PZ_Unlock(nssInitLock);
/* context was already freed or wasn't valid */
PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
return SECFailure;
}
if ((nssIsInitted == 0) && (nssInitContextList == NULL)) {
rv = nss_Shutdown();
}
/* NOTE: we don't try to free the nssInitLocks to prevent races against
* the locks. There may be a thread, right now, waiting in NSS_Init for us
* to free the lock below. If we delete the locks, bad things would happen
* to that thread */
PZ_Unlock(nssInitLock);
return rv;
}
PRBool
NSS_IsInitialized(void)
{
return (nssIsInitted) || (nssInitContextList != NULL);
}
extern const char __nss_base_version[];
PRBool
NSS_VersionCheck(const char *importedVersion)
{
/*
* This is the secret handshake algorithm.
*
* This release has a simple version compatibility
* check algorithm. This release is not backward
* compatible with previous major releases. It is
* not compatible with future major, minor, or
* patch releases or builds.
*/
int vmajor = 0, vminor = 0, vpatch = 0, vbuild = 0;
const char *ptr = importedVersion;
#define NSS_VERSION_VARIABLE __nss_base_version
#include "verref.h"
while (isdigit(*ptr)) {
vmajor = 10 * vmajor + *ptr - '0';
ptr++;
}
if (*ptr == '.') {
ptr++;
while (isdigit(*ptr)) {
vminor = 10 * vminor + *ptr - '0';
ptr++;
}
if (*ptr == '.') {
ptr++;
while (isdigit(*ptr)) {
vpatch = 10 * vpatch + *ptr - '0';
ptr++;
}
if (*ptr == '.') {
ptr++;
while (isdigit(*ptr)) {
vbuild = 10 * vbuild + *ptr - '0';
ptr++;
}
}
}
}
if (vmajor != NSS_VMAJOR) {
return PR_FALSE;
}
if (vmajor == NSS_VMAJOR && vminor > NSS_VMINOR) {
return PR_FALSE;
}
if (vmajor == NSS_VMAJOR && vminor == NSS_VMINOR && vpatch > NSS_VPATCH) {
return PR_FALSE;
}
if (vmajor == NSS_VMAJOR && vminor == NSS_VMINOR &&
vpatch == NSS_VPATCH && vbuild > NSS_VBUILD) {
return PR_FALSE;
}
return PR_TRUE;
}
const char *
NSS_GetVersion(void)
{
return NSS_VERSION;
}