1 // -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
2 // This Source Code Form is subject to the terms of the Mozilla Public
3 // License, v. 2.0. If a copy of the MPL was not distributed with this
4 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 // Tests the certificate overrides we allow.
8 // add_cert_override_test will queue a test that does the following:
9 // 1. Attempt to connect to the given host. This should fail with the
11 // 2. Add an override for that host/port/certificate.
12 // 3. Connect again. This should succeed.
16 // Enable the collection (during test) for all products so even products
17 // that don't collect the data will be able to run the test without failure.
18 Services.prefs.setBoolPref(
19 "toolkit.telemetry.testing.overrideProductsCheck",
23 function check_telemetry() {
24 let histogram = Services.telemetry
25 .getHistogramById("SSL_CERT_ERROR_OVERRIDES")
27 equal(histogram.values[0], 0, "Should have 0 unclassified values");
31 "Actual and expected SEC_ERROR_UNKNOWN_ISSUER values should match"
36 "Actual and expected SEC_ERROR_CA_CERT_INVALID values should match"
39 histogram.values[4] || 0,
41 "Actual and expected SEC_ERROR_UNTRUSTED_ISSUER values should match"
46 "Actual and expected SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE values should match"
49 histogram.values[6] || 0,
51 "Actual and expected SEC_ERROR_UNTRUSTED_CERT values should match"
54 histogram.values[7] || 0,
56 "Actual and expected SEC_ERROR_INADEQUATE_KEY_USAGE values should match"
61 "Actual and expected SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED values should match"
65 gIsDebugBuild ? 9 : 8,
66 "Actual and expected SSL_ERROR_BAD_CERT_DOMAIN values should match"
71 "Actual and expected SEC_ERROR_EXPIRED_CERTIFICATE values should match"
76 "Actual and expected MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY values should match"
81 "Actual and expected MOZILLA_PKIX_ERROR_V1_CERT_USED_AS_CA values should match"
86 "Actual and expected MOZILLA_PKIX_ERROR_INADEQUATE_KEY_SIZE values should match"
91 "Actual and expected MOZILLA_PKIX_ERROR_NOT_YET_VALID_CERTIFICATE values should match"
96 "Actual and expected MOZILLA_PKIX_ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE values should match"
101 "Actual and expected SEC_ERROR_INVALID_TIME values should match"
104 histogram.values[17],
106 "Actual and expected MOZILLA_PKIX_ERROR_EMPTY_ISSUER_NAME values should match"
109 histogram.values[19],
111 "Actual and expected MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT values should match"
114 histogram.values[20],
116 "Actual and expected MOZILLA_PKIX_ERROR_MITM_DETECTED values should match"
119 let keySizeHistogram = Services.telemetry
120 .getHistogramById("CERT_CHAIN_KEY_SIZE_STATUS")
123 keySizeHistogram.values[0],
125 "Actual and expected unchecked key size values should match"
128 keySizeHistogram.values[1],
129 gIsDebugBuild ? 17 : 15,
130 "Actual and expected successful verifications of 2048-bit keys should match"
133 keySizeHistogram.values[2] || 0,
135 "Actual and expected successful verifications of 1024-bit keys should match"
138 keySizeHistogram.values[3],
140 "Actual and expected verification failures unrelated to key size should match"
146 // Internally, specifying "port" -1 is the same as port 443. This tests that.
147 function run_port_equivalency_test(inPort, outPort) {
149 (inPort == 443 && outPort == -1) || (inPort == -1 && outPort == 443),
150 "The two specified ports must be -1 and 443 (in any order)"
152 let certOverrideService = Cc[
153 "@mozilla.org/security/certoverride;1"
154 ].getService(Ci.nsICertOverrideService);
155 let cert = constructCertFromFile("bad_certs/default-ee.pem");
156 let expectedTemporary = true;
157 certOverrideService.rememberValidityOverride(
164 let actualTemporary = {};
166 certOverrideService.hasMatchingOverride(
173 `override set on port ${inPort} should match port ${outPort}`
176 actualTemporary.value,
178 "input override temporary value should match output temporary value"
181 !certOverrideService.hasMatchingOverride("example.com", 563, {}, cert, {}),
182 `override set on port ${inPort} should not match port 563`
184 certOverrideService.clearValidityOverride("example.com", inPort, {});
186 !certOverrideService.hasMatchingOverride(
193 `override cleared on port ${inPort} should match port ${outPort}`
197 function run_test() {
198 run_port_equivalency_test(-1, 443);
199 run_port_equivalency_test(443, -1);
201 Services.prefs.setIntPref("security.OCSP.enabled", 1);
202 add_tls_server_setup("BadCertAndPinningServer", "bad_certs");
204 let fakeOCSPResponder = new HttpServer();
205 fakeOCSPResponder.registerPrefixHandler("/", function (request, response) {
206 response.setStatusLine(request.httpVersion, 500, "Internal Server Error");
208 fakeOCSPResponder.start(8888);
211 add_localhost_tests();
213 add_distrust_tests();
215 add_test(function () {
216 fakeOCSPResponder.stop(check_telemetry);
222 function add_simple_tests() {
223 add_cert_override_test("expired.example.com", SEC_ERROR_EXPIRED_CERTIFICATE);
224 add_cert_override_test(
225 "notyetvalid.example.com",
226 MOZILLA_PKIX_ERROR_NOT_YET_VALID_CERTIFICATE
228 add_cert_override_test("before-epoch.example.com", SEC_ERROR_INVALID_TIME);
229 add_cert_override_test(
230 "before-epoch-self-signed.example.com",
231 MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT
233 add_cert_override_test(
234 "selfsigned.example.com",
235 MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT
237 add_cert_override_test("unknownissuer.example.com", SEC_ERROR_UNKNOWN_ISSUER);
238 add_cert_override_test(
239 "expiredissuer.example.com",
240 SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE
242 add_cert_override_test(
243 "notyetvalidissuer.example.com",
244 MOZILLA_PKIX_ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE
246 add_cert_override_test(
247 "before-epoch-issuer.example.com",
248 SEC_ERROR_INVALID_TIME
250 add_cert_override_test(
251 "md5signature.example.com",
252 SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED
254 add_cert_override_test(
255 "emptyissuername.example.com",
256 MOZILLA_PKIX_ERROR_EMPTY_ISSUER_NAME
258 // This has name information in the subject alternative names extension,
259 // but not the subject common name.
260 add_cert_override_test("mismatch.example.com", SSL_ERROR_BAD_CERT_DOMAIN);
261 // This has name information in the subject common name but not the subject
262 // alternative names extension.
263 add_cert_override_test("mismatch-CN.example.com", SSL_ERROR_BAD_CERT_DOMAIN);
265 // A Microsoft IIS utility generates self-signed certificates with
266 // properties similar to the one this "host" will present.
267 add_cert_override_test(
268 "selfsigned-inadequateEKU.example.com",
269 MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT
272 add_prevented_cert_override_test(
273 "inadequatekeyusage.example.com",
274 SEC_ERROR_INADEQUATE_KEY_USAGE
277 // Test triggering the MitM detection. We don't set-up a proxy here. Just
278 // set the pref. Without the pref set we expect an unkown issuer error.
279 add_cert_override_test("mitm.example.com", SEC_ERROR_UNKNOWN_ISSUER);
280 add_test(function () {
281 Services.prefs.setStringPref(
282 "security.pki.mitm_canary_issuer",
285 let certOverrideService = Cc[
286 "@mozilla.org/security/certoverride;1"
287 ].getService(Ci.nsICertOverrideService);
288 certOverrideService.clearValidityOverride("mitm.example.com", 8443, {});
291 add_cert_override_test("mitm.example.com", MOZILLA_PKIX_ERROR_MITM_DETECTED);
292 add_test(function () {
293 Services.prefs.setStringPref(
294 "security.pki.mitm_canary_issuer",
297 let certOverrideService = Cc[
298 "@mozilla.org/security/certoverride;1"
299 ].getService(Ci.nsICertOverrideService);
300 certOverrideService.clearValidityOverride("mitm.example.com", 8443, {});
303 // If the canary issuer doesn't match the one we see, we exepct and unknown
305 add_cert_override_test("mitm.example.com", SEC_ERROR_UNKNOWN_ISSUER);
306 // If security.pki.mitm_canary_issuer.enabled is false, there should always
307 // be an unknown issuer error.
308 add_test(function () {
309 Services.prefs.setBoolPref(
310 "security.pki.mitm_canary_issuer.enabled",
313 let certOverrideService = Cc[
314 "@mozilla.org/security/certoverride;1"
315 ].getService(Ci.nsICertOverrideService);
316 certOverrideService.clearValidityOverride("mitm.example.com", 8443, {});
319 add_cert_override_test("mitm.example.com", SEC_ERROR_UNKNOWN_ISSUER);
320 add_test(function () {
321 Services.prefs.clearUserPref("security.pki.mitm_canary_issuer");
325 // This is intended to test the case where a verification has failed for one
326 // overridable reason (e.g. unknown issuer) but then, in the process of
327 // reporting that error, a non-overridable error is encountered. The
328 // non-overridable error should be prioritized.
329 add_test(function () {
330 let rootCert = constructCertFromFile("bad_certs/test-ca.pem");
331 setCertTrust(rootCert, ",,");
334 add_prevented_cert_override_test(
335 "nsCertTypeCritical.example.com",
336 SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION
338 add_test(function () {
339 let rootCert = constructCertFromFile("bad_certs/test-ca.pem");
340 setCertTrust(rootCert, "CTu,,");
344 // Bug 990603: Apache documentation has recommended generating a self-signed
345 // test certificate with basic constraints: CA:true. For compatibility, this
346 // is a scenario in which an override is allowed.
347 add_cert_override_test(
348 "self-signed-end-entity-with-cA-true.example.com",
349 MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT
352 add_cert_override_test(
353 "ca-used-as-end-entity.example.com",
354 MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY
357 // If an X.509 version 1 certificate is not a trust anchor, we will
358 // encounter an overridable error.
359 add_cert_override_test(
360 "end-entity-issued-by-v1-cert.example.com",
361 MOZILLA_PKIX_ERROR_V1_CERT_USED_AS_CA
363 // If we make that certificate a trust anchor, the connection will succeed.
364 add_test(function () {
365 let certOverrideService = Cc[
366 "@mozilla.org/security/certoverride;1"
367 ].getService(Ci.nsICertOverrideService);
368 certOverrideService.clearValidityOverride(
369 "end-entity-issued-by-v1-cert.example.com",
373 let v1Cert = constructCertFromFile("bad_certs/v1Cert.pem");
374 setCertTrust(v1Cert, "CTu,,");
379 "end-entity-issued-by-v1-cert.example.com",
382 // Reset the trust for that certificate.
383 add_test(function () {
384 let v1Cert = constructCertFromFile("bad_certs/v1Cert.pem");
385 setCertTrust(v1Cert, ",,");
390 // Due to compatibility issues, we allow overrides for certificates issued by
391 // certificates that are not valid CAs.
392 add_cert_override_test(
393 "end-entity-issued-by-non-CA.example.com",
394 SEC_ERROR_CA_CERT_INVALID
397 // This host presents a 1016-bit RSA key.
398 add_cert_override_test(
399 "inadequate-key-size-ee.example.com",
400 MOZILLA_PKIX_ERROR_INADEQUATE_KEY_SIZE
403 // The test root is not a built-in (by default), so the invalid dNSName entry
404 // in the subject alternative name extension is skipped.
406 "ipAddressAsDNSNameInSAN.example.com",
411 // Treat the test root like a built-in.
412 add_test(function () {
413 let rootCert = constructCertFromFile("bad_certs/test-ca.pem");
414 Services.prefs.setCharPref(
415 "security.test.built_in_root_hash",
416 rootCert.sha256Fingerprint
420 // If the root is a built-in, the invalid dNSName entry in the subject
421 // alternative name extension is not skipped, and this result in an error.
422 add_cert_override_test(
423 "ipAddressAsDNSNameInSAN.example.com",
424 SSL_ERROR_BAD_CERT_DOMAIN
426 // Reset the test root's built-in status.
427 add_test(function () {
428 Services.prefs.clearUserPref("security.test.built_in_root_hash");
433 add_cert_override_test("noValidNames.example.com", SSL_ERROR_BAD_CERT_DOMAIN);
434 add_cert_override_test(
435 "badSubjectAltNames.example.com",
436 SSL_ERROR_BAD_CERT_DOMAIN
439 add_cert_override_test(
440 "bug413909.xn--hxajbheg2az3al.xn--jxalpdlp",
441 SEC_ERROR_UNKNOWN_ISSUER
443 add_test(function () {
444 // At this point, the override for bug413909.xn--hxajbheg2az3al.xn--jxalpdlp
445 // is still valid. Do some additional tests relating to IDN handling.
446 let certOverrideService = Cc[
447 "@mozilla.org/security/certoverride;1"
448 ].getService(Ci.nsICertOverrideService);
449 let uri = Services.io.newURI(
450 "https://bug413909.xn--hxajbheg2az3al.xn--jxalpdlp"
452 let cert = constructCertFromFile("bad_certs/idn-certificate.pem");
454 certOverrideService.hasMatchingOverride(
461 "IDN certificate should have matching override using ascii host"
465 !certOverrideService.hasMatchingOverride(
472 /NS_ERROR_ILLEGAL_VALUE/,
473 "IDN certificate should not have matching override using (non-ascii) host"
475 let invalidHost = uri.asciiHost.replace(/./g, c =>
476 String.fromCharCode(c.charCodeAt(0) | 0x100)
480 !certOverrideService.hasMatchingOverride(
487 /NS_ERROR_ILLEGAL_VALUE/,
488 "hasMatchingOverride should not truncate high-bytes"
493 add_test(function () {
494 // Add a bunch of overrides...
495 let certOverrideService = Cc[
496 "@mozilla.org/security/certoverride;1"
497 ].getService(Ci.nsICertOverrideService);
498 let cert = constructCertFromFile("bad_certs/default-ee.pem");
499 certOverrideService.rememberValidityOverride(
507 certOverrideService.hasMatchingOverride("example.com", 443, {}, cert, {}),
508 "Should have added override for example.com:443"
510 certOverrideService.rememberValidityOverride(
517 certOverrideService.rememberValidityOverride("::1", 80, {}, cert, false);
519 certOverrideService.hasMatchingOverride("example.com", 80, {}, cert, {}),
520 "Should have added override for example.com:80"
522 certOverrideService.rememberValidityOverride(
530 certOverrideService.hasMatchingOverride("example.org", 443, {}, cert, {}),
531 "Should have added override for example.org:443"
534 certOverrideService.hasMatchingOverride("::1", 80, {}, cert, {}),
535 "Should have added override for [::1]:80"
537 // When in a private browsing context, overrides added in non-private
538 // contexts should match (but not vice-versa).
540 certOverrideService.hasMatchingOverride(
543 { privateBrowsingId: 1 },
547 "Should have override for example.org:443 with privateBrowsingId 1"
550 certOverrideService.hasMatchingOverride(
553 { privateBrowsingId: 2 },
557 "Should have override for example.org:443 with privateBrowsingId 2"
560 certOverrideService.hasMatchingOverride(
563 { firstPartyDomain: "example.org", userContextId: 1 },
567 "Should ignore firstPartyDomain and userContextId when checking overrides"
569 certOverrideService.rememberValidityOverride(
577 certOverrideService.hasMatchingOverride("example.org", 80, {}, cert, {}),
578 "Should have added override for example.org:80"
580 certOverrideService.rememberValidityOverride(
583 { firstPartyDomain: "example.org", userContextId: 1 },
588 certOverrideService.hasMatchingOverride(
595 "Should ignore firstPartyDomain and userContextId when adding overrides"
598 certOverrideService.hasMatchingOverride(
601 { firstPartyDomain: "example.com", userContextId: 2 },
605 "Should ignore firstPartyDomain and userContextId when checking overrides"
607 certOverrideService.rememberValidityOverride(
610 { privateBrowsingId: 1 },
615 certOverrideService.hasMatchingOverride(
618 { privateBrowsingId: 1 },
622 "Should have added override for example.test:443 with privateBrowsingId 1"
625 !certOverrideService.hasMatchingOverride(
628 { privateBrowsingId: 2 },
632 "Should not have override for example.test:443 with privateBrowsingId 2"
635 !certOverrideService.hasMatchingOverride(
642 "Should not have override for example.test:443 with non-private OriginAttributes"
645 certOverrideService.clearAllOverrides();
647 // And ensure they're all gone.
649 !certOverrideService.hasMatchingOverride(
656 "Should have removed override for example.com:443"
659 !certOverrideService.hasMatchingOverride("example.com", 80, {}, cert, {}),
660 "Should have removed override for example.com:80"
663 !certOverrideService.hasMatchingOverride(
670 "Should have removed override for example.org:443"
673 !certOverrideService.hasMatchingOverride("example.org", 80, {}, cert, {}),
674 "Should have removed override for example.org:80"
677 !certOverrideService.hasMatchingOverride(
680 { privateBrowsingId: 1 },
684 "Should have removed override for example.org:443 with privateBrowsingId 1"
691 function add_localhost_tests() {
692 add_cert_override_test("localhost", SEC_ERROR_UNKNOWN_ISSUER);
693 add_cert_override_test("127.0.0.1", SSL_ERROR_BAD_CERT_DOMAIN);
694 add_cert_override_test("::1", SSL_ERROR_BAD_CERT_DOMAIN);
697 function add_combo_tests() {
698 add_cert_override_test(
699 "mismatch-expired.example.com",
700 SSL_ERROR_BAD_CERT_DOMAIN
702 add_cert_override_test(
703 "mismatch-notYetValid.example.com",
704 SSL_ERROR_BAD_CERT_DOMAIN
706 add_cert_override_test(
707 "mismatch-untrusted.example.com",
708 SEC_ERROR_UNKNOWN_ISSUER
710 add_cert_override_test(
711 "untrusted-expired.example.com",
712 SEC_ERROR_UNKNOWN_ISSUER
714 add_cert_override_test(
715 "mismatch-untrusted-expired.example.com",
716 SEC_ERROR_UNKNOWN_ISSUER
719 add_cert_override_test(
720 "md5signature-expired.example.com",
721 SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED
724 add_cert_override_test(
725 "ca-used-as-end-entity-name-mismatch.example.com",
726 MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY
730 function add_distrust_tests() {
731 // Before we specifically distrust this certificate, it should be trusted.
732 add_connection_test("untrusted.example.com", PRErrorCodeSuccess);
735 "bad_certs/default-ee.pem",
736 "untrusted.example.com",
737 SEC_ERROR_UNTRUSTED_CERT
741 "bad_certs/other-test-ca.pem",
742 "untrustedissuer.example.com",
743 SEC_ERROR_UNTRUSTED_ISSUER
747 "bad_certs/test-ca.pem",
748 "ca-used-as-end-entity.example.com",
749 SEC_ERROR_UNTRUSTED_ISSUER
753 function add_distrust_test(certFileName, hostName, expectedResult) {
754 let certToDistrust = constructCertFromFile(certFileName);
756 add_test(function () {
757 // Add an entry to the NSS certDB that says to distrust the cert
758 setCertTrust(certToDistrust, "pu,,");
762 add_prevented_cert_override_test(hostName, expectedResult);
763 add_test(function () {
764 setCertTrust(certToDistrust, "u,,");