test_pinning.js (10576B)
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/. 5 // 6 // For all cases, the acceptable pinset includes only certificates pinned to 7 // Test End Entity Cert (signed by issuer testCA). Other certificates 8 // are issued by otherCA, which is never in the pinset but is a user-specified 9 // trust anchor. This test covers multiple cases: 10 // 11 // Pinned domain include-subdomains.pinning.example.com includes subdomains 12 // - PASS: include-subdomains.pinning.example.com serves a correct cert 13 // - PASS: good.include-subdomains.pinning.example.com serves a correct cert 14 // - FAIL (strict): bad.include-subdomains.pinning.example.com serves a cert 15 // not in the pinset 16 // - PASS (mitm): bad.include-subdomains.pinning.example.com serves a cert not 17 // in the pinset, but issued by a user-specified trust domain 18 // 19 // Pinned domain exclude-subdomains.pinning.example.com excludes subdomains 20 // - PASS: exclude-subdomains.pinning.example.com serves a correct cert 21 // - FAIL: exclude-subdomains.pinning.example.com serves an incorrect cert 22 // (TODO: test using verifyCertNow) 23 // - PASS: sub.exclude-subdomains.pinning.example.com serves an incorrect cert 24 25 "use strict"; 26 27 // Enable the collection (during test) for all products so even products 28 // that don't collect the data will be able to run the test without failure. 29 Services.prefs.setBoolPref( 30 "toolkit.telemetry.testing.overrideProductsCheck", 31 true 32 ); 33 34 do_get_profile(); // must be called before getting nsIX509CertDB 35 const certdb = Cc["@mozilla.org/security/x509certdb;1"].getService( 36 Ci.nsIX509CertDB 37 ); 38 39 function add_clear_override(host) { 40 add_test(function () { 41 let certOverrideService = Cc[ 42 "@mozilla.org/security/certoverride;1" 43 ].getService(Ci.nsICertOverrideService); 44 certOverrideService.clearValidityOverride(host, 8443, {}); 45 run_next_test(); 46 }); 47 } 48 49 function test_strict() { 50 // In strict mode, we always evaluate pinning data, regardless of whether the 51 // issuer is a built-in trust anchor. We only enforce pins that are not in 52 // test mode. 53 add_test(function () { 54 Services.prefs.setIntPref("security.cert_pinning.enforcement_level", 2); 55 run_next_test(); 56 }); 57 58 // Normally this is overridable. But, since we have pinning information for 59 // this host, we don't allow overrides. 60 add_prevented_cert_override_test( 61 "unknownissuer.include-subdomains.pinning.example.com", 62 SEC_ERROR_UNKNOWN_ISSUER 63 ); 64 add_clear_override("unknownissuer.include-subdomains.pinning.example.com"); 65 66 // Issued by otherCA, which is not in the pinset for pinning.example.com. 67 add_connection_test( 68 "bad.include-subdomains.pinning.example.com", 69 MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE 70 ); 71 72 // Check that using a FQDN doesn't bypass pinning. 73 add_connection_test( 74 "bad.include-subdomains.pinning.example.com.", 75 MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE 76 ); 77 // For some reason this is also navigable (see bug 1118522). 78 add_connection_test( 79 "bad.include-subdomains.pinning.example.com..", 80 MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE 81 ); 82 83 // These domains serve certs that match the pinset. 84 add_connection_test( 85 "include-subdomains.pinning.example.com", 86 PRErrorCodeSuccess 87 ); 88 add_connection_test( 89 "good.include-subdomains.pinning.example.com", 90 PRErrorCodeSuccess 91 ); 92 add_connection_test( 93 "exclude-subdomains.pinning.example.com", 94 PRErrorCodeSuccess 95 ); 96 97 // This domain serves a cert that doesn't match the pinset, but subdomains 98 // are excluded. 99 add_connection_test( 100 "sub.exclude-subdomains.pinning.example.com", 101 PRErrorCodeSuccess 102 ); 103 104 // This domain's pinset is exactly the same as 105 // include-subdomains.pinning.example.com, serves the same cert as 106 // bad.include-subdomains.pinning.example.com, but it should pass because 107 // it's in test_mode. 108 add_connection_test("test-mode.pinning.example.com", PRErrorCodeSuccess); 109 // Similarly, this pin is in test-mode, so it should be overridable. 110 add_cert_override_test( 111 "unknownissuer.test-mode.pinning.example.com", 112 SEC_ERROR_UNKNOWN_ISSUER 113 ); 114 add_clear_override("unknownissuer.test-mode.pinning.example.com"); 115 } 116 117 function test_mitm() { 118 // In MITM mode, we allow pinning to pass if the chain resolves to any 119 // user-specified trust anchor, even if it is not in the pinset. 120 add_test(function () { 121 Services.prefs.setIntPref("security.cert_pinning.enforcement_level", 1); 122 run_next_test(); 123 }); 124 125 add_connection_test( 126 "include-subdomains.pinning.example.com", 127 PRErrorCodeSuccess 128 ); 129 add_connection_test( 130 "good.include-subdomains.pinning.example.com", 131 PRErrorCodeSuccess 132 ); 133 134 // Normally this is overridable. But, since we have pinning information for 135 // this host, we don't allow overrides (since building a trusted chain fails, 136 // we have no reason to believe this was issued by a user-added trust 137 // anchor, so we can't allow overrides for it). 138 add_prevented_cert_override_test( 139 "unknownissuer.include-subdomains.pinning.example.com", 140 SEC_ERROR_UNKNOWN_ISSUER 141 ); 142 add_clear_override("unknownissuer.include-subdomains.pinning.example.com"); 143 144 // In this case, even though otherCA is not in the pinset, it is a 145 // user-specified trust anchor and the pinning check succeeds. 146 add_connection_test( 147 "bad.include-subdomains.pinning.example.com", 148 PRErrorCodeSuccess 149 ); 150 151 add_connection_test( 152 "exclude-subdomains.pinning.example.com", 153 PRErrorCodeSuccess 154 ); 155 add_connection_test( 156 "sub.exclude-subdomains.pinning.example.com", 157 PRErrorCodeSuccess 158 ); 159 add_connection_test("test-mode.pinning.example.com", PRErrorCodeSuccess); 160 add_cert_override_test( 161 "unknownissuer.test-mode.pinning.example.com", 162 SEC_ERROR_UNKNOWN_ISSUER 163 ); 164 add_clear_override("unknownissuer.test-mode.pinning.example.com"); 165 } 166 167 function test_disabled() { 168 // Disable pinning. 169 add_test(function () { 170 Services.prefs.setIntPref("security.cert_pinning.enforcement_level", 0); 171 run_next_test(); 172 }); 173 174 add_connection_test( 175 "include-subdomains.pinning.example.com", 176 PRErrorCodeSuccess 177 ); 178 add_connection_test( 179 "good.include-subdomains.pinning.example.com", 180 PRErrorCodeSuccess 181 ); 182 add_connection_test( 183 "bad.include-subdomains.pinning.example.com", 184 PRErrorCodeSuccess 185 ); 186 add_connection_test( 187 "exclude-subdomains.pinning.example.com", 188 PRErrorCodeSuccess 189 ); 190 add_connection_test( 191 "sub.exclude-subdomains.pinning.example.com", 192 PRErrorCodeSuccess 193 ); 194 add_connection_test("test-mode.pinning.example.com", PRErrorCodeSuccess); 195 196 add_cert_override_test( 197 "unknownissuer.include-subdomains.pinning.example.com", 198 SEC_ERROR_UNKNOWN_ISSUER 199 ); 200 add_clear_override("unknownissuer.include-subdomains.pinning.example.com"); 201 add_cert_override_test( 202 "unknownissuer.test-mode.pinning.example.com", 203 SEC_ERROR_UNKNOWN_ISSUER 204 ); 205 add_clear_override("unknownissuer.test-mode.pinning.example.com"); 206 } 207 208 function test_enforce_test_mode() { 209 // In enforce test mode, we always enforce all pins, even test pins. 210 add_test(function () { 211 Services.prefs.setIntPref("security.cert_pinning.enforcement_level", 3); 212 run_next_test(); 213 }); 214 215 // Normally this is overridable. But, since we have pinning information for 216 // this host, we don't allow overrides. 217 add_prevented_cert_override_test( 218 "unknownissuer.include-subdomains.pinning.example.com", 219 SEC_ERROR_UNKNOWN_ISSUER 220 ); 221 add_clear_override("unknownissuer.include-subdomains.pinning.example.com"); 222 223 // Issued by otherCA, which is not in the pinset for pinning.example.com. 224 add_connection_test( 225 "bad.include-subdomains.pinning.example.com", 226 MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE 227 ); 228 229 // These domains serve certs that match the pinset. 230 add_connection_test( 231 "include-subdomains.pinning.example.com", 232 PRErrorCodeSuccess 233 ); 234 add_connection_test( 235 "good.include-subdomains.pinning.example.com", 236 PRErrorCodeSuccess 237 ); 238 add_connection_test( 239 "exclude-subdomains.pinning.example.com", 240 PRErrorCodeSuccess 241 ); 242 243 // This domain serves a cert that doesn't match the pinset, but subdomains 244 // are excluded. 245 add_connection_test( 246 "sub.exclude-subdomains.pinning.example.com", 247 PRErrorCodeSuccess 248 ); 249 250 // This domain's pinset is exactly the same as 251 // include-subdomains.pinning.example.com, serves the same cert as 252 // bad.include-subdomains.pinning.example.com, is in test-mode, but we are 253 // enforcing test mode pins. 254 add_connection_test( 255 "test-mode.pinning.example.com", 256 MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE 257 ); 258 // Normally this is overridable. But, since we have pinning information for 259 // this host (and since we're enforcing test mode), we don't allow overrides. 260 add_prevented_cert_override_test( 261 "unknownissuer.test-mode.pinning.example.com", 262 SEC_ERROR_UNKNOWN_ISSUER 263 ); 264 add_clear_override("unknownissuer.test-mode.pinning.example.com"); 265 } 266 267 function check_pinning_telemetry() { 268 let prod_histogram = Services.telemetry 269 .getHistogramById("CERT_PINNING_RESULTS") 270 .snapshot(); 271 let test_histogram = Services.telemetry 272 .getHistogramById("CERT_PINNING_TEST_RESULTS") 273 .snapshot(); 274 // Because all of our test domains are pinned to user-specified trust 275 // anchors, effectively only strict mode and enforce test-mode get evaluated 276 equal( 277 prod_histogram.values[0], 278 4, 279 "Actual and expected prod (non-Mozilla) failure count should match" 280 ); 281 equal( 282 prod_histogram.values[1], 283 6, 284 "Actual and expected prod (non-Mozilla) success count should match" 285 ); 286 equal( 287 test_histogram.values[0], 288 2, 289 "Actual and expected test (non-Mozilla) failure count should match" 290 ); 291 equal( 292 test_histogram.values[1] || 0, 293 0, 294 "Actual and expected test (non-Mozilla) success count should match" 295 ); 296 297 run_next_test(); 298 } 299 300 function run_test() { 301 // Ensure that static pinning works when HPKP is disabled. 302 Services.prefs.setBoolPref("security.cert_pinning.hpkp.enabled", false); 303 304 add_tls_server_setup("BadCertAndPinningServer", "bad_certs"); 305 306 // Add a user-specified trust anchor. 307 addCertFromFile(certdb, "bad_certs/other-test-ca.pem", "CTu,u,u"); 308 309 test_strict(); 310 test_mitm(); 311 test_disabled(); 312 test_enforce_test_mode(); 313 314 add_test(function () { 315 check_pinning_telemetry(); 316 }); 317 run_next_test(); 318 }