From 89b8b47d46ee042d154eae8461cf0b33f6002cfd Mon Sep 17 00:00:00 2001 From: Kai Engert Date: Fri, 20 Jul 2018 16:09:32 +0200 Subject: [PATCH] Backport upstream addition of nss-policy-check utility, rhbz#1428746 --- backport-policycheck-1474887.patch | 950 +++++++++++++++++++++++++++++ nss.spec | 15 +- 2 files changed, 961 insertions(+), 4 deletions(-) create mode 100644 backport-policycheck-1474887.patch diff --git a/backport-policycheck-1474887.patch b/backport-policycheck-1474887.patch new file mode 100644 index 0000000..4abc047 --- /dev/null +++ b/backport-policycheck-1474887.patch @@ -0,0 +1,950 @@ +# HG changeset patch +# User Kai Engert +# Date 1531823374 -7200 +# Tue Jul 17 12:29:34 2018 +0200 +# Node ID a6d6a56b6e39558e9b6b0b32009a46ea78040bfd +# Parent 1f58a4995451ac0bdb79b5e3117b365edec3af2d +Bug 1474887, nss-policy-check: a tool to check a NSS policy configuration for errors, r=rrelyea + +diff --git a/automation/abi-check/expected-report-libnssutil3.so.txt b/automation/abi-check/expected-report-libnssutil3.so.txt +--- a/automation/abi-check/expected-report-libnssutil3.so.txt ++++ b/automation/abi-check/expected-report-libnssutil3.so.txt +@@ -0,0 +1,5 @@ ++ ++1 Added function: ++ ++ 'function char* NSSUTIL_AddNSSFlagToModuleSpec(char*, char*)' {NSSUTIL_AddNSSFlagToModuleSpec@@NSSUTIL_3.39} ++ +diff --git a/automation/taskcluster/graph/src/extend.js b/automation/taskcluster/graph/src/extend.js +--- a/automation/taskcluster/graph/src/extend.js ++++ b/automation/taskcluster/graph/src/extend.js +@@ -928,6 +928,9 @@ function scheduleTests(task_build, task_ + queue.scheduleTask(merge(no_cert_base, { + name: "SDR tests", symbol: "SDR", tests: "sdr" + })); ++ queue.scheduleTask(merge(no_cert_base, { ++ name: "Policy tests", symbol: "Policy", tests: "policy" ++ })); + + // Schedule tests that need certificates. + let cert_base = merge(test_base, {parent: task_cert}); +diff --git a/automation/taskcluster/graph/src/try_syntax.js b/automation/taskcluster/graph/src/try_syntax.js +--- a/automation/taskcluster/graph/src/try_syntax.js ++++ b/automation/taskcluster/graph/src/try_syntax.js +@@ -37,7 +37,7 @@ function parseOptions(opts) { + let aliases = {"gtests": "gtest"}; + let allUnitTests = ["bogo", "crmf", "chains", "cipher", "db", "ec", "fips", + "gtest", "interop", "lowhash", "merge", "sdr", "smime", "tools", +- "ssl", "mpi", "scert", "spki"]; ++ "ssl", "mpi", "scert", "spki", "policy"]; + let unittests = intersect(opts.unittests.split(/\s*,\s*/).map(t => { + return aliases[t] || t; + }), allUnitTests); +diff --git a/cmd/manifest.mn b/cmd/manifest.mn +--- a/cmd/manifest.mn ++++ b/cmd/manifest.mn +@@ -47,6 +47,7 @@ NSS_SRCDIRS = \ + listsuites \ + makepqg \ + multinit \ ++ nss-policy-check \ + ocspclnt \ + ocspresp \ + oidcalc \ +diff --git a/cmd/nss-policy-check/Makefile b/cmd/nss-policy-check/Makefile +new file mode 100644 +--- /dev/null ++++ b/cmd/nss-policy-check/Makefile +@@ -0,0 +1,47 @@ ++#! gmake ++# ++# 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/. ++ ++####################################################################### ++# (1) Include initial platform-independent assignments (MANDATORY). # ++####################################################################### ++ ++include manifest.mn ++ ++####################################################################### ++# (2) Include "global" configuration information. (OPTIONAL) # ++####################################################################### ++ ++include $(CORE_DEPTH)/coreconf/config.mk ++ ++####################################################################### ++# (3) Include "component" configuration information. (OPTIONAL) # ++####################################################################### ++ ++####################################################################### ++# (4) Include "local" platform-dependent assignments (OPTIONAL). # ++####################################################################### ++ ++include ../platlibs.mk ++ ++####################################################################### ++# (5) Execute "global" rules. (OPTIONAL) # ++####################################################################### ++ ++include $(CORE_DEPTH)/coreconf/rules.mk ++ ++####################################################################### ++# (6) Execute "component" rules. (OPTIONAL) # ++####################################################################### ++ ++ ++ ++####################################################################### ++# (7) Execute "local" rules. (OPTIONAL). # ++####################################################################### ++ ++ ++include ../platrules.mk ++ +diff --git a/cmd/nss-policy-check/manifest.mn b/cmd/nss-policy-check/manifest.mn +new file mode 100644 +--- /dev/null ++++ b/cmd/nss-policy-check/manifest.mn +@@ -0,0 +1,15 @@ ++# ++# 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/. ++ ++CORE_DEPTH = ../.. ++ ++MODULE = nss ++ ++CSRCS = nss-policy-check.c ++ ++REQUIRES = seccmd ++ ++PROGRAM = nss-policy-check ++ +diff --git a/cmd/nss-policy-check/nss-policy-check.c b/cmd/nss-policy-check/nss-policy-check.c +new file mode 100644 +--- /dev/null ++++ b/cmd/nss-policy-check/nss-policy-check.c +@@ -0,0 +1,205 @@ ++/* 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/. */ ++ ++/* This program can be used to check the validity of a NSS crypto policy ++ * configuration file, specified using a config= line. ++ * ++ * Exit codes: ++ * failure: 2 ++ * warning: 1 ++ * success: 0 ++ */ ++ ++#include ++#include ++#include ++#include "utilparst.h" ++#include "nss.h" ++#include "secport.h" ++#include "secutil.h" ++#include "secmod.h" ++#include "ssl.h" ++#include "prenv.h" ++ ++const char *sWarn = "WARN"; ++const char *sInfo = "INFO"; ++ ++void ++get_tls_info(SSLProtocolVariant protocolVariant, const char *display) ++{ ++ SSLVersionRange vrange_supported, vrange_enabled; ++ unsigned num_enabled = 0; ++ PRBool failed = PR_FALSE; ++ ++ /* We assume SSL v2 is inactive, and therefore SSL_VersionRangeGetDefault ++ * gives complete information. */ ++ if ((SSL_VersionRangeGetSupported(protocolVariant, &vrange_supported) != SECSuccess) || ++ (SSL_VersionRangeGetDefault(protocolVariant, &vrange_enabled) != SECSuccess) || ++ !vrange_enabled.min || ++ !vrange_enabled.max || ++ vrange_enabled.max < vrange_supported.min || ++ vrange_enabled.min > vrange_supported.max) { ++ failed = PR_TRUE; ++ } else { ++ if (vrange_enabled.min < vrange_supported.min) { ++ vrange_enabled.min = vrange_supported.min; ++ } ++ if (vrange_enabled.max > vrange_supported.max) { ++ vrange_enabled.max = vrange_supported.max; ++ } ++ if (vrange_enabled.min > vrange_enabled.max) { ++ failed = PR_TRUE; ++ } ++ } ++ if (failed) { ++ num_enabled = 0; ++ } else { ++ num_enabled = vrange_enabled.max - vrange_enabled.min + 1; ++ } ++ fprintf(stderr, "NSS-POLICY-%s: NUMBER-OF-%s-VERSIONS: %u\n", ++ num_enabled ? sInfo : sWarn, display, num_enabled); ++ if (!num_enabled) { ++ PR_SetEnv("NSS_POLICY_WARN=1"); ++ } ++} ++ ++#ifndef PATH_MAX ++#define PATH_MAX 1024 ++#endif ++ ++int ++main(int argc, char **argv) ++{ ++ const PRUint16 *cipherSuites = SSL_ImplementedCiphers; ++ int i; ++ SECStatus rv; ++ SECMODModule *module = NULL; ++ char path[PATH_MAX]; ++ const char *filename; ++ char moduleSpec[1024 + PATH_MAX]; ++ unsigned num_enabled = 0; ++ int result = 0; ++ int fullPathLen; ++ ++ if (argc != 2) { ++ fprintf(stderr, "Syntax: nss-policy-check \n"); ++ result = 2; ++ goto loser; ++ } ++ ++ fullPathLen = strlen(argv[1]); ++ ++ if (!fullPathLen || PR_Access(argv[1], PR_ACCESS_READ_OK) != PR_SUCCESS) { ++ fprintf(stderr, "Error: cannot read file %s\n", argv[1]); ++ result = 2; ++ goto loser; ++ } ++ ++ if (fullPathLen >= PATH_MAX) { ++ fprintf(stderr, "Error: filename parameter is too long\n"); ++ result = 2; ++ goto loser; ++ } ++ ++ path[0] = 0; ++ filename = argv[1] + fullPathLen - 1; ++ while ((filename > argv[1]) && (*filename != NSSUTIL_PATH_SEPARATOR[0])) { ++ filename--; ++ } ++ ++ if (filename == argv[1]) { ++ PORT_Strcpy(path, "."); ++ } else { ++ filename++; /* Go past the path separator. */ ++ PORT_Strncat(path, argv[1], (filename - argv[1])); ++ } ++ ++ PR_SetEnv("NSS_IGNORE_SYSTEM_POLICY=1"); ++ rv = NSS_NoDB_Init(NULL); ++ if (rv != SECSuccess) { ++ fprintf(stderr, "NSS_Init failed: %s\n", PORT_ErrorToString(PR_GetError())); ++ result = 2; ++ goto loser; ++ } ++ ++ PR_SetEnv("NSS_POLICY_LOADED=0"); ++ PR_SetEnv("NSS_POLICY_FAIL=0"); ++ PR_SetEnv("NSS_POLICY_WARN=0"); ++ ++ sprintf(moduleSpec, ++ "name=\"Policy File\" " ++ "parameters=\"configdir='sql:%s' " ++ "secmod='%s' " ++ "flags=readOnly,noCertDB,forceSecmodChoice,forceOpen\" " ++ "NSS=\"flags=internal,moduleDB,skipFirst,moduleDBOnly,critical,printPolicyFeedback\"", ++ path, filename); ++ ++ module = SECMOD_LoadModule(moduleSpec, NULL, PR_TRUE); ++ if (!module || !module->loaded || atoi(PR_GetEnvSecure("NSS_POLICY_LOADED")) != 1) { ++ fprintf(stderr, "Error: failed to load policy file\n"); ++ result = 2; ++ goto loser; ++ } ++ ++ rv = SSL_OptionSetDefault(SSL_SECURITY, PR_TRUE); ++ if (rv != SECSuccess) { ++ fprintf(stderr, "enable SSL_SECURITY failed: %s\n", PORT_ErrorToString(PR_GetError())); ++ result = 2; ++ goto loser; ++ } ++ ++ for (i = 0; i < SSL_NumImplementedCiphers; i++) { ++ PRUint16 suite = cipherSuites[i]; ++ PRBool enabled; ++ SSLCipherSuiteInfo info; ++ ++ rv = SSL_CipherPrefGetDefault(suite, &enabled); ++ if (rv != SECSuccess) { ++ fprintf(stderr, ++ "SSL_CipherPrefGetDefault didn't like value 0x%04x (i = %d): %s\n", ++ suite, i, PORT_ErrorToString(PR_GetError())); ++ continue; ++ } ++ rv = SSL_GetCipherSuiteInfo(suite, &info, (int)(sizeof info)); ++ if (rv != SECSuccess) { ++ fprintf(stderr, ++ "SSL_GetCipherSuiteInfo didn't like value 0x%04x (i = %d): %s\n", ++ suite, i, PORT_ErrorToString(PR_GetError())); ++ continue; ++ } ++ if (enabled) { ++ ++num_enabled; ++ fprintf(stderr, "NSS-POLICY-INFO: ciphersuite %s is enabled\n", info.cipherSuiteName); ++ } ++ } ++ fprintf(stderr, "NSS-POLICY-%s: NUMBER-OF-CIPHERSUITES: %u\n", num_enabled ? sInfo : sWarn, num_enabled); ++ if (!num_enabled) { ++ PR_SetEnv("NSS_POLICY_WARN=1"); ++ } ++ ++ get_tls_info(ssl_variant_stream, "TLS"); ++ get_tls_info(ssl_variant_datagram, "DTLS"); ++ ++ if (atoi(PR_GetEnvSecure("NSS_POLICY_FAIL")) != 0) { ++ result = 2; ++ } else if (atoi(PR_GetEnvSecure("NSS_POLICY_WARN")) != 0) { ++ result = 1; ++ } ++ ++loser: ++ if (module) { ++ SECMOD_DestroyModule(module); ++ } ++ rv = NSS_Shutdown(); ++ if (rv != SECSuccess) { ++ fprintf(stderr, "NSS_Shutdown failed: %s\n", PORT_ErrorToString(PR_GetError())); ++ result = 2; ++ } ++ if (result == 2) { ++ fprintf(stderr, "NSS-POLICY-FAIL\n"); ++ } else if (result == 1) { ++ fprintf(stderr, "NSS-POLICY-WARN\n"); ++ } ++ return result; ++} +diff --git a/cmd/nss-policy-check/nss-policy-check.gyp b/cmd/nss-policy-check/nss-policy-check.gyp +new file mode 100644 +--- /dev/null ++++ b/cmd/nss-policy-check/nss-policy-check.gyp +@@ -0,0 +1,24 @@ ++# 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/. ++{ ++ 'includes': [ ++ '../../coreconf/config.gypi', ++ '../../cmd/platlibs.gypi' ++ ], ++ 'targets': [ ++ { ++ 'target_name': 'nss-policy-check', ++ 'type': 'executable', ++ 'sources': [ ++ 'nss-policy-check.c' ++ ], ++ 'dependencies': [ ++ '<(DEPTH)/exports.gyp:nss_exports' ++ ] ++ } ++ ], ++ 'variables': { ++ 'module': 'nss' ++ } ++} +\ No newline at end of file +diff --git a/lib/nss/nssinit.c b/lib/nss/nssinit.c +--- a/lib/nss/nssinit.c ++++ b/lib/nss/nssinit.c +@@ -54,7 +54,7 @@ nss_mktemp(char *path) + + #define NSS_MAX_FLAG_SIZE sizeof("readOnly") + sizeof("noCertDB") + \ + sizeof("noModDB") + sizeof("forceOpen") + sizeof("passwordRequired") + \ +- sizeof("optimizeSpace") ++ sizeof("optimizeSpace") + sizeof("printPolicyFeedback") + #define NSS_DEFAULT_MOD_NAME "NSS Internal Module" + + static char * +diff --git a/lib/pk11wrap/pk11pars.c b/lib/pk11wrap/pk11pars.c +--- a/lib/pk11wrap/pk11pars.c ++++ b/lib/pk11wrap/pk11pars.c +@@ -194,7 +194,7 @@ typedef struct { + * This table should be merged with the SECOID table. + */ + #define CIPHER_NAME(x) x, (sizeof(x) - 1) +-static const oidValDef algOptList[] = { ++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 }, +@@ -316,7 +316,9 @@ static const oidValDef algOptList[] = { + 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_CERT_SIGNATURE }, +@@ -334,7 +336,9 @@ static const oidValDef algOptList[] = { + NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, + { CIPHER_NAME("SHA512"), SEC_OID_SHA512, + NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_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 }, +@@ -342,7 +346,9 @@ static const oidValDef algOptList[] = { + { 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 }, +@@ -362,7 +368,9 @@ static const oidValDef algOptList[] = { + { 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 }, +@@ -376,6 +384,20 @@ static const oidValDef algOptList[] = { + { CIPHER_NAME("ECDH-RSA"), SEC_OID_TLS_ECDH_RSA, NSS_USE_ALG_IN_SSL_KX }, + }; + ++typedef struct { ++ const oidValDef *list; ++ PRUint32 entries; ++ const char *description; ++} algListsDef; ++ ++static const algListsDef algOptLists[] = { ++ { curveOptList, PR_ARRAY_SIZE(curveOptList), "ECC" }, ++ { hashOptList, PR_ARRAY_SIZE(hashOptList), "HASH" }, ++ { macOptList, PR_ARRAY_SIZE(macOptList), "MAC" }, ++ { cipherOptList, PR_ARRAY_SIZE(cipherOptList), "CIPHER" }, ++ { kxOptList, PR_ARRAY_SIZE(kxOptList), "OTHER-KX" }, ++}; ++ + static const optionFreeDef sslOptList[] = { + /* Versions */ + { CIPHER_NAME("SSL2.0"), 0x002 }, +@@ -447,7 +469,8 @@ secmod_ArgGetSubValue(const char *cipher + } + + static PRUint32 +-secmod_parsePolicyValue(const char *policyFlags, int policyLength) ++secmod_parsePolicyValue(const char *policyFlags, int policyLength, ++ PRBool printPolicyFeedback) + { + const char *flag, *currentString; + PRUint32 flags = 0; +@@ -456,6 +479,7 @@ secmod_parsePolicyValue(const char *poli + for (currentString = policyFlags; currentString && + currentString < policyFlags + policyLength;) { + int length; ++ PRBool unknown = PR_TRUE; + flag = secmod_ArgGetSubValue(currentString, ',', ':', &length, + ¤tString); + if (length == 0) { +@@ -467,41 +491,49 @@ secmod_parsePolicyValue(const char *poli + if ((policy->name_size == length) && + PORT_Strncasecmp(policy->name, flag, name_size) == 0) { + flags |= policy->flag; ++ unknown = PR_FALSE; + break; + } + } ++ if (unknown && printPolicyFeedback) { ++ 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 PRInt32 +-secmod_getPolicyOptValue(const char *policyValue, int policyValueLength) ++static SECStatus ++secmod_getPolicyOptValue(const char *policyValue, int policyValueLength, ++ PRInt32 *result) + { + PRInt32 val = atoi(policyValue); + int i; + + if ((val != 0) || (*policyValue == '0')) { +- return val; ++ *result = val; ++ return SECSuccess; + } + 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) { +- val = sslOptList[i].option; +- break; ++ *result = sslOptList[i].option; ++ return SECSuccess; + } + } +- return val; ++ return SECFailure; + } + + static SECStatus +-secmod_applyCryptoPolicy(const char *policyString, +- PRBool allow) ++secmod_applyCryptoPolicy(const char *policyString, PRBool allow, ++ PRBool printPolicyFeedback) + { + const char *cipher, *currentString; +- unsigned i; ++ unsigned i, j; + SECStatus rv = SECSuccess; + PRBool unknown; + +@@ -526,56 +558,63 @@ secmod_applyCryptoPolicy(const char *pol + /* disable or enable all options by default */ + PRUint32 value = 0; + if (newValue) { +- value = secmod_parsePolicyValue(&cipher[3] + 1, length - 3 - 1); ++ value = secmod_parsePolicyValue(&cipher[3] + 1, length - 3 - 1, printPolicyFeedback); + } +- for (i = 0; i < PR_ARRAY_SIZE(algOptList); i++) { +- PRUint32 enable, disable; +- if (!newValue) { +- value = algOptList[i].val; ++ for (i = 0; i < PR_ARRAY_SIZE(algOptLists); i++) { ++ const algListsDef *algOptList = &algOptLists[i]; ++ for (j = 0; j < algOptList->entries; j++) { ++ PRUint32 enable, disable; ++ if (!newValue) { ++ value = algOptList->list[j].val; ++ } ++ if (allow) { ++ enable = value; ++ disable = 0; ++ } else { ++ enable = 0; ++ disable = value; ++ } ++ NSS_SetAlgorithmPolicy(algOptList->list[j].oid, enable, disable); + } +- if (allow) { +- enable = value; +- disable = 0; +- } else { +- enable = 0; +- disable = value; +- } +- NSS_SetAlgorithmPolicy(algOptList[i].oid, enable, disable); + } + continue; + } + +- for (i = 0; i < PR_ARRAY_SIZE(algOptList); i++) { +- const oidValDef *algOpt = &algOptList[i]; +- unsigned name_size = algOpt->name_size; +- PRBool newOption = PR_FALSE; ++ 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; +- PRUint32 enable, disable; +- if (newOption) { +- value = secmod_parsePolicyValue(&cipher[name_size] + 1, +- length - name_size - 1); ++ if ((length >= name_size) && (cipher[name_size] == '/')) { ++ newOption = PR_TRUE; + } +- if (allow) { +- enable = value; +- disable = 0; +- } else { +- enable = 0; +- disable = value; ++ if ((newOption || algOpt->name_size == length) && ++ PORT_Strncasecmp(algOpt->name, cipher, name_size) == 0) { ++ PRUint32 value = algOpt->val; ++ PRUint32 enable, disable; ++ if (newOption) { ++ value = secmod_parsePolicyValue(&cipher[name_size] + 1, ++ length - name_size - 1, ++ printPolicyFeedback); ++ } ++ if (allow) { ++ enable = value; ++ disable = 0; ++ } else { ++ enable = 0; ++ disable = value; ++ } ++ rv = NSS_SetAlgorithmPolicy(algOpt->oid, enable, disable); ++ if (rv != SECSuccess) { ++ /* could not enable option */ ++ /* NSS_SetAlgorithPolicy should have set the error code */ ++ return SECFailure; ++ } ++ unknown = PR_FALSE; ++ break; + } +- rv = NSS_SetAlgorithmPolicy(algOpt->oid, enable, disable); +- if (rv != SECSuccess) { +- /* could not enable option */ +- /* NSS_SetAlgorithPolicy should have set the error code */ +- return SECFailure; +- } +- unknown = PR_FALSE; +- break; + } + } + if (!unknown) { +@@ -588,9 +627,19 @@ secmod_applyCryptoPolicy(const char *pol + + if ((length > name_size) && cipher[name_size] == '=' && + PORT_Strncasecmp(freeOpt->name, cipher, name_size) == 0) { +- PRInt32 val = secmod_getPolicyOptValue(&cipher[name_size + 1], +- length - name_size - 1); +- ++ 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) { ++ 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 */ +@@ -603,12 +652,83 @@ secmod_applyCryptoPolicy(const char *pol + break; + } + } ++ ++ if (unknown && printPolicyFeedback) { ++ PR_SetEnv("NSS_POLICY_FAIL=1"); ++ fprintf(stderr, "NSS-POLICY-FAIL %s: unknown identifier: %.*s\n", ++ allow ? "allow" : "disallow", 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_ALG_IN_CERT_SIGNATURE)) { ++ ++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]) { ++ haveWarning = PR_TRUE; ++ } ++ } ++ if (haveWarning) { ++ PR_SetEnv("NSS_POLICY_WARN=1"); ++ } ++} ++ + static SECStatus +-secmod_parseCryptoPolicy(const char *policyConfig) ++secmod_parseCryptoPolicy(const char *policyConfig, PRBool printPolicyFeedback) + { + char *disallow, *allow; + SECStatus rv; +@@ -623,16 +743,26 @@ secmod_parseCryptoPolicy(const char *pol + return rv; + } + disallow = NSSUTIL_ArgGetParamValue("disallow", policyConfig); +- rv = secmod_applyCryptoPolicy(disallow, PR_FALSE); ++ rv = secmod_applyCryptoPolicy(disallow, PR_FALSE, printPolicyFeedback); + if (disallow) + PORT_Free(disallow); + if (rv != SECSuccess) { + return rv; + } + allow = NSSUTIL_ArgGetParamValue("allow", policyConfig); +- rv = secmod_applyCryptoPolicy(allow, PR_TRUE); ++ rv = secmod_applyCryptoPolicy(allow, PR_TRUE, printPolicyFeedback); + if (allow) + PORT_Free(allow); ++ if (rv != SECSuccess) { ++ return rv; ++ } ++ 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; + } + +@@ -649,11 +779,16 @@ SECMOD_CreateModuleEx(const char *librar + char *slotParams, *ciphers; + /* pk11pars.h still does not have const char * interfaces */ + char *nssc = (char *)nss; ++ PRBool printPolicyFeedback = NSSUTIL_ArgHasFlag("flags", "printPolicyFeedback", nssc); + +- rv = secmod_parseCryptoPolicy(config); ++ rv = secmod_parseCryptoPolicy(config, printPolicyFeedback); + + /* 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; + } + +@@ -1647,6 +1782,7 @@ SECMOD_LoadModule(char *modulespec, SECM + SECMODModule *module = NULL; + SECMODModule *oldModule = NULL; + SECStatus rv; ++ PRBool forwardPolicyFeedback = PR_FALSE; + + /* initialize the underlying module structures */ + SECMOD_Init(); +@@ -1659,6 +1795,7 @@ SECMOD_LoadModule(char *modulespec, SECM + } + + module = SECMOD_CreateModuleEx(library, moduleName, parameters, nss, config); ++ forwardPolicyFeedback = NSSUTIL_ArgHasFlag("flags", "printPolicyFeedback", nss); + if (library) + PORT_Free(library); + if (moduleName) +@@ -1721,7 +1858,15 @@ SECMOD_LoadModule(char *modulespec, SECM + rv = SECFailure; + break; + } +- child = SECMOD_LoadModule(*index, module, PR_TRUE); ++ if (!forwardPolicyFeedback) { ++ child = SECMOD_LoadModule(*index, module, PR_TRUE); ++ } else { ++ /* Add printPolicyFeedback to the nss flags */ ++ char *specWithForwards = ++ NSSUTIL_AddNSSFlagToModuleSpec(*index, "printPolicyFeedback"); ++ child = SECMOD_LoadModule(specWithForwards, module, PR_TRUE); ++ PORT_Free(specWithForwards); ++ } + if (!child) + break; + if (child->isCritical && !child->loaded) { +diff --git a/nss.gyp b/nss.gyp +--- a/nss.gyp ++++ b/nss.gyp +@@ -135,6 +135,7 @@ + 'cmd/listsuites/listsuites.gyp:listsuites', + 'cmd/makepqg/makepqg.gyp:makepqg', + 'cmd/multinit/multinit.gyp:multinit', ++ 'cmd/nss-policy-check/nss-policy-check.gyp:nss-policy-check', + 'cmd/ocspclnt/ocspclnt.gyp:ocspclnt', + 'cmd/ocspresp/ocspresp.gyp:ocspresp', + 'cmd/oidcalc/oidcalc.gyp:oidcalc', +diff --git a/readme.md b/readme.md +--- a/readme.md ++++ b/readme.md +@@ -97,7 +97,7 @@ e.g. `NSS_TESTS=ssl_gtests ./all.sh` or + and running the bash script there `cd ssl_gtests && ./ssl_gtests.sh`. The + following tests are available: + +- cipher lowhash libpkix cert dbtests tools fips sdr crmf smime ssl ocsp merge pkits chains ec gtests ssl_gtests bogo ++ cipher lowhash libpkix cert dbtests tools fips sdr crmf smime ssl ocsp merge pkits chains ec gtests ssl_gtests bogo policy + + To make tests run faster it's recommended to set `NSS_CYCLES=standard` to run + only the standard cycle. +diff --git a/tests/all.sh b/tests/all.sh +--- a/tests/all.sh ++++ b/tests/all.sh +@@ -37,6 +37,7 @@ + # memleak.sh - memory leak testing (optional) + # ssl_gtests.sh- Gtest based unit tests for ssl + # gtests.sh - Gtest based unit tests for everything else ++# policy.sh - Crypto Policy tests + # bogo.sh - Bogo interop tests (disabled by default) + # https://boringssl.googlesource.com/boringssl/+/master/ssl/test/PORTING.md + # interop.sh - Interoperability tests (disabled by default) +@@ -300,7 +301,7 @@ if [ $NO_INIT_SUPPORT -eq 0 ]; then + RUN_FIPS="fips" + fi + +-tests="cipher lowhash libpkix cert dbtests tools $RUN_FIPS sdr crmf smime ssl ocsp merge pkits ec gtests ssl_gtests" ++tests="cipher lowhash libpkix cert dbtests tools $RUN_FIPS sdr crmf smime ssl ocsp merge pkits ec gtests ssl_gtests policy" + # Don't run chains tests when we have a gyp build. + if [ "$OBJDIR" != "Debug" -a "$OBJDIR" != "Release" ]; then + tests="$tests chains" +diff --git a/tests/policy/crypto-policy.txt b/tests/policy/crypto-policy.txt +new file mode 100644 +--- /dev/null ++++ b/tests/policy/crypto-policy.txt +@@ -0,0 +1,19 @@ ++# col 1: expected return value of nss-policy-check ++# col 2: policy config statement, using _ instead of space ++# col 3: an extended regular expression, expected to match the output ++# col 4: description of the test ++# ++0 disallow=ALL_allow=HMAC-SHA256:HMAC-SHA1:HMAC-SHA384:HMAC-SHA512:SECP256R1:SECP384R1:SECP521R1:aes256-gcm:chacha20-poly1305:aes256-cbc:camellia256-cbc:aes128-gcm:aes128-cbc:camellia128-cbc:SHA256:SHA384:SHA512:SHA1:ECDHE-RSA:ECDHE-ECDSA:RSA:DHE-RSA:tls-version-min=tls1.0:dtls-version-min=dtls1.0:DH-MIN=1023:DSA-MIN=2048:RSA-MIN=2048 NSS-POLICY-INFO.*LOADED-SUCCESSFULLY Standard policy ++0 disallow=ALL_allow=HMAC-SHA1:HMAC-SHA256:HMAC-SHA384:HMAC-SHA512:SECP256R1:SECP384R1:SECP521R1:aes256-gcm:chacha20-poly1305:aes256-cbc:camellia256-cbc:aes128-gcm:aes128-cbc:camellia128-cbc:des-ede3-cbc:rc4:SHA256:SHA384:SHA512:SHA1:ECDHE-RSA:ECDHE-ECDSA:RSA:DHE-RSA:DHE-DSS:tls-version-min=tls1.0:dtls-version-min=tls1.0:DH-MIN=1023:DSA-MIN=1023:RSA-MIN=1023 NSS-POLICY-INFO.*LOADED-SUCCESSFULLY Legacy policy ++0 disallow=ALL_allow=HMAC-SHA256:HMAC-SHA384:HMAC-SHA512:SECP384R1:SECP521R1:aes256-gcm:chacha20-poly1305:SHA384:SHA512:ECDHE-RSA:ECDHE-ECDSA:RSA:DHE-RSA:tls-version-min=tls1.2:dtls-version-min=dtls1.2:DH-MIN=3072:DSA-MIN=3072:RSA-MIN=3072 NSS-POLICY-INFO.*LOADED-SUCCESSFULLY Reduced policy ++2 disallow=ALL_allow=dtls-version-min=:dtls-version-max= NSS-POLICY-FAIL Missing value ++2 disallow=ALL_allow=RSA-MIN=whatever NSS-POLICY-FAIL Invalid value ++2 disallow=ALL_allow=flower NSS-POLICY-FAIL Invalid identifier ++1 disallow=all NSS-POLICY-WARN.*NUMBER-OF-CERT-SIG disallow all ++1 disallow=ALL_allow=HMAC-SHA256:HMAC-SHA384:HMAC-SHA512:SECP384R1:SECP521R1:aes256-gcm:chacha20-poly1305:ECDHE-RSA:ECDHE-ECDSA:RSA:DHE-RSA:tls-version-min=tls1.2:dtls-version-min=dtls1.2:DH-MIN=3072:DSA-MIN=3072:RSA-MIN=3072 NSS-POLICY-WARN.*NUMBER-OF-HASH No Hashes ++1 disallow=ALL_allow=tls-version-min=0:tls-version-max=0 NSS-POLICY-WARN.*NUMBER-OF-TLS-VERSIONS All TLS versions disabled ++1 disallow=ALL_allow=dtls-version-min=0:dtls-version-max=0 NSS-POLICY-WARN.*NUMBER-OF-DTLS-VERSIONS All DTLS versions disabled ++1 disallow=ALL_allow=tls-version-min=tls1.2:tls-version-max=tls1.1 NSS-POLICY-WARN.*NUMBER-OF-TLS-VERSIONS Invalid range of TLS versions ++1 disallow=ALL_allow=dtls-version-min=tls1.2:dtls-version-max=tls1.1 NSS-POLICY-WARN.*NUMBER-OF-DTLS-VERSIONS Invalid range of DTLS versions ++1 disallow=ALL_allow=tls-version-min=tls1.1:tls-version-max=tls1.2 NSS-POLICY-INFO.*NUMBER-OF-TLS-VERSIONS Valid range of TLS versions ++1 disallow=ALL_allow=dtls-version-min=tls1.1:dtls-version-max=tls1.2 NSS-POLICY-INFO.*NUMBER-OF-DTLS-VERSIONS Valid range of DTLS versions +diff --git a/tests/policy/policy.sh b/tests/policy/policy.sh +new file mode 100644 +--- /dev/null ++++ b/tests/policy/policy.sh +@@ -0,0 +1,58 @@ ++#! /bin/bash ++# ++# 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/. ++ ++######################################################################## ++# ++# mozilla/security/nss/tests/policy/policy.sh ++# ++# Script to test NSS crypto policy code ++# ++######################################################################## ++ ++ignore_blank_lines() ++{ ++ LC_ALL=C grep -v '^[[:space:]]*\(#\|$\)' "$1" ++} ++ ++policy_run_tests() ++{ ++ html_head "CRYPTO-POLICY" ++ ++ POLICY_INPUT=${QADIR}/policy/crypto-policy.txt ++ ++ ignore_blank_lines ${POLICY_INPUT} | \ ++ while read value policy match testname ++ do ++ echo "$SCRIPTNAME: running \"$testname\" ----------------------------" ++ policy=`echo ${policy} | sed -e 's;_; ;g'` ++ match=`echo ${match} | sed -e 's;_; ;g'` ++ POLICY_FILE="${TMP}/nss-policy" ++ ++ echo "$SCRIPTNAME: policy: \"$policy\"" ++ ++ cat > "$POLICY_FILE" << ++EOF++ ++library= ++name=Policy ++NSS=flags=policyOnly,moduleDB ++++EOF++ ++ echo "config=\"${policy}\"" >> "$POLICY_FILE" ++ echo "" >> "$POLICY_FILE" ++ ++ nss-policy-check "$POLICY_FILE" >${TMP}/$HOST.tmp.$$ 2>&1 ++ ret=$? ++ cat ${TMP}/$HOST.tmp.$$ ++ ++ html_msg $ret $value "\"${testname}\"" \ ++ "produced a returncode of $ret, expected is $value" ++ ++ egrep "${match}" ${TMP}/$HOST.tmp.$$ ++ ret=$? ++ html_msg $ret 0 "\"${testname}\" output is expected to match \"${match}\"" ++ ++ done ++} ++ ++policy_run_tests diff --git a/nss.spec b/nss.spec index 399ae60..d68378a 100644 --- a/nss.spec +++ b/nss.spec @@ -1,5 +1,6 @@ %global nspr_version 4.19.0 %global nss_util_version 3.38.0 +%global nss_util_build -4 %global nss_softokn_version 3.38.0 %global unsupported_tools_directory %{_libdir}/nss/unsupported-tools %global allTools "certutil cmsutil crlutil derdump modutil pk12util signtool signver ssltap vfychain vfyserv" @@ -9,12 +10,12 @@ Name: nss Version: 3.38.0 # for Rawhide, please always use release >= 2 # for Fedora release branches, please use release < 2 (1.0, 1.1, ...) -Release: 3%{?dist} +Release: 4%{?dist} License: MPLv2.0 URL: http://www.mozilla.org/projects/security/pki/nss/ Group: System Environment/Libraries Requires: nspr >= %{nspr_version} -Requires: nss-util >= %{nss_util_version} +Requires: nss-util >= %{nss_util_version}%{nss_util_build} # TODO: revert to same version as nss once we are done with the merge Requires: nss-softokn%{_isa} >= %{nss_softokn_version} Requires: nss-system-init @@ -24,7 +25,7 @@ BuildRequires: nspr-devel >= %{nspr_version} # TODO: revert to same version as nss once we are done with the merge # Using '>=' but on RHEL the requires should be '=' BuildRequires: nss-softokn-devel >= %{nss_softokn_version} -BuildRequires: nss-util-devel >= %{nss_util_version} +BuildRequires: nss-util-devel >= %{nss_util_version}%{nss_util_build} BuildRequires: sqlite-devel BuildRequires: zlib-devel BuildRequires: pkgconfig @@ -78,6 +79,7 @@ Patch58: rhbz1185708-enable-ecc-3des-ciphers-by-default.patch # Upstream: https://bugzilla.mozilla.org/show_bug.cgi?id=1279520 Patch59: nss-check-policy-file.patch Patch60: nss-load-policy-file.patch +Patch61: backport-policycheck-1474887.patch Patch62: nss-skip-util-gtest.patch %description @@ -161,6 +163,7 @@ low level services. pushd nss %patch59 -p1 -b .check_policy_file %patch60 -p1 -b .load_policy_file +%patch61 -p1 -b .1474887 %patch62 -p1 -b .skip_util_gtest popd @@ -522,7 +525,7 @@ do done # Copy the binaries we want -for file in certutil cmsutil crlutil modutil pk12util signver ssltap +for file in certutil cmsutil crlutil modutil nss-policy-check pk12util signver ssltap do %{__install} -p -m 755 dist/*.OBJ/bin/$file $RPM_BUILD_ROOT/%{_bindir} done @@ -627,6 +630,7 @@ update-crypto-policies %{_bindir}/cmsutil %{_bindir}/crlutil %{_bindir}/modutil +%{_bindir}/nss-policy-check %{_bindir}/pk12util %{_bindir}/signver %{_bindir}/ssltap @@ -737,6 +741,9 @@ update-crypto-policies %changelog +* Fri Jul 20 2018 Kai Engert - 3.38.0-4 +- Backport upstream addition of nss-policy-check utility, rhbz#1428746 + * Fri Jul 13 2018 Fedora Release Engineering - 3.38.0-3 - Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild