tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }