/* 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/. */ /* * The following handles the loading, unloading and management of * various PCKS #11 modules */ #include #include #include "pkcs11.h" #include "seccomon.h" #include "secmod.h" #include "secmodi.h" #include "secmodti.h" #include "pki3hack.h" #include "secerr.h" #include "nss.h" #include "utilpars.h" #include "pk11pub.h" /* create a new module */ static SECMODModule * secmod_NewModule(void) { SECMODModule *newMod; PLArenaPool *arena; /* create an arena in which dllName and commonName can be * allocated. */ arena = PORT_NewArena(512); if (arena == NULL) { return NULL; } newMod = (SECMODModule *)PORT_ArenaAlloc(arena, sizeof(SECMODModule)); if (newMod == NULL) { PORT_FreeArena(arena, PR_FALSE); return NULL; } /* * initialize of the fields of the module */ newMod->arena = arena; newMod->internal = PR_FALSE; newMod->loaded = PR_FALSE; newMod->isFIPS = PR_FALSE; newMod->dllName = NULL; newMod->commonName = NULL; newMod->library = NULL; newMod->functionList = NULL; newMod->slotCount = 0; newMod->slots = NULL; newMod->slotInfo = NULL; newMod->slotInfoCount = 0; newMod->refCount = 1; newMod->ssl[0] = 0; newMod->ssl[1] = 0; newMod->libraryParams = NULL; newMod->moduleDBFunc = NULL; newMod->parent = NULL; newMod->isCritical = PR_FALSE; newMod->isModuleDB = PR_FALSE; newMod->moduleDBOnly = PR_FALSE; newMod->trustOrder = 0; newMod->cipherOrder = 0; newMod->evControlMask = 0; newMod->refLock = PZ_NewLock(nssILockRefLock); if (newMod->refLock == NULL) { PORT_FreeArena(arena, PR_FALSE); return NULL; } return newMod; } /* private flags for isModuleDB (field in SECMODModule). */ /* The meaing of these flags is as follows: * * SECMOD_FLAG_MODULE_DB_IS_MODULE_DB - This is a module that accesses the * database of other modules to load. Module DBs are loadable modules that * tells NSS which PKCS #11 modules to load and when. These module DBs are * chainable. That is, one module DB can load another one. NSS system init * design takes advantage of this feature. In system NSS, a fixed system * module DB loads the system defined libraries, then chains out to the * traditional module DBs to load any system or user configured modules * (like smart cards). This bit is the same as the already existing meaning * of isModuleDB = PR_TRUE. None of the other module db flags should be set * if this flag isn't on. * * SECMOD_FLAG_MODULE_DB_SKIP_FIRST - This flag tells NSS to skip the first * PKCS #11 module presented by a module DB. This allows the OS to load a * softoken from the system module, then ask the existing module DB code to * load the other PKCS #11 modules in that module DB (skipping it's request * to load softoken). This gives the system init finer control over the * configuration of that softoken module. * * SECMOD_FLAG_MODULE_DB_DEFAULT_MODDB - This flag allows system init to mark a * different module DB as the 'default' module DB (the one in which * 'Add module' changes will go). Without this flag NSS takes the first * module as the default Module DB, but in system NSS, that first module * is the system module, which is likely read only (at least to the user). * This allows system NSS to delegate those changes to the user's module DB, * preserving the user's ability to load new PKCS #11 modules (which only * affect him), from existing applications like Firefox. */ #define SECMOD_FLAG_MODULE_DB_IS_MODULE_DB 0x01 /* must be set if any of the \ *other flags are set */ #define SECMOD_FLAG_MODULE_DB_SKIP_FIRST 0x02 #define SECMOD_FLAG_MODULE_DB_DEFAULT_MODDB 0x04 #define SECMOD_FLAG_MODULE_DB_POLICY_ONLY 0x08 /* private flags for internal (field in SECMODModule). */ /* The meaing of these flags is as follows: * * SECMOD_FLAG_INTERNAL_IS_INTERNAL - This is a marks the the module is * the internal module (that is, softoken). This bit is the same as the * already existing meaning of internal = PR_TRUE. None of the other * internal flags should be set if this flag isn't on. * * SECMOD_FLAG_MODULE_INTERNAL_KEY_SLOT - This flag allows system init to mark * a different slot returned byt PK11_GetInternalKeySlot(). The 'primary' * slot defined by this module will be the new internal key slot. */ #define SECMOD_FLAG_INTERNAL_IS_INTERNAL 0x01 /* must be set if any of \ *the other flags are set */ #define SECMOD_FLAG_INTERNAL_KEY_SLOT 0x02 /* private flags for policy check. */ #define SECMOD_FLAG_POLICY_CHECK_IDENTIFIER 0x01 #define SECMOD_FLAG_POLICY_CHECK_VALUE 0x02 /* * for 3.4 we continue to use the old SECMODModule structure */ SECMODModule * SECMOD_CreateModule(const char *library, const char *moduleName, const char *parameters, const char *nss) { return SECMOD_CreateModuleEx(library, moduleName, parameters, nss, NULL); } /* * NSS config options format: * * The specified ciphers will be allowed by policy, but an application * may allow more by policy explicitly: * config="allow=curve1:curve2:hash1:hash2:rsa-1024..." * * Only the specified hashes and curves will be allowed: * config="disallow=all allow=sha1:sha256:secp256r1:secp384r1" * * Only the specified hashes and curves will be allowed, and * RSA keys of 2048 or more will be accepted, and DH key exchange * with 1024-bit primes or more: * config="disallow=all allow=sha1:sha256:secp256r1:secp384r1:min-rsa=2048:min-dh=1024" * * A policy that enables the AES ciphersuites and the SECP256/384 curves: * config="allow=aes128-cbc:aes128-gcm:TLS1.0:TLS1.2:TLS1.1:HMAC-SHA1:SHA1:SHA256:SHA384:RSA:ECDHE-RSA:SECP256R1:SECP384R1" * * Disallow values are parsed first, then allow values, independent of the * order they appear. * * flags: turn on the following flags: * policy-lock: turn off the ability for applications to change policy with * the call NSS_SetAlgorithmPolicy or the other system policy * calls (SSL_SetPolicy, etc.) * ssl-lock: turn off the ability to change the ssl defaults. * * The following only apply to ssl cipher suites (future smime) * * enable: turn on ciphersuites by default. * disable: turn off ciphersuites by default without disallowing them by policy. * * */ typedef struct { const char *name; unsigned name_size; SECOidTag oid; PRUint32 val; } oidValDef; typedef struct { const char *name; unsigned name_size; PRInt32 option; } optionFreeDef; typedef struct { const char *name; unsigned name_size; PRUint32 flag; } policyFlagDef; /* * This table should be merged with the SECOID table. */ #define CIPHER_NAME(x) x, (sizeof(x) - 1) static const oidValDef curveOptList[] = { /* Curves */ { CIPHER_NAME("PRIME192V1"), SEC_OID_ANSIX962_EC_PRIME192V1, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("PRIME192V2"), SEC_OID_ANSIX962_EC_PRIME192V2, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("PRIME192V3"), SEC_OID_ANSIX962_EC_PRIME192V3, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("PRIME239V1"), SEC_OID_ANSIX962_EC_PRIME239V1, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("PRIME239V2"), SEC_OID_ANSIX962_EC_PRIME239V2, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("PRIME239V3"), SEC_OID_ANSIX962_EC_PRIME239V3, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("PRIME256V1"), SEC_OID_ANSIX962_EC_PRIME256V1, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("SECP112R1"), SEC_OID_SECG_EC_SECP112R1, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("SECP112R2"), SEC_OID_SECG_EC_SECP112R2, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("SECP128R1"), SEC_OID_SECG_EC_SECP128R1, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("SECP128R2"), SEC_OID_SECG_EC_SECP128R2, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("SECP160K1"), SEC_OID_SECG_EC_SECP160K1, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("SECP160R1"), SEC_OID_SECG_EC_SECP160R1, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("SECP160R2"), SEC_OID_SECG_EC_SECP160R2, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("SECP192K1"), SEC_OID_SECG_EC_SECP192K1, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("SECP192R1"), SEC_OID_ANSIX962_EC_PRIME192V1, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("SECP224K1"), SEC_OID_SECG_EC_SECP224K1, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("SECP256K1"), SEC_OID_SECG_EC_SECP256K1, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("SECP256R1"), SEC_OID_ANSIX962_EC_PRIME256V1, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("SECP384R1"), SEC_OID_SECG_EC_SECP384R1, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("SECP521R1"), SEC_OID_SECG_EC_SECP521R1, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("CURVE25519"), SEC_OID_CURVE25519, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("XYBER768D00"), SEC_OID_XYBER768D00, 0 }, /* ANSI X9.62 named elliptic curves (characteristic two field) */ { CIPHER_NAME("C2PNB163V1"), SEC_OID_ANSIX962_EC_C2PNB163V1, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("C2PNB163V2"), SEC_OID_ANSIX962_EC_C2PNB163V2, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("C2PNB163V3"), SEC_OID_ANSIX962_EC_C2PNB163V3, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("C2PNB176V1"), SEC_OID_ANSIX962_EC_C2PNB176V1, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("C2TNB191V1"), SEC_OID_ANSIX962_EC_C2TNB191V1, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("C2TNB191V2"), SEC_OID_ANSIX962_EC_C2TNB191V2, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("C2TNB191V3"), SEC_OID_ANSIX962_EC_C2TNB191V3, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("C2ONB191V4"), SEC_OID_ANSIX962_EC_C2ONB191V4, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("C2ONB191V5"), SEC_OID_ANSIX962_EC_C2ONB191V5, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("C2PNB208W1"), SEC_OID_ANSIX962_EC_C2PNB208W1, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("C2TNB239V1"), SEC_OID_ANSIX962_EC_C2TNB239V1, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("C2TNB239V2"), SEC_OID_ANSIX962_EC_C2TNB239V2, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("C2TNB239V3"), SEC_OID_ANSIX962_EC_C2TNB239V3, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("C2ONB239V4"), SEC_OID_ANSIX962_EC_C2ONB239V4, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("C2ONB239V5"), SEC_OID_ANSIX962_EC_C2ONB239V5, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("C2PNB272W1"), SEC_OID_ANSIX962_EC_C2PNB272W1, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("C2PNB304W1"), SEC_OID_ANSIX962_EC_C2PNB304W1, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("C2TNB359V1"), SEC_OID_ANSIX962_EC_C2TNB359V1, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("C2PNB368W1"), SEC_OID_ANSIX962_EC_C2PNB368W1, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("C2TNB431R1"), SEC_OID_ANSIX962_EC_C2TNB431R1, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, /* SECG named elliptic curves (characteristic two field) */ { CIPHER_NAME("SECT113R1"), SEC_OID_SECG_EC_SECT113R1, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("SECT131R1"), SEC_OID_SECG_EC_SECT113R2, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("SECT131R1"), SEC_OID_SECG_EC_SECT131R1, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("SECT131R2"), SEC_OID_SECG_EC_SECT131R2, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("SECT163K1"), SEC_OID_SECG_EC_SECT163K1, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("SECT163R1"), SEC_OID_SECG_EC_SECT163R1, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("SECT163R2"), SEC_OID_SECG_EC_SECT163R2, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("SECT193R1"), SEC_OID_SECG_EC_SECT193R1, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("SECT193R2"), SEC_OID_SECG_EC_SECT193R2, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("SECT233K1"), SEC_OID_SECG_EC_SECT233K1, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("SECT233R1"), SEC_OID_SECG_EC_SECT233R1, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("SECT239K1"), SEC_OID_SECG_EC_SECT239K1, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("SECT283K1"), SEC_OID_SECG_EC_SECT283K1, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("SECT283R1"), SEC_OID_SECG_EC_SECT283R1, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("SECT409K1"), SEC_OID_SECG_EC_SECT409K1, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("SECT409R1"), SEC_OID_SECG_EC_SECT409R1, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("SECT571K1"), SEC_OID_SECG_EC_SECT571K1, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("SECT571R1"), SEC_OID_SECG_EC_SECT571R1, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, }; static const oidValDef hashOptList[] = { /* Hashes */ { CIPHER_NAME("MD2"), SEC_OID_MD2, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_SIGNATURE }, { CIPHER_NAME("MD4"), SEC_OID_MD4, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_SIGNATURE }, { CIPHER_NAME("MD5"), SEC_OID_MD5, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_SIGNATURE }, { CIPHER_NAME("SHA1"), SEC_OID_SHA1, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_SIGNATURE }, { CIPHER_NAME("SHA224"), SEC_OID_SHA224, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_SIGNATURE }, { CIPHER_NAME("SHA256"), SEC_OID_SHA256, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_SIGNATURE }, { CIPHER_NAME("SHA384"), SEC_OID_SHA384, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_SIGNATURE }, { CIPHER_NAME("SHA512"), SEC_OID_SHA512, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_SIGNATURE } }; static const oidValDef macOptList[] = { /* MACs */ { CIPHER_NAME("HMAC-SHA1"), SEC_OID_HMAC_SHA1, NSS_USE_ALG_IN_SSL }, { CIPHER_NAME("HMAC-SHA224"), SEC_OID_HMAC_SHA224, NSS_USE_ALG_IN_SSL }, { CIPHER_NAME("HMAC-SHA256"), SEC_OID_HMAC_SHA256, NSS_USE_ALG_IN_SSL }, { CIPHER_NAME("HMAC-SHA384"), SEC_OID_HMAC_SHA384, NSS_USE_ALG_IN_SSL }, { CIPHER_NAME("HMAC-SHA512"), SEC_OID_HMAC_SHA512, NSS_USE_ALG_IN_SSL }, { CIPHER_NAME("HMAC-MD5"), SEC_OID_HMAC_MD5, NSS_USE_ALG_IN_SSL }, }; static const oidValDef cipherOptList[] = { /* Ciphers */ { CIPHER_NAME("AES128-CBC"), SEC_OID_AES_128_CBC, NSS_USE_ALG_IN_SSL }, { CIPHER_NAME("AES192-CBC"), SEC_OID_AES_192_CBC, NSS_USE_ALG_IN_SSL }, { CIPHER_NAME("AES256-CBC"), SEC_OID_AES_256_CBC, NSS_USE_ALG_IN_SSL }, { CIPHER_NAME("AES128-GCM"), SEC_OID_AES_128_GCM, NSS_USE_ALG_IN_SSL }, { CIPHER_NAME("AES192-GCM"), SEC_OID_AES_192_GCM, NSS_USE_ALG_IN_SSL }, { CIPHER_NAME("AES256-GCM"), SEC_OID_AES_256_GCM, NSS_USE_ALG_IN_SSL }, { CIPHER_NAME("CAMELLIA128-CBC"), SEC_OID_CAMELLIA_128_CBC, NSS_USE_ALG_IN_SSL }, { CIPHER_NAME("CAMELLIA192-CBC"), SEC_OID_CAMELLIA_192_CBC, NSS_USE_ALG_IN_SSL }, { CIPHER_NAME("CAMELLIA256-CBC"), SEC_OID_CAMELLIA_256_CBC, NSS_USE_ALG_IN_SSL }, { CIPHER_NAME("CHACHA20-POLY1305"), SEC_OID_CHACHA20_POLY1305, NSS_USE_ALG_IN_SSL }, { CIPHER_NAME("SEED-CBC"), SEC_OID_SEED_CBC, NSS_USE_ALG_IN_SSL }, { CIPHER_NAME("DES-EDE3-CBC"), SEC_OID_DES_EDE3_CBC, NSS_USE_ALG_IN_SSL }, { CIPHER_NAME("DES-40-CBC"), SEC_OID_DES_40_CBC, NSS_USE_ALG_IN_SSL }, { CIPHER_NAME("DES-CBC"), SEC_OID_DES_CBC, NSS_USE_ALG_IN_SSL }, { CIPHER_NAME("NULL-CIPHER"), SEC_OID_NULL_CIPHER, NSS_USE_ALG_IN_SSL }, { CIPHER_NAME("RC2"), SEC_OID_RC2_CBC, NSS_USE_ALG_IN_SSL }, { CIPHER_NAME("RC4"), SEC_OID_RC4, NSS_USE_ALG_IN_SSL }, { CIPHER_NAME("IDEA"), SEC_OID_IDEA_CBC, NSS_USE_ALG_IN_SSL }, }; static const oidValDef kxOptList[] = { /* Key exchange */ { CIPHER_NAME("RSA"), SEC_OID_TLS_RSA, NSS_USE_ALG_IN_SSL_KX }, { CIPHER_NAME("RSA-EXPORT"), SEC_OID_TLS_RSA_EXPORT, NSS_USE_ALG_IN_SSL_KX }, { CIPHER_NAME("DHE-RSA"), SEC_OID_TLS_DHE_RSA, NSS_USE_ALG_IN_SSL_KX }, { CIPHER_NAME("DHE-DSS"), SEC_OID_TLS_DHE_DSS, NSS_USE_ALG_IN_SSL_KX }, { CIPHER_NAME("DH-RSA"), SEC_OID_TLS_DH_RSA, NSS_USE_ALG_IN_SSL_KX }, { CIPHER_NAME("DH-DSS"), SEC_OID_TLS_DH_DSS, NSS_USE_ALG_IN_SSL_KX }, { CIPHER_NAME("ECDHE-ECDSA"), SEC_OID_TLS_ECDHE_ECDSA, NSS_USE_ALG_IN_SSL_KX }, { CIPHER_NAME("ECDHE-RSA"), SEC_OID_TLS_ECDHE_RSA, NSS_USE_ALG_IN_SSL_KX }, { CIPHER_NAME("ECDH-ECDSA"), SEC_OID_TLS_ECDH_ECDSA, NSS_USE_ALG_IN_SSL_KX }, { CIPHER_NAME("ECDH-RSA"), SEC_OID_TLS_ECDH_RSA, NSS_USE_ALG_IN_SSL_KX }, }; static const oidValDef signOptList[] = { /* Signatures */ { CIPHER_NAME("DSA"), SEC_OID_ANSIX9_DSA_SIGNATURE, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_SIGNATURE }, { CIPHER_NAME("RSA-PKCS"), SEC_OID_PKCS1_RSA_ENCRYPTION, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_SIGNATURE }, { CIPHER_NAME("RSA-PSS"), SEC_OID_PKCS1_RSA_PSS_SIGNATURE, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_SIGNATURE }, { CIPHER_NAME("ECDSA"), SEC_OID_ANSIX962_EC_PUBLIC_KEY, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_SIGNATURE }, { CIPHER_NAME("ED25519"), SEC_OID_ED25519_PUBLIC_KEY, NSS_USE_ALG_IN_SIGNATURE }, }; typedef struct { const oidValDef *list; PRUint32 entries; const char *description; PRBool allowEmpty; } algListsDef; static const algListsDef algOptLists[] = { { curveOptList, PR_ARRAY_SIZE(curveOptList), "ECC", PR_FALSE }, { hashOptList, PR_ARRAY_SIZE(hashOptList), "HASH", PR_FALSE }, { macOptList, PR_ARRAY_SIZE(macOptList), "MAC", PR_FALSE }, { cipherOptList, PR_ARRAY_SIZE(cipherOptList), "CIPHER", PR_FALSE }, { kxOptList, PR_ARRAY_SIZE(kxOptList), "OTHER-KX", PR_FALSE }, { signOptList, PR_ARRAY_SIZE(signOptList), "OTHER-SIGN", PR_FALSE }, }; static const optionFreeDef sslOptList[] = { /* Versions */ { CIPHER_NAME("SSL2.0"), 0x002 }, { CIPHER_NAME("SSL3.0"), 0x300 }, { CIPHER_NAME("SSL3.1"), 0x301 }, { CIPHER_NAME("TLS1.0"), 0x301 }, { CIPHER_NAME("TLS1.1"), 0x302 }, { CIPHER_NAME("TLS1.2"), 0x303 }, { CIPHER_NAME("TLS1.3"), 0x304 }, { CIPHER_NAME("DTLS1.0"), 0x302 }, { CIPHER_NAME("DTLS1.1"), 0x302 }, { CIPHER_NAME("DTLS1.2"), 0x303 }, { CIPHER_NAME("DTLS1.3"), 0x304 }, }; static const optionFreeDef keySizeFlagsList[] = { { CIPHER_NAME("KEY-SIZE-SSL"), NSS_KEY_SIZE_POLICY_SSL_FLAG }, { CIPHER_NAME("KEY-SIZE-SIGN"), NSS_KEY_SIZE_POLICY_SIGN_FLAG }, { CIPHER_NAME("KEY-SIZE-VERIFY"), NSS_KEY_SIZE_POLICY_VERIFY_FLAG }, }; static const optionFreeDef freeOptList[] = { /* Restrictions for asymetric keys */ { CIPHER_NAME("RSA-MIN"), NSS_RSA_MIN_KEY_SIZE }, { CIPHER_NAME("DH-MIN"), NSS_DH_MIN_KEY_SIZE }, { CIPHER_NAME("DSA-MIN"), NSS_DSA_MIN_KEY_SIZE }, { CIPHER_NAME("ECC-MIN"), NSS_ECC_MIN_KEY_SIZE }, /* what operations doe the key size apply to */ { CIPHER_NAME("KEY-SIZE-FLAGS"), NSS_KEY_SIZE_POLICY_FLAGS }, /* constraints on SSL Protocols */ { CIPHER_NAME("TLS-VERSION-MIN"), NSS_TLS_VERSION_MIN_POLICY }, { CIPHER_NAME("TLS-VERSION-MAX"), NSS_TLS_VERSION_MAX_POLICY }, /* constraints on DTLS Protocols */ { CIPHER_NAME("DTLS-VERSION-MIN"), NSS_DTLS_VERSION_MIN_POLICY }, { CIPHER_NAME("DTLS-VERSION-MAX"), NSS_DTLS_VERSION_MAX_POLICY } }; static const policyFlagDef policyFlagList[] = { { CIPHER_NAME("SSL"), NSS_USE_ALG_IN_SSL }, { CIPHER_NAME("SSL-KEY-EXCHANGE"), NSS_USE_ALG_IN_SSL_KX }, /* add other key exhanges in the future */ { CIPHER_NAME("KEY-EXCHANGE"), NSS_USE_ALG_IN_SSL_KX }, { CIPHER_NAME("CERT-SIGNATURE"), NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("CMS-SIGNATURE"), NSS_USE_ALG_IN_CMS_SIGNATURE }, { CIPHER_NAME("ALL-SIGNATURE"), NSS_USE_ALG_IN_SIGNATURE }, /* sign turns off all signatures, but doesn't change the * allowance for specific sigantures... for example: * disallow=sha256/all allow=sha256/signature doesn't allow * cert-sigantures, where disallow=sha256/all allow=sha256/all-signature * does. * however, disallow=sha356/signature and disallow=sha256/all-siganture are * equivalent in effect */ { CIPHER_NAME("SIGNATURE"), NSS_USE_ALG_IN_ANY_SIGNATURE }, /* enable/disable everything */ { CIPHER_NAME("ALL"), NSS_USE_ALG_IN_SSL | NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_SIGNATURE }, { CIPHER_NAME("NONE"), 0 } }; /* * Get the next cipher on the list. point to the next one in 'next'. * return the length; */ static const char * secmod_ArgGetSubValue(const char *cipher, char sep1, char sep2, int *len, const char **next) { const char *start = cipher; if (start == NULL) { *len = 0; *next = NULL; return start; } for (; *cipher && *cipher != sep2; cipher++) { if (*cipher == sep1) { *next = cipher + 1; *len = cipher - start; return start; } } *next = NULL; *len = cipher - start; return start; } static PRUint32 secmod_parsePolicyValue(const char *policyFlags, int policyLength, PRBool printPolicyFeedback, PRUint32 policyCheckFlags) { const char *flag, *currentString; PRUint32 flags = 0; int i; for (currentString = policyFlags; currentString && currentString < policyFlags + policyLength;) { int length; PRBool unknown = PR_TRUE; flag = secmod_ArgGetSubValue(currentString, ',', ':', &length, ¤tString); if (length == 0) { continue; } for (i = 0; i < PR_ARRAY_SIZE(policyFlagList); i++) { const policyFlagDef *policy = &policyFlagList[i]; unsigned name_size = policy->name_size; if ((policy->name_size == length) && PORT_Strncasecmp(policy->name, flag, name_size) == 0) { flags |= policy->flag; unknown = PR_FALSE; break; } } if (unknown && printPolicyFeedback && (policyCheckFlags & SECMOD_FLAG_POLICY_CHECK_VALUE)) { PR_SetEnv("NSS_POLICY_FAIL=1"); fprintf(stderr, "NSS-POLICY-FAIL %.*s: unknown value: %.*s\n", policyLength, policyFlags, length, flag); } } return flags; } /* allow symbolic names for values. The only ones currently defines or * SSL protocol versions. */ static SECStatus secmod_getPolicyOptValue(const char *policyValue, int policyValueLength, PRInt32 *result) { PRInt32 val = atoi(policyValue); int i; if ((val != 0) || (*policyValue == '0')) { *result = val; return SECSuccess; } /* handle any ssl strings */ for (i = 0; i < PR_ARRAY_SIZE(sslOptList); i++) { if (policyValueLength == sslOptList[i].name_size && PORT_Strncasecmp(sslOptList[i].name, policyValue, sslOptList[i].name_size) == 0) { *result = sslOptList[i].option; return SECSuccess; } } /* handle key_size flags. Each flag represents a bit, which * gets or'd together. They can be separated by , | or + */ val = 0; while (*policyValue) { PRBool found = PR_FALSE; for (i = 0; i < PR_ARRAY_SIZE(keySizeFlagsList); i++) { if (PORT_Strncasecmp(keySizeFlagsList[i].name, policyValue, keySizeFlagsList[i].name_size) == 0) { val |= keySizeFlagsList[i].option; found = PR_TRUE; policyValue += keySizeFlagsList[i].name_size; break; } } if (!found) { return SECFailure; } if (*policyValue == ',' || *policyValue == '|' || *policyValue == '+') { policyValue++; } } *result = val; return SECSuccess; } /* Policy operations: * Disallow: operation is disallowed by policy. Implies disabled. * Allow: operation is allowed by policy (but could be disabled). * Disable: operation is turned off by default (but could be allowed). * Enable: operation is enabled by default. Implies allowed. */ typedef enum { NSS_DISALLOW, NSS_ALLOW, NSS_DISABLE, NSS_ENABLE } NSSPolicyOperation; /* apply the operator specific policy */ SECStatus secmod_setPolicyOperation(SECOidTag oid, NSSPolicyOperation operation, PRUint32 value) { SECStatus rv = SECSuccess; switch (operation) { case NSS_DISALLOW: /* clear the requested policy bits */ rv = NSS_SetAlgorithmPolicy(oid, 0, value); break; case NSS_ALLOW: /* set the requested policy bits */ rv = NSS_SetAlgorithmPolicy(oid, value, 0); break; /* enable/disable only apply to SSL cipher suites (future S/MIME). * Enable/disable is implemented by clearing the DEFAULT_NOT_VALID * flag, then setting the NSS_USE_DEFAULT_SSL_ENABLE flag to the * correct value. The ssl policy code will then sort out what to * set based on ciphers and cipher suite values.*/ case NSS_DISABLE: if (value & (NSS_USE_ALG_IN_SSL | NSS_USE_ALG_IN_SSL_KX)) { /* clear not valid and enable */ rv = NSS_SetAlgorithmPolicy(oid, 0, NSS_USE_DEFAULT_NOT_VALID | NSS_USE_DEFAULT_SSL_ENABLE); } break; case NSS_ENABLE: if (value & (NSS_USE_ALG_IN_SSL | NSS_USE_ALG_IN_SSL_KX)) { /* set enable, clear not valid. NOTE: enable implies allow! */ rv = NSS_SetAlgorithmPolicy(oid, value | NSS_USE_DEFAULT_SSL_ENABLE, NSS_USE_DEFAULT_NOT_VALID); } break; default: PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); rv = SECFailure; break; } return rv; } const char * secmod_getOperationString(NSSPolicyOperation operation) { switch (operation) { case NSS_DISALLOW: return "disallow"; case NSS_ALLOW: return "allow"; case NSS_DISABLE: return "disable"; case NSS_ENABLE: return "enable"; default: break; } return "invalid"; } static SECStatus secmod_applyCryptoPolicy(const char *policyString, NSSPolicyOperation operation, PRBool printPolicyFeedback, PRUint32 policyCheckFlags) { const char *cipher, *currentString; unsigned i, j; SECStatus rv = SECSuccess; PRBool unknown; if (policyString == NULL || policyString[0] == 0) { return SECSuccess; /* do nothing */ } /* if we change any of these, make sure it gets applied in ssl as well */ NSS_SetAlgorithmPolicy(SEC_OID_APPLY_SSL_POLICY, NSS_USE_POLICY_IN_SSL, 0); for (currentString = policyString; currentString;) { int length; PRBool newValue = PR_FALSE; cipher = secmod_ArgGetSubValue(currentString, ':', 0, &length, ¤tString); unknown = PR_TRUE; if (length >= 3 && cipher[3] == '/') { newValue = PR_TRUE; } if ((newValue || (length == 3)) && PORT_Strncasecmp(cipher, "all", 3) == 0) { /* disable or enable all options by default */ PRUint32 value = 0; if (newValue) { value = secmod_parsePolicyValue(&cipher[3] + 1, length - 3 - 1, printPolicyFeedback, policyCheckFlags); } for (i = 0; i < PR_ARRAY_SIZE(algOptLists); i++) { const algListsDef *algOptList = &algOptLists[i]; for (j = 0; j < algOptList->entries; j++) { if (!newValue) { value = algOptList->list[j].val; } secmod_setPolicyOperation(algOptList->list[j].oid, operation, value); } } continue; } for (i = 0; i < PR_ARRAY_SIZE(algOptLists); i++) { const algListsDef *algOptList = &algOptLists[i]; for (j = 0; j < algOptList->entries; j++) { const oidValDef *algOpt = &algOptList->list[j]; unsigned name_size = algOpt->name_size; PRBool newOption = PR_FALSE; if ((length >= name_size) && (cipher[name_size] == '/')) { newOption = PR_TRUE; } if ((newOption || algOpt->name_size == length) && PORT_Strncasecmp(algOpt->name, cipher, name_size) == 0) { PRUint32 value = algOpt->val; if (newOption) { value = secmod_parsePolicyValue(&cipher[name_size] + 1, length - name_size - 1, printPolicyFeedback, policyCheckFlags); } rv = secmod_setPolicyOperation(algOptList->list[j].oid, operation, value); if (rv != SECSuccess) { /* could not enable option */ /* NSS_SetAlgorithPolicy should have set the error code */ return SECFailure; } unknown = PR_FALSE; break; } } } if (!unknown) { continue; } for (i = 0; i < PR_ARRAY_SIZE(freeOptList); i++) { const optionFreeDef *freeOpt = &freeOptList[i]; unsigned name_size = freeOpt->name_size; if ((length > name_size) && cipher[name_size] == '=' && PORT_Strncasecmp(freeOpt->name, cipher, name_size) == 0) { PRInt32 val; const char *policyValue = &cipher[name_size + 1]; int policyValueLength = length - name_size - 1; rv = secmod_getPolicyOptValue(policyValue, policyValueLength, &val); if (rv != SECSuccess) { if (printPolicyFeedback && (policyCheckFlags & SECMOD_FLAG_POLICY_CHECK_VALUE)) { PR_SetEnv("NSS_POLICY_FAIL=1"); fprintf(stderr, "NSS-POLICY-FAIL %.*s: unknown value: %.*s\n", length, cipher, policyValueLength, policyValue); } return SECFailure; } rv = NSS_OptionSet(freeOpt->option, val); if (rv != SECSuccess) { /* could not enable option */ /* NSS_OptionSet should have set the error code */ return SECFailure; } /* to allow the policy to expand in the future. ignore ciphers * we don't understand */ unknown = PR_FALSE; break; } } if (unknown && printPolicyFeedback && (policyCheckFlags & SECMOD_FLAG_POLICY_CHECK_IDENTIFIER)) { PR_SetEnv("NSS_POLICY_FAIL=1"); fprintf(stderr, "NSS-POLICY-FAIL %s: unknown identifier: %.*s\n", secmod_getOperationString(operation), length, cipher); } } return rv; } static void secmod_sanityCheckCryptoPolicy(void) { unsigned i, j; SECStatus rv = SECSuccess; unsigned num_kx_enabled = 0; unsigned num_ssl_enabled = 0; unsigned num_sig_enabled = 0; unsigned enabledCount[PR_ARRAY_SIZE(algOptLists)]; const char *sWarn = "WARN"; const char *sInfo = "INFO"; PRBool haveWarning = PR_FALSE; for (i = 0; i < PR_ARRAY_SIZE(algOptLists); i++) { const algListsDef *algOptList = &algOptLists[i]; enabledCount[i] = 0; for (j = 0; j < algOptList->entries; j++) { const oidValDef *algOpt = &algOptList->list[j]; PRUint32 value; PRBool anyEnabled = PR_FALSE; rv = NSS_GetAlgorithmPolicy(algOpt->oid, &value); if (rv != SECSuccess) { PR_SetEnv("NSS_POLICY_FAIL=1"); fprintf(stderr, "NSS-POLICY-FAIL: internal failure with NSS_GetAlgorithmPolicy at %u\n", i); return; } if ((algOpt->val & NSS_USE_ALG_IN_SSL_KX) && (value & NSS_USE_ALG_IN_SSL_KX)) { ++num_kx_enabled; anyEnabled = PR_TRUE; fprintf(stderr, "NSS-POLICY-INFO: %s is enabled for KX\n", algOpt->name); } if ((algOpt->val & NSS_USE_ALG_IN_SSL) && (value & NSS_USE_ALG_IN_SSL)) { ++num_ssl_enabled; anyEnabled = PR_TRUE; fprintf(stderr, "NSS-POLICY-INFO: %s is enabled for SSL\n", algOpt->name); } if ((algOpt->val & NSS_USE_ALG_IN_CERT_SIGNATURE) && ((value & NSS_USE_CERT_SIGNATURE_OK) == NSS_USE_CERT_SIGNATURE_OK)) { ++num_sig_enabled; anyEnabled = PR_TRUE; fprintf(stderr, "NSS-POLICY-INFO: %s is enabled for CERT-SIGNATURE\n", algOpt->name); } if (anyEnabled) { ++enabledCount[i]; } } } fprintf(stderr, "NSS-POLICY-%s: NUMBER-OF-SSL-ALG-KX: %u\n", num_kx_enabled ? sInfo : sWarn, num_kx_enabled); fprintf(stderr, "NSS-POLICY-%s: NUMBER-OF-SSL-ALG: %u\n", num_ssl_enabled ? sInfo : sWarn, num_ssl_enabled); fprintf(stderr, "NSS-POLICY-%s: NUMBER-OF-CERT-SIG: %u\n", num_sig_enabled ? sInfo : sWarn, num_sig_enabled); if (!num_kx_enabled || !num_ssl_enabled || !num_sig_enabled) { haveWarning = PR_TRUE; } for (i = 0; i < PR_ARRAY_SIZE(algOptLists); i++) { const algListsDef *algOptList = &algOptLists[i]; fprintf(stderr, "NSS-POLICY-%s: NUMBER-OF-%s: %u\n", enabledCount[i] ? sInfo : sWarn, algOptList->description, enabledCount[i]); if (!enabledCount[i] && !algOptList->allowEmpty) { haveWarning = PR_TRUE; } } if (haveWarning) { PR_SetEnv("NSS_POLICY_WARN=1"); } } static SECStatus secmod_parseCryptoPolicy(const char *policyConfig, PRBool printPolicyFeedback, PRUint32 policyCheckFlags) { char *args; SECStatus rv; if (policyConfig == NULL) { return SECSuccess; /* no policy given */ } /* make sure we initialize the oid table and set all the default policy * values first so we can override them here */ rv = SECOID_Init(); if (rv != SECSuccess) { return rv; } args = NSSUTIL_ArgGetParamValue("disallow", policyConfig); rv = secmod_applyCryptoPolicy(args, NSS_DISALLOW, printPolicyFeedback, policyCheckFlags); if (args) PORT_Free(args); if (rv != SECSuccess) { return rv; } args = NSSUTIL_ArgGetParamValue("allow", policyConfig); rv = secmod_applyCryptoPolicy(args, NSS_ALLOW, printPolicyFeedback, policyCheckFlags); if (args) PORT_Free(args); if (rv != SECSuccess) { return rv; } args = NSSUTIL_ArgGetParamValue("disable", policyConfig); rv = secmod_applyCryptoPolicy(args, NSS_DISABLE, printPolicyFeedback, policyCheckFlags); if (args) PORT_Free(args); if (rv != SECSuccess) { return rv; } args = NSSUTIL_ArgGetParamValue("enable", policyConfig); rv = secmod_applyCryptoPolicy(args, NSS_ENABLE, printPolicyFeedback, policyCheckFlags); if (args) PORT_Free(args); if (rv != SECSuccess) { return rv; } /* this has to be last. Everything after this will be a noop */ if (NSSUTIL_ArgHasFlag("flags", "ssl-lock", policyConfig)) { PRInt32 locks; /* don't overwrite other (future) lock flags */ rv = NSS_OptionGet(NSS_DEFAULT_LOCKS, &locks); if (rv == SECSuccess) { rv = NSS_OptionSet(NSS_DEFAULT_LOCKS, locks | NSS_DEFAULT_SSL_LOCK); } if (rv != SECSuccess) { return rv; } } if (NSSUTIL_ArgHasFlag("flags", "policy-lock", policyConfig)) { NSS_LockPolicy(); } if (printPolicyFeedback) { /* This helps to distinguish configurations that don't contain any * policy config= statement. */ PR_SetEnv("NSS_POLICY_LOADED=1"); fprintf(stderr, "NSS-POLICY-INFO: LOADED-SUCCESSFULLY\n"); secmod_sanityCheckCryptoPolicy(); } return rv; } static PRUint32 secmod_parsePolicyCheckFlags(const char *nss) { PRUint32 policyCheckFlags = 0; if (NSSUTIL_ArgHasFlag("flags", "policyCheckIdentifier", nss)) { policyCheckFlags |= SECMOD_FLAG_POLICY_CHECK_IDENTIFIER; } if (NSSUTIL_ArgHasFlag("flags", "policyCheckValue", nss)) { policyCheckFlags |= SECMOD_FLAG_POLICY_CHECK_VALUE; } return policyCheckFlags; } /* * for 3.4 we continue to use the old SECMODModule structure */ SECMODModule * SECMOD_CreateModuleEx(const char *library, const char *moduleName, const char *parameters, const char *nss, const char *config) { SECMODModule *mod; SECStatus rv; char *slotParams, *ciphers; PRBool printPolicyFeedback = NSSUTIL_ArgHasFlag("flags", "printPolicyFeedback", nss); PRUint32 policyCheckFlags = secmod_parsePolicyCheckFlags(nss); rv = secmod_parseCryptoPolicy(config, printPolicyFeedback, policyCheckFlags); /* do not load the module if policy parsing fails */ if (rv != SECSuccess) { if (printPolicyFeedback) { PR_SetEnv("NSS_POLICY_FAIL=1"); fprintf(stderr, "NSS-POLICY-FAIL: policy config parsing failed, not loading module %s\n", moduleName); } return NULL; } mod = secmod_NewModule(); if (mod == NULL) return NULL; mod->commonName = PORT_ArenaStrdup(mod->arena, moduleName ? moduleName : ""); if (library) { mod->dllName = PORT_ArenaStrdup(mod->arena, library); } /* new field */ if (parameters) { mod->libraryParams = PORT_ArenaStrdup(mod->arena, parameters); } mod->internal = NSSUTIL_ArgHasFlag("flags", "internal", nss); mod->isFIPS = NSSUTIL_ArgHasFlag("flags", "FIPS", nss); /* if the system FIPS mode is enabled, force FIPS to be on */ if (SECMOD_GetSystemFIPSEnabled()) { mod->isFIPS = PR_TRUE; } mod->isCritical = NSSUTIL_ArgHasFlag("flags", "critical", nss); slotParams = NSSUTIL_ArgGetParamValue("slotParams", nss); mod->slotInfo = NSSUTIL_ArgParseSlotInfo(mod->arena, slotParams, &mod->slotInfoCount); if (slotParams) PORT_Free(slotParams); /* new field */ mod->trustOrder = NSSUTIL_ArgReadLong("trustOrder", nss, NSSUTIL_DEFAULT_TRUST_ORDER, NULL); /* new field */ mod->cipherOrder = NSSUTIL_ArgReadLong("cipherOrder", nss, NSSUTIL_DEFAULT_CIPHER_ORDER, NULL); /* new field */ mod->isModuleDB = NSSUTIL_ArgHasFlag("flags", "moduleDB", nss); mod->moduleDBOnly = NSSUTIL_ArgHasFlag("flags", "moduleDBOnly", nss); if (mod->moduleDBOnly) mod->isModuleDB = PR_TRUE; /* we need more bits, but we also want to preserve binary compatibility * so we overload the isModuleDB PRBool with additional flags. * These flags are only valid if mod->isModuleDB is already set. * NOTE: this depends on the fact that PRBool is at least a char on * all platforms. These flags are only valid if moduleDB is set, so * code checking if (mod->isModuleDB) will continue to work correctly. */ if (mod->isModuleDB) { char flags = SECMOD_FLAG_MODULE_DB_IS_MODULE_DB; if (NSSUTIL_ArgHasFlag("flags", "skipFirst", nss)) { flags |= SECMOD_FLAG_MODULE_DB_SKIP_FIRST; } if (NSSUTIL_ArgHasFlag("flags", "defaultModDB", nss)) { flags |= SECMOD_FLAG_MODULE_DB_DEFAULT_MODDB; } if (NSSUTIL_ArgHasFlag("flags", "policyOnly", nss)) { flags |= SECMOD_FLAG_MODULE_DB_POLICY_ONLY; } /* additional moduleDB flags could be added here in the future */ mod->isModuleDB = (PRBool)flags; } if (mod->internal) { char flags = SECMOD_FLAG_INTERNAL_IS_INTERNAL; if (NSSUTIL_ArgHasFlag("flags", "internalKeySlot", nss)) { flags |= SECMOD_FLAG_INTERNAL_KEY_SLOT; } mod->internal = (PRBool)flags; } ciphers = NSSUTIL_ArgGetParamValue("ciphers", nss); NSSUTIL_ArgParseCipherFlags(&mod->ssl[0], ciphers); if (ciphers) PORT_Free(ciphers); secmod_PrivateModuleCount++; return mod; } PRBool SECMOD_GetSkipFirstFlag(SECMODModule *mod) { char flags = (char)mod->isModuleDB; return (flags & SECMOD_FLAG_MODULE_DB_SKIP_FIRST) ? PR_TRUE : PR_FALSE; } PRBool SECMOD_GetDefaultModDBFlag(SECMODModule *mod) { char flags = (char)mod->isModuleDB; return (flags & SECMOD_FLAG_MODULE_DB_DEFAULT_MODDB) ? PR_TRUE : PR_FALSE; } PRBool secmod_PolicyOnly(SECMODModule *mod) { char flags = (char)mod->isModuleDB; return (flags & SECMOD_FLAG_MODULE_DB_POLICY_ONLY) ? PR_TRUE : PR_FALSE; } PRBool secmod_IsInternalKeySlot(SECMODModule *mod) { char flags = (char)mod->internal; return (flags & SECMOD_FLAG_INTERNAL_KEY_SLOT) ? PR_TRUE : PR_FALSE; } void secmod_SetInternalKeySlotFlag(SECMODModule *mod, PRBool val) { char flags = (char)mod->internal; if (val) { flags |= SECMOD_FLAG_INTERNAL_KEY_SLOT; } else { flags &= ~SECMOD_FLAG_INTERNAL_KEY_SLOT; } mod->internal = flags; } /* * copy desc and value into target. Target is known to be big enough to * hold desc +2 +value, which is good because the result of this will be * *desc"*value". We may, however, have to add some escapes for special * characters imbedded into value (rare). This string potentially comes from * a user, so we don't want the user overflowing the target buffer by using * excessive escapes. To prevent this we count the escapes we need to add and * try to expand the buffer with Realloc. */ static char * secmod_doDescCopy(char *target, char **base, int *baseLen, const char *desc, int descLen, char *value) { int diff, esc_len; esc_len = NSSUTIL_EscapeSize(value, '\"') - 1; diff = esc_len - strlen(value); if (diff > 0) { /* we need to escape... expand newSpecPtr as well to make sure * we don't overflow it */ int offset = target - *base; char *newPtr = PORT_Realloc(*base, *baseLen + diff); if (!newPtr) { return target; /* not enough space, just drop the whole copy */ } *baseLen += diff; target = newPtr + offset; *base = newPtr; value = NSSUTIL_Escape(value, '\"'); if (value == NULL) { return target; /* couldn't escape value, just drop the copy */ } } PORT_Memcpy(target, desc, descLen); target += descLen; *target++ = '\"'; PORT_Memcpy(target, value, esc_len); target += esc_len; *target++ = '\"'; if (diff > 0) { PORT_Free(value); } return target; } #define SECMOD_SPEC_COPY(new, start, end) \ if (end > start) { \ int _cnt = end - start; \ PORT_Memcpy(new, start, _cnt); \ new += _cnt; \ } #define SECMOD_TOKEN_DESCRIPTION "tokenDescription=" #define SECMOD_SLOT_DESCRIPTION "slotDescription=" /* * Find any tokens= values in the module spec. * Always return a new spec which does not have any tokens= arguments. * If tokens= arguments are found, Split the the various tokens defined into * an array of child specs to return. * * Caller is responsible for freeing the child spec and the new token * spec. */ char * secmod_ParseModuleSpecForTokens(PRBool convert, PRBool isFIPS, const char *moduleSpec, char ***children, CK_SLOT_ID **ids) { int newSpecLen = PORT_Strlen(moduleSpec) + 2; char *newSpec = PORT_Alloc(newSpecLen); char *newSpecPtr = newSpec; const char *modulePrev = moduleSpec; char *target = NULL; char *tmp = NULL; char **childArray = NULL; const char *tokenIndex; CK_SLOT_ID *idArray = NULL; int tokenCount = 0; int i; if (newSpec == NULL) { return NULL; } *children = NULL; if (ids) { *ids = NULL; } moduleSpec = NSSUTIL_ArgStrip(moduleSpec); SECMOD_SPEC_COPY(newSpecPtr, modulePrev, moduleSpec); /* Notes on 'convert' and 'isFIPS' flags: The base parameters for opening * a new softoken module takes the following parameters to name the * various tokens: * * cryptoTokenDescription: name of the non-fips crypto token. * cryptoSlotDescription: name of the non-fips crypto slot. * dbTokenDescription: name of the non-fips db token. * dbSlotDescription: name of the non-fips db slot. * FIPSTokenDescription: name of the fips db/crypto token. * FIPSSlotDescription: name of the fips db/crypto slot. * * if we are opening a new slot, we need to have the following * parameters: * tokenDescription: name of the token. * slotDescription: name of the slot. * * * The convert flag tells us to drop the unnecessary *TokenDescription * and *SlotDescription arguments and convert the appropriate pair * (either db or FIPS based on the isFIPS flag) to tokenDescription and * slotDescription). */ /* * walk down the list. if we find a tokens= argument, save it, * otherise copy the argument. */ while (*moduleSpec) { int next; modulePrev = moduleSpec; NSSUTIL_HANDLE_STRING_ARG(moduleSpec, target, "tokens=", modulePrev = moduleSpec; /* skip copying */) NSSUTIL_HANDLE_STRING_ARG( moduleSpec, tmp, "cryptoTokenDescription=", if (convert) { modulePrev = moduleSpec; }) NSSUTIL_HANDLE_STRING_ARG( moduleSpec, tmp, "cryptoSlotDescription=", if (convert) { modulePrev = moduleSpec; }) NSSUTIL_HANDLE_STRING_ARG( moduleSpec, tmp, "dbTokenDescription=", if (convert) { modulePrev = moduleSpec; if (!isFIPS) { newSpecPtr = secmod_doDescCopy(newSpecPtr, &newSpec, &newSpecLen, SECMOD_TOKEN_DESCRIPTION, sizeof(SECMOD_TOKEN_DESCRIPTION) - 1, tmp); } }) NSSUTIL_HANDLE_STRING_ARG( moduleSpec, tmp, "dbSlotDescription=", if (convert) { modulePrev = moduleSpec; /* skip copying */ if (!isFIPS) { newSpecPtr = secmod_doDescCopy(newSpecPtr, &newSpec, &newSpecLen, SECMOD_SLOT_DESCRIPTION, sizeof(SECMOD_SLOT_DESCRIPTION) - 1, tmp); } }) NSSUTIL_HANDLE_STRING_ARG( moduleSpec, tmp, "FIPSTokenDescription=", if (convert) { modulePrev = moduleSpec; /* skip copying */ if (isFIPS) { newSpecPtr = secmod_doDescCopy(newSpecPtr, &newSpec, &newSpecLen, SECMOD_TOKEN_DESCRIPTION, sizeof(SECMOD_TOKEN_DESCRIPTION) - 1, tmp); } }) NSSUTIL_HANDLE_STRING_ARG( moduleSpec, tmp, "FIPSSlotDescription=", if (convert) { modulePrev = moduleSpec; /* skip copying */ if (isFIPS) { newSpecPtr = secmod_doDescCopy(newSpecPtr, &newSpec, &newSpecLen, SECMOD_SLOT_DESCRIPTION, sizeof(SECMOD_SLOT_DESCRIPTION) - 1, tmp); } }) NSSUTIL_HANDLE_FINAL_ARG(moduleSpec) SECMOD_SPEC_COPY(newSpecPtr, modulePrev, moduleSpec); } if (tmp) { PORT_Free(tmp); tmp = NULL; } *newSpecPtr = 0; /* no target found, return the newSpec */ if (target == NULL) { return newSpec; } /* now build the child array from target */ /*first count them */ for (tokenIndex = NSSUTIL_ArgStrip(target); *tokenIndex; tokenIndex = NSSUTIL_ArgStrip(NSSUTIL_ArgSkipParameter(tokenIndex))) { tokenCount++; } childArray = PORT_NewArray(char *, tokenCount + 1); if (childArray == NULL) { /* just return the spec as is then */ PORT_Free(target); return newSpec; } if (ids) { idArray = PORT_NewArray(CK_SLOT_ID, tokenCount + 1); if (idArray == NULL) { PORT_Free(childArray); PORT_Free(target); return newSpec; } } /* now fill them in */ for (tokenIndex = NSSUTIL_ArgStrip(target), i = 0; *tokenIndex && (i < tokenCount); tokenIndex = NSSUTIL_ArgStrip(tokenIndex)) { int next; char *name = NSSUTIL_ArgGetLabel(tokenIndex, &next); tokenIndex += next; if (idArray) { idArray[i] = NSSUTIL_ArgDecodeNumber(name); } PORT_Free(name); /* drop the explicit number */ /* if anything is left, copy the args to the child array */ if (!NSSUTIL_ArgIsBlank(*tokenIndex)) { childArray[i++] = NSSUTIL_ArgFetchValue(tokenIndex, &next); tokenIndex += next; } } PORT_Free(target); childArray[i] = 0; if (idArray) { idArray[i] = 0; } /* return it */ *children = childArray; if (ids) { *ids = idArray; } return newSpec; } /* get the database and flags from the spec */ static char * secmod_getConfigDir(const char *spec, char **certPrefix, char **keyPrefix, PRBool *readOnly) { char *config = NULL; *certPrefix = NULL; *keyPrefix = NULL; *readOnly = NSSUTIL_ArgHasFlag("flags", "readOnly", spec); if (NSSUTIL_ArgHasFlag("flags", "nocertdb", spec) || NSSUTIL_ArgHasFlag("flags", "nokeydb", spec)) { return NULL; } spec = NSSUTIL_ArgStrip(spec); while (*spec) { int next; NSSUTIL_HANDLE_STRING_ARG(spec, config, "configdir=", ;) NSSUTIL_HANDLE_STRING_ARG(spec, *certPrefix, "certPrefix=", ;) NSSUTIL_HANDLE_STRING_ARG(spec, *keyPrefix, "keyPrefix=", ;) NSSUTIL_HANDLE_FINAL_ARG(spec) } return config; } struct SECMODConfigListStr { char *config; char *certPrefix; char *keyPrefix; PRBool isReadOnly; }; /* * return an array of already openned databases from a spec list. */ SECMODConfigList * secmod_GetConfigList(PRBool isFIPS, char *spec, int *count) { char **children; CK_SLOT_ID *ids; char *strippedSpec; int childCount; SECMODConfigList *conflist = NULL; int i; strippedSpec = secmod_ParseModuleSpecForTokens(PR_TRUE, isFIPS, spec, &children, &ids); if (strippedSpec == NULL) { return NULL; } for (childCount = 0; children && children[childCount]; childCount++) ; *count = childCount + 1; /* include strippedSpec */ conflist = PORT_NewArray(SECMODConfigList, *count); if (conflist == NULL) { *count = 0; goto loser; } conflist[0].config = secmod_getConfigDir(strippedSpec, &conflist[0].certPrefix, &conflist[0].keyPrefix, &conflist[0].isReadOnly); for (i = 0; i < childCount; i++) { conflist[i + 1].config = secmod_getConfigDir(children[i], &conflist[i + 1].certPrefix, &conflist[i + 1].keyPrefix, &conflist[i + 1].isReadOnly); } loser: secmod_FreeChildren(children, ids); PORT_Free(strippedSpec); return conflist; } /* * determine if we are trying to open an old dbm database. For this test * RDB databases should return PR_FALSE. */ static PRBool secmod_configIsDBM(char *configDir) { char *env; /* explicit dbm open */ if (strncmp(configDir, "dbm:", 4) == 0) { return PR_TRUE; } /* explicit open of a non-dbm database */ if ((strncmp(configDir, "sql:", 4) == 0) || (strncmp(configDir, "rdb:", 4) == 0) || (strncmp(configDir, "extern:", 7) == 0)) { return PR_FALSE; } env = PR_GetEnvSecure("NSS_DEFAULT_DB_TYPE"); /* implicit dbm open */ if ((env == NULL) || (strcmp(env, "dbm") == 0)) { return PR_TRUE; } /* implicit non-dbm open */ return PR_FALSE; } /* * match two prefixes. prefix may be NULL. NULL patches '\0' */ static PRBool secmod_matchPrefix(char *prefix1, char *prefix2) { if ((prefix1 == NULL) || (*prefix1 == 0)) { if ((prefix2 == NULL) || (*prefix2 == 0)) { return PR_TRUE; } return PR_FALSE; } if (strcmp(prefix1, prefix2) == 0) { return PR_TRUE; } return PR_FALSE; } /* do two config paramters match? Not all callers are compariing * SECMODConfigLists directly, so this function breaks them out to their * components. */ static PRBool secmod_matchConfig(char *configDir1, char *configDir2, char *certPrefix1, char *certPrefix2, char *keyPrefix1, char *keyPrefix2, PRBool isReadOnly1, PRBool isReadOnly2) { /* TODO: Document the answer to the question: * "Why not allow them to match if they are both NULL?" * See: https://bugzilla.mozilla.org/show_bug.cgi?id=1318633#c1 */ if ((configDir1 == NULL) || (configDir2 == NULL)) { return PR_FALSE; } if (strcmp(configDir1, configDir2) != 0) { return PR_FALSE; } if (!secmod_matchPrefix(certPrefix1, certPrefix2)) { return PR_FALSE; } if (!secmod_matchPrefix(keyPrefix1, keyPrefix2)) { return PR_FALSE; } /* these last test -- if we just need the DB open read only, * than any open will suffice, but if we requested it read/write * and it's only open read only, we need to open it again */ if (isReadOnly1) { return PR_TRUE; } if (isReadOnly2) { /* isReadonly1 == PR_FALSE */ return PR_FALSE; } return PR_TRUE; } /* * return true if we are requesting a database that is already openned. */ PRBool secmod_MatchConfigList(const char *spec, SECMODConfigList *conflist, int count) { char *config; char *certPrefix; char *keyPrefix; PRBool isReadOnly; PRBool ret = PR_FALSE; int i; config = secmod_getConfigDir(spec, &certPrefix, &keyPrefix, &isReadOnly); if (!config) { goto done; } /* NOTE: we dbm isn't multiple open safe. If we open the same database * twice from two different locations, then we can corrupt our database * (the cache will be inconsistent). Protect against this by claiming * for comparison only that we are always openning dbm databases read only. */ if (secmod_configIsDBM(config)) { isReadOnly = 1; } for (i = 0; i < count; i++) { if (secmod_matchConfig(config, conflist[i].config, certPrefix, conflist[i].certPrefix, keyPrefix, conflist[i].keyPrefix, isReadOnly, conflist[i].isReadOnly)) { ret = PR_TRUE; goto done; } } ret = PR_FALSE; done: PORT_Free(config); PORT_Free(certPrefix); PORT_Free(keyPrefix); return ret; } /* * Find the slot id from the module spec. If the slot is the database slot, we * can get the slot id from the default database slot. */ CK_SLOT_ID secmod_GetSlotIDFromModuleSpec(const char *moduleSpec, SECMODModule *module) { char *tmp_spec = NULL; char **children, **thisChild; CK_SLOT_ID *ids, *thisID, slotID = -1; char *inConfig = NULL, *thisConfig = NULL; char *inCertPrefix = NULL, *thisCertPrefix = NULL; char *inKeyPrefix = NULL, *thisKeyPrefix = NULL; PRBool inReadOnly, thisReadOnly; inConfig = secmod_getConfigDir(moduleSpec, &inCertPrefix, &inKeyPrefix, &inReadOnly); if (!inConfig) { goto done; } if (secmod_configIsDBM(inConfig)) { inReadOnly = 1; } tmp_spec = secmod_ParseModuleSpecForTokens(PR_TRUE, module->isFIPS, module->libraryParams, &children, &ids); if (tmp_spec == NULL) { goto done; } /* first check to see if the parent is the database */ thisConfig = secmod_getConfigDir(tmp_spec, &thisCertPrefix, &thisKeyPrefix, &thisReadOnly); if (!thisConfig) { goto done; } if (secmod_matchConfig(inConfig, thisConfig, inCertPrefix, thisCertPrefix, inKeyPrefix, thisKeyPrefix, inReadOnly, thisReadOnly)) { /* yup it's the default key slot, get the id for it */ PK11SlotInfo *slot = PK11_GetInternalKeySlot(); if (slot) { slotID = slot->slotID; PK11_FreeSlot(slot); } goto done; } /* find id of the token */ for (thisChild = children, thisID = ids; thisChild && *thisChild; thisChild++, thisID++) { PORT_Free(thisConfig); PORT_Free(thisCertPrefix); PORT_Free(thisKeyPrefix); thisConfig = secmod_getConfigDir(*thisChild, &thisCertPrefix, &thisKeyPrefix, &thisReadOnly); if (thisConfig == NULL) { continue; } if (secmod_matchConfig(inConfig, thisConfig, inCertPrefix, thisCertPrefix, inKeyPrefix, thisKeyPrefix, inReadOnly, thisReadOnly)) { slotID = *thisID; break; } } done: PORT_Free(inConfig); PORT_Free(inCertPrefix); PORT_Free(inKeyPrefix); PORT_Free(thisConfig); PORT_Free(thisCertPrefix); PORT_Free(thisKeyPrefix); if (tmp_spec) { secmod_FreeChildren(children, ids); PORT_Free(tmp_spec); } return slotID; } void secmod_FreeConfigList(SECMODConfigList *conflist, int count) { int i; for (i = 0; i < count; i++) { PORT_Free(conflist[i].config); PORT_Free(conflist[i].certPrefix); PORT_Free(conflist[i].keyPrefix); } PORT_Free(conflist); } void secmod_FreeChildren(char **children, CK_SLOT_ID *ids) { char **thisChild; if (!children) { return; } for (thisChild = children; thisChild && *thisChild; thisChild++) { PORT_Free(*thisChild); } PORT_Free(children); if (ids) { PORT_Free(ids); } return; } /* * caclulate the length of each child record: * " 0x{id}=<{escaped_child}>" */ static int secmod_getChildLength(char *child, CK_SLOT_ID id) { int length = NSSUTIL_DoubleEscapeSize(child, '>', ']'); if (id == 0) { length++; } while (id) { length++; id = id >> 4; } length += 6; /* {sp}0x[id]=<{child}> */ return length; } /* * Build a child record: * " 0x{id}=<{escaped_child}>" */ static SECStatus secmod_mkTokenChild(char **next, int *length, char *child, CK_SLOT_ID id) { int len; char *escSpec; len = PR_snprintf(*next, *length, " 0x%x=<", id); if (len < 0) { return SECFailure; } *next += len; *length -= len; escSpec = NSSUTIL_DoubleEscape(child, '>', ']'); if (escSpec == NULL) { return SECFailure; } if (*child && (*escSpec == 0)) { PORT_Free(escSpec); return SECFailure; } len = strlen(escSpec); if (len + 1 > *length) { PORT_Free(escSpec); return SECFailure; } PORT_Memcpy(*next, escSpec, len); *next += len; *length -= len; PORT_Free(escSpec); **next = '>'; (*next)++; (*length)--; return SECSuccess; } #define TOKEN_STRING " tokens=[" char * secmod_MkAppendTokensList(PLArenaPool *arena, char *oldParam, char *newToken, CK_SLOT_ID newID, char **children, CK_SLOT_ID *ids) { char *rawParam = NULL; /* oldParam with tokens stripped off */ char *newParam = NULL; /* space for the return parameter */ char *nextParam = NULL; /* current end of the new parameter */ char **oldChildren = NULL; CK_SLOT_ID *oldIds = NULL; void *mark = NULL; /* mark the arena pool in case we need * to release it */ int length, i, tmpLen; SECStatus rv; /* first strip out and save the old tokenlist */ rawParam = secmod_ParseModuleSpecForTokens(PR_FALSE, PR_FALSE, oldParam, &oldChildren, &oldIds); if (!rawParam) { goto loser; } /* now calculate the total length of the new buffer */ /* First the 'fixed stuff', length of rawparam (does not include a NULL), * length of the token string (does include the NULL), closing bracket */ length = strlen(rawParam) + sizeof(TOKEN_STRING) + 1; /* now add then length of all the old children */ for (i = 0; oldChildren && oldChildren[i]; i++) { length += secmod_getChildLength(oldChildren[i], oldIds[i]); } /* add the new token */ length += secmod_getChildLength(newToken, newID); /* and it's new children */ for (i = 0; children && children[i]; i++) { if (ids[i] == -1) { continue; } length += secmod_getChildLength(children[i], ids[i]); } /* now allocate and build the string */ mark = PORT_ArenaMark(arena); if (!mark) { goto loser; } newParam = PORT_ArenaAlloc(arena, length); if (!newParam) { goto loser; } PORT_Strcpy(newParam, oldParam); tmpLen = strlen(oldParam); nextParam = newParam + tmpLen; length -= tmpLen; PORT_Memcpy(nextParam, TOKEN_STRING, sizeof(TOKEN_STRING) - 1); nextParam += sizeof(TOKEN_STRING) - 1; length -= sizeof(TOKEN_STRING) - 1; for (i = 0; oldChildren && oldChildren[i]; i++) { rv = secmod_mkTokenChild(&nextParam, &length, oldChildren[i], oldIds[i]); if (rv != SECSuccess) { goto loser; } } rv = secmod_mkTokenChild(&nextParam, &length, newToken, newID); if (rv != SECSuccess) { goto loser; } for (i = 0; children && children[i]; i++) { if (ids[i] == -1) { continue; } rv = secmod_mkTokenChild(&nextParam, &length, children[i], ids[i]); if (rv != SECSuccess) { goto loser; } } if (length < 2) { goto loser; } *nextParam++ = ']'; *nextParam++ = 0; /* we are going to return newParam now, don't release the mark */ PORT_ArenaUnmark(arena, mark); mark = NULL; loser: if (mark) { PORT_ArenaRelease(arena, mark); newParam = NULL; /* if the mark is still active, * don't return the param */ } if (rawParam) { PORT_Free(rawParam); } if (oldChildren) { secmod_FreeChildren(oldChildren, oldIds); } return newParam; } static char * secmod_mkModuleSpec(SECMODModule *module) { char *nss = NULL, *modSpec = NULL, **slotStrings = NULL; int slotCount, i, si; SECMODListLock *moduleLock = SECMOD_GetDefaultModuleListLock(); /* allocate target slot info strings */ slotCount = 0; SECMOD_GetReadLock(moduleLock); if (module->slotCount) { for (i = 0; i < module->slotCount; i++) { if (module->slots[i]->defaultFlags != 0) { slotCount++; } } } else { slotCount = module->slotInfoCount; } slotStrings = (char **)PORT_ZAlloc(slotCount * sizeof(char *)); if (slotStrings == NULL) { SECMOD_ReleaseReadLock(moduleLock); goto loser; } /* build the slot info strings */ if (module->slotCount) { for (i = 0, si = 0; i < module->slotCount; i++) { if (module->slots[i]->defaultFlags) { PORT_Assert(si < slotCount); if (si >= slotCount) break; slotStrings[si] = NSSUTIL_MkSlotString(module->slots[i]->slotID, module->slots[i]->defaultFlags, module->slots[i]->timeout, module->slots[i]->askpw, module->slots[i]->hasRootCerts, module->slots[i]->hasRootTrust); si++; } } } else { for (i = 0; i < slotCount; i++) { slotStrings[i] = NSSUTIL_MkSlotString( module->slotInfo[i].slotID, module->slotInfo[i].defaultFlags, module->slotInfo[i].timeout, module->slotInfo[i].askpw, module->slotInfo[i].hasRootCerts, module->slotInfo[i].hasRootTrust); } } SECMOD_ReleaseReadLock(moduleLock); nss = NSSUTIL_MkNSSString(slotStrings, slotCount, module->internal, module->isFIPS, module->isModuleDB, module->moduleDBOnly, module->isCritical, module->trustOrder, module->cipherOrder, module->ssl[0], module->ssl[1]); modSpec = NSSUTIL_MkModuleSpec(module->dllName, module->commonName, module->libraryParams, nss); PORT_Free(slotStrings); PR_smprintf_free(nss); loser: return (modSpec); } char ** SECMOD_GetModuleSpecList(SECMODModule *module) { SECMODModuleDBFunc func = (SECMODModuleDBFunc)module->moduleDBFunc; if (func) { return (*func)(SECMOD_MODULE_DB_FUNCTION_FIND, module->libraryParams, NULL); } return NULL; } SECStatus SECMOD_AddPermDB(SECMODModule *module) { SECMODModuleDBFunc func; char *moduleSpec; char **retString; if (module->parent == NULL) return SECFailure; func = (SECMODModuleDBFunc)module->parent->moduleDBFunc; if (func) { moduleSpec = secmod_mkModuleSpec(module); retString = (*func)(SECMOD_MODULE_DB_FUNCTION_ADD, module->parent->libraryParams, moduleSpec); PORT_Free(moduleSpec); if (retString != NULL) return SECSuccess; } return SECFailure; } SECStatus SECMOD_DeletePermDB(SECMODModule *module) { SECMODModuleDBFunc func; char *moduleSpec; char **retString; if (module->parent == NULL) return SECFailure; func = (SECMODModuleDBFunc)module->parent->moduleDBFunc; if (func) { moduleSpec = secmod_mkModuleSpec(module); retString = (*func)(SECMOD_MODULE_DB_FUNCTION_DEL, module->parent->libraryParams, moduleSpec); PORT_Free(moduleSpec); if (retString != NULL) return SECSuccess; } return SECFailure; } SECStatus SECMOD_FreeModuleSpecList(SECMODModule *module, char **moduleSpecList) { SECMODModuleDBFunc func = (SECMODModuleDBFunc)module->moduleDBFunc; char **retString; if (func) { retString = (*func)(SECMOD_MODULE_DB_FUNCTION_RELEASE, module->libraryParams, moduleSpecList); if (retString != NULL) return SECSuccess; } return SECFailure; } /* * load a PKCS#11 module but do not add it to the default NSS trust domain */ SECMODModule * SECMOD_LoadModule(char *modulespec, SECMODModule *parent, PRBool recurse) { char *library = NULL, *moduleName = NULL, *parameters = NULL, *nss = NULL; char *config = NULL; SECStatus status; SECMODModule *module = NULL; SECMODModule *oldModule = NULL; SECStatus rv; PRBool forwardPolicyFeedback = PR_FALSE; PRUint32 forwardPolicyCheckFlags; /* initialize the underlying module structures */ SECMOD_Init(); status = NSSUTIL_ArgParseModuleSpecEx(modulespec, &library, &moduleName, ¶meters, &nss, &config); if (status != SECSuccess) { goto loser; } module = SECMOD_CreateModuleEx(library, moduleName, parameters, nss, config); forwardPolicyFeedback = NSSUTIL_ArgHasFlag("flags", "printPolicyFeedback", nss); forwardPolicyCheckFlags = secmod_parsePolicyCheckFlags(nss); if (library) PORT_Free(library); if (moduleName) PORT_Free(moduleName); if (parameters) PORT_Free(parameters); if (nss) PORT_Free(nss); if (config) PORT_Free(config); if (!module) { goto loser; } /* a policy only stanza doesn't actually get 'loaded'. policy has already * been parsed as a side effect of the CreateModuleEx call */ if (secmod_PolicyOnly(module)) { return module; } if (parent) { module->parent = SECMOD_ReferenceModule(parent); if (module->internal && secmod_IsInternalKeySlot(parent)) { module->internal = parent->internal; } } /* load it */ rv = secmod_LoadPKCS11Module(module, &oldModule); if (rv != SECSuccess) { goto loser; } /* if we just reload an old module, no need to add it to any lists. * we simple release all our references */ if (oldModule) { /* This module already exists, don't link it anywhere. This * will probably destroy this module */ SECMOD_DestroyModule(module); return oldModule; } if (recurse && module->isModuleDB) { char **moduleSpecList; PORT_SetError(0); moduleSpecList = SECMOD_GetModuleSpecList(module); if (moduleSpecList) { char **index; index = moduleSpecList; if (*index && SECMOD_GetSkipFirstFlag(module)) { index++; } for (; *index; index++) { SECMODModule *child; if (0 == PORT_Strcmp(*index, modulespec)) { /* avoid trivial infinite recursion */ PORT_SetError(SEC_ERROR_NO_MODULE); rv = SECFailure; break; } if (!forwardPolicyFeedback) { child = SECMOD_LoadModule(*index, module, PR_TRUE); } else { /* Add printPolicyFeedback to the nss flags */ char *specWithForwards = NSSUTIL_AddNSSFlagToModuleSpec(*index, "printPolicyFeedback"); char *tmp; if (forwardPolicyCheckFlags & SECMOD_FLAG_POLICY_CHECK_IDENTIFIER) { tmp = NSSUTIL_AddNSSFlagToModuleSpec(specWithForwards, "policyCheckIdentifier"); PORT_Free(specWithForwards); specWithForwards = tmp; } if (forwardPolicyCheckFlags & SECMOD_FLAG_POLICY_CHECK_VALUE) { tmp = NSSUTIL_AddNSSFlagToModuleSpec(specWithForwards, "policyCheckValue"); PORT_Free(specWithForwards); specWithForwards = tmp; } child = SECMOD_LoadModule(specWithForwards, module, PR_TRUE); PORT_Free(specWithForwards); } if (!child) break; if (child->isCritical && !child->loaded) { int err = PORT_GetError(); if (!err) err = SEC_ERROR_NO_MODULE; SECMOD_DestroyModule(child); PORT_SetError(err); rv = SECFailure; break; } SECMOD_DestroyModule(child); } SECMOD_FreeModuleSpecList(module, moduleSpecList); } else { if (!PORT_GetError()) PORT_SetError(SEC_ERROR_NO_MODULE); rv = SECFailure; } } if (rv != SECSuccess) { goto loser; } /* inherit the reference */ if (!module->moduleDBOnly) { SECMOD_AddModuleToList(module); } else { SECMOD_AddModuleToDBOnlyList(module); } /* handle any additional work here */ return module; loser: if (module) { if (module->loaded) { SECMOD_UnloadModule(module); } SECMOD_AddModuleToUnloadList(module); } return module; } /* * load a PKCS#11 module and add it to the default NSS trust domain */ SECMODModule * SECMOD_LoadUserModule(char *modulespec, SECMODModule *parent, PRBool recurse) { SECStatus rv = SECSuccess; SECMODModule *newmod = SECMOD_LoadModule(modulespec, parent, recurse); SECMODListLock *moduleLock = SECMOD_GetDefaultModuleListLock(); if (newmod) { SECMOD_GetReadLock(moduleLock); rv = STAN_AddModuleToDefaultTrustDomain(newmod); SECMOD_ReleaseReadLock(moduleLock); if (SECSuccess != rv) { SECMOD_DestroyModule(newmod); return NULL; } } return newmod; } /* * remove the PKCS#11 module from the default NSS trust domain, call * C_Finalize, and destroy the module structure */ SECStatus SECMOD_UnloadUserModule(SECMODModule *mod) { SECStatus rv = SECSuccess; int atype = 0; SECMODListLock *moduleLock = SECMOD_GetDefaultModuleListLock(); if (!mod) { return SECFailure; } SECMOD_GetReadLock(moduleLock); rv = STAN_RemoveModuleFromDefaultTrustDomain(mod); SECMOD_ReleaseReadLock(moduleLock); if (SECSuccess != rv) { return SECFailure; } return SECMOD_DeleteModuleEx(NULL, mod, &atype, PR_FALSE); }