/* 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" #include "plgetopt.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 /* private flags for policy check; should be in sync with pk11wrap/pk11pars.c */ #define SECMOD_FLAG_POLICY_CHECK_IDENTIFIER 0x01 #define SECMOD_FLAG_POLICY_CHECK_VALUE 0x02 static void printUsage(const char *progName) { fprintf(stderr, "Usage: %s [options] \n" "\tWhere options are:\n" "\t-f flag\t Set policy check flag (can be specified multiple times)\n" "\t \t Possible flags: \"identifier\" or \"value\"\n", progName); } 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; char *flags = NULL; PLOptState *optstate = NULL; PLOptStatus status; char *fullPath = NULL; int fullPathLen; char *progName = NULL; PRUint32 policyCheckFlags = 0; progName = PORT_Strdup(argv[0]); if (!progName) { result = 2; goto loser_no_shutdown; } /* Use PL_CreateOptState as SECU_ParseCommandLine ignores * positional parameters */ optstate = PL_CreateOptState(argc, argv, "f:"); if (!optstate) { result = 2; goto loser_no_shutdown; } while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) { switch (optstate->option) { case 0: /* positional parameter */ fullPath = PL_strdup(optstate->value); if (!fullPath) { result = 2; goto loser_no_shutdown; } goto breakout; case 'f': if (!PORT_Strcmp(optstate->value, "identifier")) { policyCheckFlags |= SECMOD_FLAG_POLICY_CHECK_IDENTIFIER; } else if (!PORT_Strcmp(optstate->value, "value")) { policyCheckFlags |= SECMOD_FLAG_POLICY_CHECK_VALUE; } else { fprintf(stderr, "not a valid flag: %s\n", optstate->value); printUsage(progName); result = 2; goto loser_no_shutdown; } break; default: printUsage(progName); result = 2; goto loser_no_shutdown; } } breakout: if (status != PL_OPT_OK || !fullPath) { printUsage(progName); result = 2; goto loser_no_shutdown; } fullPathLen = strlen(fullPath); if (!fullPathLen || PR_Access(fullPath, PR_ACCESS_READ_OK) != PR_SUCCESS) { fprintf(stderr, "Error: cannot read file %s\n", fullPath); result = 2; goto loser_no_shutdown; } if (fullPathLen >= PATH_MAX) { fprintf(stderr, "Error: filename parameter is too long\n"); result = 2; goto loser_no_shutdown; } path[0] = 0; filename = fullPath + fullPathLen - 1; while ((filename > fullPath) && (*filename != NSSUTIL_PATH_SEPARATOR[0])) { filename--; } if (filename == fullPath) { PORT_Strcpy(path, "."); } else { filename++; /* Go past the path separator. */ PORT_Strncat(path, fullPath, (filename - fullPath)); } 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_no_shutdown; } PR_SetEnv("NSS_POLICY_LOADED=0"); PR_SetEnv("NSS_POLICY_FAIL=0"); PR_SetEnv("NSS_POLICY_WARN=0"); flags = PORT_Strdup("internal,moduleDB,skipFirst,moduleDBOnly,critical,printPolicyFeedback"); if (!flags) { result = 2; goto loser_no_shutdown; } if (policyCheckFlags & SECMOD_FLAG_POLICY_CHECK_IDENTIFIER) { char *newflags; newflags = PORT_Realloc(flags, strlen(flags) + strlen(",policyCheckIdentifier") + 1); if (!newflags) { result = 2; goto loser_no_shutdown; } flags = newflags; PORT_Strcat(flags, ",policyCheckIdentifier"); } if (policyCheckFlags & SECMOD_FLAG_POLICY_CHECK_VALUE) { char *newflags; newflags = PORT_Realloc(flags, strlen(flags) + strlen(",policyCheckValue") + 1); if (!newflags) { result = 2; goto loser_no_shutdown; } flags = newflags; PORT_Strcat(flags, ",policyCheckValue"); } snprintf(moduleSpec, sizeof(moduleSpec), "name=\"Policy File\" " "parameters=\"configdir='sql:%s' " "secmod='%s' " "flags=readOnly,noCertDB,forceSecmodChoice,forceOpen\" " "NSS=\"flags=%s\"", path, filename, flags); 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; } loser_no_shutdown: if (optstate) { PL_DestroyOptState(optstate); } PORT_Free(flags); PORT_Free(progName); PORT_Free(fullPath); if (result == 2) { fprintf(stderr, "NSS-POLICY-FAIL\n"); } else if (result == 1) { fprintf(stderr, "NSS-POLICY-WARN\n"); } return result; }