diff options
Diffstat (limited to '')
-rw-r--r-- | security/nss/lib/pk11wrap/pk11pars.c | 2158 |
1 files changed, 2158 insertions, 0 deletions
diff --git a/security/nss/lib/pk11wrap/pk11pars.c b/security/nss/lib/pk11wrap/pk11pars.c new file mode 100644 index 0000000000..0243a930b6 --- /dev/null +++ b/security/nss/lib/pk11wrap/pk11pars.c @@ -0,0 +1,2158 @@ +/* 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 <ctype.h> +#include <assert.h> +#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 }, + /* 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 }, +}; + +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); +} |