Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | // -*- indent-tabs-mode: nil; js-indent-level: 2 -*- |
michael@0 | 2 | // This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | // License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. |
michael@0 | 5 | // |
michael@0 | 6 | // For all cases, the acceptable pinset includes only certificates pinned to |
michael@0 | 7 | // Test End Entity Cert (signed by issuer testCA). Other certificates |
michael@0 | 8 | // are issued by otherCA, which is never in the pinset but is a user-specified |
michael@0 | 9 | // trust anchor. This test covers multiple cases: |
michael@0 | 10 | // |
michael@0 | 11 | // Pinned domain include-subdomains.pinning.example.com includes subdomains |
michael@0 | 12 | // - PASS: include-subdomains.pinning.example.com serves a correct cert |
michael@0 | 13 | // - PASS: good.include-subdomains.pinning.example.com serves a correct cert |
michael@0 | 14 | // - FAIL (strict): bad.include-subdomains.pinning.example.com serves a cert |
michael@0 | 15 | // not in the pinset |
michael@0 | 16 | // - PASS (mitm): bad.include-subdomains.pinning.example.com serves a cert not |
michael@0 | 17 | // in the pinset, but issued by a user-specified trust domain |
michael@0 | 18 | // |
michael@0 | 19 | // Pinned domain exclude-subdomains.pinning.example.com excludes subdomains |
michael@0 | 20 | // - PASS: exclude-subdomains.pinning.example.com serves a correct cert |
michael@0 | 21 | // - FAIL: exclude-subdomains.pinning.example.com services an incorrect cert |
michael@0 | 22 | // (TODO: test using verifyCertnow) |
michael@0 | 23 | // - PASS: sub.exclude-subdomains.pinning.example.com serves an incorrect cert |
michael@0 | 24 | |
michael@0 | 25 | "use strict"; |
michael@0 | 26 | |
michael@0 | 27 | do_get_profile(); // must be called before getting nsIX509CertDB |
michael@0 | 28 | const certdb = Cc["@mozilla.org/security/x509certdb;1"] |
michael@0 | 29 | .getService(Ci.nsIX509CertDB); |
michael@0 | 30 | |
michael@0 | 31 | function test_strict() { |
michael@0 | 32 | // In strict mode, we always evaluate pinning data, regardless of whether the |
michael@0 | 33 | // issuer is a built-in trust anchor. We only enforce pins that are not in |
michael@0 | 34 | // test mode. |
michael@0 | 35 | add_test(function() { |
michael@0 | 36 | Services.prefs.setIntPref("security.cert_pinning.enforcement_level", 2); |
michael@0 | 37 | run_next_test(); |
michael@0 | 38 | }); |
michael@0 | 39 | |
michael@0 | 40 | // If a host should be pinned but other errors (particularly overridable |
michael@0 | 41 | // errors) like 'unknown issuer' are encountered, the pinning error takes |
michael@0 | 42 | // precedence. This prevents overrides for such hosts. |
michael@0 | 43 | add_connection_test("unknownissuer.include-subdomains.pinning.example.com", |
michael@0 | 44 | getXPCOMStatusFromNSS(MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE)); |
michael@0 | 45 | |
michael@0 | 46 | // Issued by otherCA, which is not in the pinset for pinning.example.com. |
michael@0 | 47 | add_connection_test("bad.include-subdomains.pinning.example.com", |
michael@0 | 48 | getXPCOMStatusFromNSS(MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE)); |
michael@0 | 49 | |
michael@0 | 50 | // These domains serve certs that match the pinset. |
michael@0 | 51 | add_connection_test("include-subdomains.pinning.example.com", Cr.NS_OK); |
michael@0 | 52 | add_connection_test("good.include-subdomains.pinning.example.com", Cr.NS_OK); |
michael@0 | 53 | add_connection_test("exclude-subdomains.pinning.example.com", Cr.NS_OK); |
michael@0 | 54 | |
michael@0 | 55 | // This domain serves a cert that doesn't match the pinset, but subdomains |
michael@0 | 56 | // are excluded. |
michael@0 | 57 | add_connection_test("sub.exclude-subdomains.pinning.example.com", Cr.NS_OK); |
michael@0 | 58 | |
michael@0 | 59 | // This domain's pinset is exactly the same as |
michael@0 | 60 | // include-subdomains.pinning.example.com, serves the same cert as |
michael@0 | 61 | // bad.include-subdomains.pinning.example.com, but it should pass because |
michael@0 | 62 | // it's in test_mode. |
michael@0 | 63 | add_connection_test("test-mode.pinning.example.com", Cr.NS_OK); |
michael@0 | 64 | } |
michael@0 | 65 | |
michael@0 | 66 | function test_mitm() { |
michael@0 | 67 | // In MITM mode, we allow pinning to pass if the chain resolves to any |
michael@0 | 68 | // user-specified trust anchor, even if it is not in the pinset. |
michael@0 | 69 | add_test(function() { |
michael@0 | 70 | Services.prefs.setIntPref("security.cert_pinning.enforcement_level", 1); |
michael@0 | 71 | run_next_test(); |
michael@0 | 72 | }); |
michael@0 | 73 | |
michael@0 | 74 | add_connection_test("include-subdomains.pinning.example.com", Cr.NS_OK); |
michael@0 | 75 | add_connection_test("good.include-subdomains.pinning.example.com", Cr.NS_OK); |
michael@0 | 76 | |
michael@0 | 77 | add_connection_test("unknownissuer.include-subdomains.pinning.example.com", |
michael@0 | 78 | getXPCOMStatusFromNSS(SEC_ERROR_UNKNOWN_ISSUER)); |
michael@0 | 79 | |
michael@0 | 80 | // In this case, even though otherCA is not in the pinset, it is a |
michael@0 | 81 | // user-specified trust anchor and the pinning check succeeds. |
michael@0 | 82 | add_connection_test("bad.include-subdomains.pinning.example.com", Cr.NS_OK); |
michael@0 | 83 | |
michael@0 | 84 | add_connection_test("exclude-subdomains.pinning.example.com", Cr.NS_OK); |
michael@0 | 85 | add_connection_test("sub.exclude-subdomains.pinning.example.com", Cr.NS_OK); |
michael@0 | 86 | add_connection_test("test-mode.pinning.example.com", Cr.NS_OK); |
michael@0 | 87 | }; |
michael@0 | 88 | |
michael@0 | 89 | function test_disabled() { |
michael@0 | 90 | // Disable pinning. |
michael@0 | 91 | add_test(function() { |
michael@0 | 92 | Services.prefs.setIntPref("security.cert_pinning.enforcement_level", 0); |
michael@0 | 93 | run_next_test(); |
michael@0 | 94 | }); |
michael@0 | 95 | |
michael@0 | 96 | add_connection_test("include-subdomains.pinning.example.com", Cr.NS_OK); |
michael@0 | 97 | add_connection_test("good.include-subdomains.pinning.example.com", Cr.NS_OK); |
michael@0 | 98 | add_connection_test("bad.include-subdomains.pinning.example.com", Cr.NS_OK); |
michael@0 | 99 | add_connection_test("exclude-subdomains.pinning.example.com", Cr.NS_OK); |
michael@0 | 100 | add_connection_test("sub.exclude-subdomains.pinning.example.com", Cr.NS_OK); |
michael@0 | 101 | add_connection_test("test-mode.pinning.example.com", Cr.NS_OK); |
michael@0 | 102 | |
michael@0 | 103 | add_connection_test("unknownissuer.include-subdomains.pinning.example.com", |
michael@0 | 104 | getXPCOMStatusFromNSS(SEC_ERROR_UNKNOWN_ISSUER)); |
michael@0 | 105 | } |
michael@0 | 106 | |
michael@0 | 107 | function test_enforce_test_mode() { |
michael@0 | 108 | // In enforce test mode, we always enforce all pins, even test pins. |
michael@0 | 109 | add_test(function() { |
michael@0 | 110 | Services.prefs.setIntPref("security.cert_pinning.enforcement_level", 3); |
michael@0 | 111 | run_next_test(); |
michael@0 | 112 | }); |
michael@0 | 113 | |
michael@0 | 114 | add_connection_test("unknownissuer.include-subdomains.pinning.example.com", |
michael@0 | 115 | getXPCOMStatusFromNSS(MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE)); |
michael@0 | 116 | |
michael@0 | 117 | // Issued by otherCA, which is not in the pinset for pinning.example.com. |
michael@0 | 118 | add_connection_test("bad.include-subdomains.pinning.example.com", |
michael@0 | 119 | getXPCOMStatusFromNSS(MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE)); |
michael@0 | 120 | |
michael@0 | 121 | // These domains serve certs that match the pinset. |
michael@0 | 122 | add_connection_test("include-subdomains.pinning.example.com", Cr.NS_OK); |
michael@0 | 123 | add_connection_test("good.include-subdomains.pinning.example.com", Cr.NS_OK); |
michael@0 | 124 | add_connection_test("exclude-subdomains.pinning.example.com", Cr.NS_OK); |
michael@0 | 125 | |
michael@0 | 126 | // This domain serves a cert that doesn't match the pinset, but subdomains |
michael@0 | 127 | // are excluded. |
michael@0 | 128 | add_connection_test("sub.exclude-subdomains.pinning.example.com", Cr.NS_OK); |
michael@0 | 129 | |
michael@0 | 130 | // This domain's pinset is exactly the same as |
michael@0 | 131 | // include-subdomains.pinning.example.com, serves the same cert as |
michael@0 | 132 | // bad.include-subdomains.pinning.example.com, is in test-mode, but we are |
michael@0 | 133 | // enforcing test mode pins. |
michael@0 | 134 | add_connection_test("test-mode.pinning.example.com", |
michael@0 | 135 | getXPCOMStatusFromNSS(MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE)); |
michael@0 | 136 | } |
michael@0 | 137 | |
michael@0 | 138 | function check_pinning_telemetry() { |
michael@0 | 139 | let service = Cc["@mozilla.org/base/telemetry;1"].getService(Ci.nsITelemetry); |
michael@0 | 140 | let prod_histogram = service.getHistogramById("CERT_PINNING_RESULTS") |
michael@0 | 141 | .snapshot(); |
michael@0 | 142 | let test_histogram = service.getHistogramById("CERT_PINNING_TEST_RESULTS") |
michael@0 | 143 | .snapshot(); |
michael@0 | 144 | // Because all of our test domains are pinned to user-specified trust |
michael@0 | 145 | // anchors, effectively only strict mode and enforce test-mode get evaluated |
michael@0 | 146 | do_check_eq(prod_histogram.counts[0], 4); // Failure count |
michael@0 | 147 | do_check_eq(prod_histogram.counts[1], 4); // Success count |
michael@0 | 148 | do_check_eq(test_histogram.counts[0], 2); // Failure count |
michael@0 | 149 | do_check_eq(test_histogram.counts[1], 0); // Success count |
michael@0 | 150 | |
michael@0 | 151 | let moz_prod_histogram = service.getHistogramById("CERT_PINNING_MOZ_RESULTS") |
michael@0 | 152 | .snapshot(); |
michael@0 | 153 | let moz_test_histogram = |
michael@0 | 154 | service.getHistogramById("CERT_PINNING_MOZ_TEST_RESULTS").snapshot(); |
michael@0 | 155 | do_check_eq(moz_prod_histogram.counts[0], 0); // Failure count |
michael@0 | 156 | do_check_eq(moz_prod_histogram.counts[1], 0); // Success count |
michael@0 | 157 | do_check_eq(moz_test_histogram.counts[0], 0); // Failure count |
michael@0 | 158 | do_check_eq(moz_test_histogram.counts[1], 0); // Success count |
michael@0 | 159 | |
michael@0 | 160 | let per_host_histogram = |
michael@0 | 161 | service.getHistogramById("CERT_PINNING_MOZ_RESULTS_BY_HOST").snapshot(); |
michael@0 | 162 | do_check_eq(per_host_histogram.counts[0], 0); // Failure count |
michael@0 | 163 | do_check_eq(per_host_histogram.counts[1], 2); // Success count |
michael@0 | 164 | run_next_test(); |
michael@0 | 165 | } |
michael@0 | 166 | |
michael@0 | 167 | function run_test() { |
michael@0 | 168 | add_tls_server_setup("BadCertServer"); |
michael@0 | 169 | |
michael@0 | 170 | // Add a user-specified trust anchor. |
michael@0 | 171 | addCertFromFile(certdb, "tlsserver/other-test-ca.der", "CTu,u,u"); |
michael@0 | 172 | |
michael@0 | 173 | test_strict(); |
michael@0 | 174 | test_mitm(); |
michael@0 | 175 | test_disabled(); |
michael@0 | 176 | test_enforce_test_mode(); |
michael@0 | 177 | |
michael@0 | 178 | add_test(function () { |
michael@0 | 179 | check_pinning_telemetry(); |
michael@0 | 180 | }); |
michael@0 | 181 | run_next_test(); |
michael@0 | 182 | } |