Backport upstream addition of nss-policy-check utility, rhbz#1428746

This commit is contained in:
Kai Engert 2018-07-20 16:09:32 +02:00
parent e4c3da9da7
commit 89b8b47d46
2 changed files with 961 additions and 4 deletions

View File

@ -0,0 +1,950 @@
# HG changeset patch
# User Kai Engert <kaie@kuix.de>
# 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 <limits.h>
+#include <errno.h>
+#include <stdio.h>
+#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 <path-to-policy-file>\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,
&currentString);
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

View File

@ -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 <kaie@redhat.com> - 3.38.0-4
- Backport upstream addition of nss-policy-check utility, rhbz#1428746
* Fri Jul 13 2018 Fedora Release Engineering <releng@fedoraproject.org> - 3.38.0-3
- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild