michael@0: // -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: // This Source Code Form is subject to the terms of the Mozilla Public michael@0: // License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: // file, You can obtain one at http://mozilla.org/MPL/2.0/. michael@0: "use strict"; michael@0: michael@0: // Tests the certificate overrides we allow. michael@0: // add_cert_override_test will queue a test that does the following: michael@0: // 1. Attempt to connect to the given host. This should fail with the michael@0: // given error and override bits. michael@0: // 2. Add an override for that host/port/certificate/override bits. michael@0: // 3. Connect again. This should succeed. michael@0: michael@0: do_get_profile(); michael@0: let certOverrideService = Cc["@mozilla.org/security/certoverride;1"] michael@0: .getService(Ci.nsICertOverrideService); michael@0: michael@0: function add_cert_override(aHost, aExpectedBits, aSecurityInfo) { michael@0: let sslstatus = aSecurityInfo.QueryInterface(Ci.nsISSLStatusProvider) michael@0: .SSLStatus; michael@0: let bits = michael@0: (sslstatus.isUntrusted ? Ci.nsICertOverrideService.ERROR_UNTRUSTED : 0) | michael@0: (sslstatus.isDomainMismatch ? Ci.nsICertOverrideService.ERROR_MISMATCH : 0) | michael@0: (sslstatus.isNotValidAtThisTime ? Ci.nsICertOverrideService.ERROR_TIME : 0); michael@0: do_check_eq(bits, aExpectedBits); michael@0: let cert = sslstatus.serverCert; michael@0: certOverrideService.rememberValidityOverride(aHost, 8443, cert, aExpectedBits, michael@0: true); michael@0: } michael@0: michael@0: function add_cert_override_test(aHost, aExpectedBits, aExpectedError) { michael@0: add_connection_test(aHost, aExpectedError, null, michael@0: add_cert_override.bind(this, aHost, aExpectedBits)); michael@0: add_connection_test(aHost, Cr.NS_OK); michael@0: } michael@0: michael@0: function add_non_overridable_test(aHost, aExpectedError) { michael@0: add_connection_test( michael@0: aHost, getXPCOMStatusFromNSS(aExpectedError), null, michael@0: function (securityInfo) { michael@0: // bug 754369 - no SSLStatus probably means this is a non-overridable michael@0: // error, which is what we're testing (although it would be best to test michael@0: // this directly). michael@0: securityInfo.QueryInterface(Ci.nsISSLStatusProvider); michael@0: do_check_eq(securityInfo.SSLStatus, null); michael@0: }); michael@0: } michael@0: michael@0: function check_telemetry() { michael@0: let histogram = Cc["@mozilla.org/base/telemetry;1"] michael@0: .getService(Ci.nsITelemetry) michael@0: .getHistogramById("SSL_CERT_ERROR_OVERRIDES") michael@0: .snapshot(); michael@0: do_check_eq(histogram.counts[ 0], 0); michael@0: do_check_eq(histogram.counts[ 2], 8 + 1); // SEC_ERROR_UNKNOWN_ISSUER michael@0: do_check_eq(histogram.counts[ 3], 2); // SEC_ERROR_CA_CERT_INVALID michael@0: do_check_eq(histogram.counts[ 4], 0 + 5); // SEC_ERROR_UNTRUSTED_ISSUER michael@0: do_check_eq(histogram.counts[ 5], 0 + 1); // SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE michael@0: do_check_eq(histogram.counts[ 6], 0 + 1); // SEC_ERROR_UNTRUSTED_CERT michael@0: do_check_eq(histogram.counts[ 7], 0 + 1); // SEC_ERROR_INADEQUATE_KEY_USAGE michael@0: do_check_eq(histogram.counts[ 8], 2 + 2); // SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED michael@0: do_check_eq(histogram.counts[ 9], 4 + 4); // SSL_ERROR_BAD_CERT_DOMAIN michael@0: do_check_eq(histogram.counts[10], 5 + 5); // SEC_ERROR_EXPIRED_CERTIFICATE michael@0: run_next_test(); michael@0: } michael@0: michael@0: function run_test() { michael@0: add_tls_server_setup("BadCertServer"); michael@0: michael@0: let fakeOCSPResponder = new HttpServer(); michael@0: fakeOCSPResponder.registerPrefixHandler("/", function (request, response) { michael@0: response.setStatusLine(request.httpVersion, 500, "Internal Server Error"); michael@0: }); michael@0: fakeOCSPResponder.start(8080); michael@0: michael@0: add_tests_in_mode(true); michael@0: add_tests_in_mode(false); michael@0: michael@0: add_test(function () { michael@0: fakeOCSPResponder.stop(check_telemetry); michael@0: }); michael@0: michael@0: run_next_test(); michael@0: } michael@0: michael@0: function add_tests_in_mode(useMozillaPKIX) { michael@0: add_test(function () { michael@0: Services.prefs.setBoolPref("security.use_mozillapkix_verification", michael@0: useMozillaPKIX); michael@0: run_next_test(); michael@0: }); michael@0: michael@0: add_simple_tests(useMozillaPKIX); michael@0: add_combo_tests(useMozillaPKIX); michael@0: add_distrust_tests(useMozillaPKIX); michael@0: michael@0: add_test(function () { michael@0: certOverrideService.clearValidityOverride("all:temporary-certificates", 0); michael@0: run_next_test(); michael@0: }); michael@0: } michael@0: michael@0: function add_simple_tests(useMozillaPKIX) { michael@0: add_cert_override_test("expired.example.com", michael@0: Ci.nsICertOverrideService.ERROR_TIME, michael@0: getXPCOMStatusFromNSS(SEC_ERROR_EXPIRED_CERTIFICATE)); michael@0: if (useMozillaPKIX) { michael@0: add_cert_override_test("selfsigned.example.com", michael@0: Ci.nsICertOverrideService.ERROR_UNTRUSTED, michael@0: getXPCOMStatusFromNSS(SEC_ERROR_UNKNOWN_ISSUER)); michael@0: } else { michael@0: add_cert_override_test("selfsigned.example.com", michael@0: Ci.nsICertOverrideService.ERROR_UNTRUSTED, michael@0: getXPCOMStatusFromNSS(SEC_ERROR_CA_CERT_INVALID)); michael@0: } michael@0: add_cert_override_test("unknownissuer.example.com", michael@0: Ci.nsICertOverrideService.ERROR_UNTRUSTED, michael@0: getXPCOMStatusFromNSS(SEC_ERROR_UNKNOWN_ISSUER)); michael@0: add_cert_override_test("expiredissuer.example.com", michael@0: Ci.nsICertOverrideService.ERROR_UNTRUSTED, michael@0: getXPCOMStatusFromNSS( michael@0: useMozillaPKIX ? SEC_ERROR_UNKNOWN_ISSUER michael@0: : SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE)); michael@0: add_cert_override_test("md5signature.example.com", michael@0: Ci.nsICertOverrideService.ERROR_UNTRUSTED, michael@0: getXPCOMStatusFromNSS( michael@0: SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED)); michael@0: add_cert_override_test("mismatch.example.com", michael@0: Ci.nsICertOverrideService.ERROR_MISMATCH, michael@0: getXPCOMStatusFromNSS(SSL_ERROR_BAD_CERT_DOMAIN)); michael@0: michael@0: // A Microsoft IIS utility generates self-signed certificates with michael@0: // properties similar to the one this "host" will present (see michael@0: // tlsserver/generate_certs.sh). michael@0: // One of the errors classic verification collects is that this michael@0: // certificate has an inadequate key usage to sign a certificate michael@0: // (i.e. itself). As a result, to be able to override this, michael@0: // SEC_ERROR_INADEQUATE_KEY_USAGE must be overridable (although, michael@0: // confusingly, this isn't the main error reported). michael@0: // mozilla::pkix just says this certificate's issuer is unknown. michael@0: if (useMozillaPKIX) { michael@0: add_cert_override_test("selfsigned-inadequateEKU.example.com", michael@0: Ci.nsICertOverrideService.ERROR_UNTRUSTED, michael@0: getXPCOMStatusFromNSS(SEC_ERROR_UNKNOWN_ISSUER)); michael@0: } else { michael@0: add_cert_override_test("selfsigned-inadequateEKU.example.com", michael@0: Ci.nsICertOverrideService.ERROR_UNTRUSTED, michael@0: getXPCOMStatusFromNSS(SEC_ERROR_CA_CERT_INVALID)); michael@0: } michael@0: michael@0: // SEC_ERROR_INADEQUATE_KEY_USAGE is overridable in general for michael@0: // classic verification, but not for mozilla::pkix verification. michael@0: if (useMozillaPKIX) { michael@0: add_non_overridable_test("inadequatekeyusage.example.com", michael@0: SEC_ERROR_INADEQUATE_KEY_USAGE); michael@0: } else { michael@0: add_cert_override_test("inadequatekeyusage.example.com", michael@0: Ci.nsICertOverrideService.ERROR_UNTRUSTED, michael@0: getXPCOMStatusFromNSS(SEC_ERROR_INADEQUATE_KEY_USAGE)); michael@0: } michael@0: michael@0: // Bug 990603: Apache documentation has recommended generating a self-signed michael@0: // test certificate with basic constraints: CA:true. For compatibility, this michael@0: // is a scenario in which an override is allowed. michael@0: add_cert_override_test("self-signed-end-entity-with-cA-true.example.com", michael@0: Ci.nsICertOverrideService.ERROR_UNTRUSTED, michael@0: getXPCOMStatusFromNSS( michael@0: useMozillaPKIX ? SEC_ERROR_UNKNOWN_ISSUER michael@0: : SEC_ERROR_UNTRUSTED_ISSUER)); michael@0: } michael@0: michael@0: function add_combo_tests(useMozillaPKIX) { michael@0: // Note that "untrusted" here really is "unknown issuer" in the michael@0: // mozilla::pkix case. michael@0: michael@0: add_cert_override_test("mismatch-expired.example.com", michael@0: Ci.nsICertOverrideService.ERROR_MISMATCH | michael@0: Ci.nsICertOverrideService.ERROR_TIME, michael@0: getXPCOMStatusFromNSS(SSL_ERROR_BAD_CERT_DOMAIN)); michael@0: add_cert_override_test("mismatch-untrusted.example.com", michael@0: Ci.nsICertOverrideService.ERROR_MISMATCH | michael@0: Ci.nsICertOverrideService.ERROR_UNTRUSTED, michael@0: getXPCOMStatusFromNSS( michael@0: useMozillaPKIX ? SEC_ERROR_UNKNOWN_ISSUER michael@0: : SEC_ERROR_UNTRUSTED_ISSUER)); michael@0: add_cert_override_test("untrusted-expired.example.com", michael@0: Ci.nsICertOverrideService.ERROR_UNTRUSTED | michael@0: Ci.nsICertOverrideService.ERROR_TIME, michael@0: getXPCOMStatusFromNSS( michael@0: useMozillaPKIX ? SEC_ERROR_UNKNOWN_ISSUER michael@0: : SEC_ERROR_UNTRUSTED_ISSUER)); michael@0: add_cert_override_test("mismatch-untrusted-expired.example.com", michael@0: Ci.nsICertOverrideService.ERROR_MISMATCH | michael@0: Ci.nsICertOverrideService.ERROR_UNTRUSTED | michael@0: Ci.nsICertOverrideService.ERROR_TIME, michael@0: getXPCOMStatusFromNSS( michael@0: useMozillaPKIX ? SEC_ERROR_UNKNOWN_ISSUER michael@0: : SEC_ERROR_UNTRUSTED_ISSUER)); michael@0: michael@0: add_cert_override_test("md5signature-expired.example.com", michael@0: Ci.nsICertOverrideService.ERROR_UNTRUSTED | michael@0: Ci.nsICertOverrideService.ERROR_TIME, michael@0: getXPCOMStatusFromNSS( michael@0: SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED)); michael@0: } michael@0: michael@0: function add_distrust_tests(useMozillaPKIX) { michael@0: // Before we specifically distrust this certificate, it should be trusted. michael@0: add_connection_test("untrusted.example.com", Cr.NS_OK); michael@0: michael@0: // XXX(Bug 975777): Active distrust is an overridable error when NSS-based michael@0: // verification is used. michael@0: add_distrust_override_test("tlsserver/default-ee.der", michael@0: "untrusted.example.com", michael@0: getXPCOMStatusFromNSS(SEC_ERROR_UNTRUSTED_CERT), michael@0: useMozillaPKIX michael@0: ? getXPCOMStatusFromNSS(SEC_ERROR_UNTRUSTED_CERT) michael@0: : Cr.NS_OK); michael@0: michael@0: // XXX(Bug 975777): Active distrust is an overridable error when NSS-based michael@0: // verification is used. michael@0: add_distrust_override_test("tlsserver/other-test-ca.der", michael@0: "untrustedissuer.example.com", michael@0: getXPCOMStatusFromNSS(SEC_ERROR_UNTRUSTED_ISSUER), michael@0: useMozillaPKIX michael@0: ? getXPCOMStatusFromNSS(SEC_ERROR_UNTRUSTED_ISSUER) michael@0: : Cr.NS_OK); michael@0: } michael@0: michael@0: function add_distrust_override_test(certFileName, hostName, michael@0: expectedResultBefore, expectedResultAfter) { michael@0: let certToDistrust = constructCertFromFile(certFileName); michael@0: michael@0: add_test(function () { michael@0: // Add an entry to the NSS certDB that says to distrust the cert michael@0: setCertTrust(certToDistrust, "pu,,"); michael@0: clearSessionCache(); michael@0: run_next_test(); michael@0: }); michael@0: add_connection_test(hostName, expectedResultBefore, null, michael@0: function (securityInfo) { michael@0: securityInfo.QueryInterface(Ci.nsISSLStatusProvider); michael@0: // XXX(Bug 754369): SSLStatus isn't available for michael@0: // non-overridable errors. michael@0: if (securityInfo.SSLStatus) { michael@0: certOverrideService.rememberValidityOverride( michael@0: hostName, 8443, securityInfo.SSLStatus.serverCert, michael@0: Ci.nsICertOverrideService.ERROR_UNTRUSTED, true); michael@0: } else { michael@0: // A missing SSLStatus probably means (due to bug michael@0: // 754369) that the error was non-overridable, which michael@0: // is what we're trying to test, though we'd rather michael@0: // not test it this way. michael@0: do_check_neq(expectedResultAfter, Cr.NS_OK); michael@0: } michael@0: clearSessionCache(); michael@0: }); michael@0: add_connection_test(hostName, expectedResultAfter, null, michael@0: function () { michael@0: setCertTrust(certToDistrust, "u,,"); michael@0: }); michael@0: }