tor-browser

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

test_ocsp_caching.js (14514B)


      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 "use strict";
      6 
      7 // Checks various aspects of the OCSP cache, mainly to to ensure we do not fetch
      8 // responses more than necessary.
      9 
     10 var gFetchCount = 0;
     11 var gGoodOCSPResponse = null;
     12 var gResponsePattern = [];
     13 
     14 function respondWithGoodOCSP(request, response) {
     15  info("returning 200 OK");
     16  response.setStatusLine(request.httpVersion, 200, "OK");
     17  response.setHeader("Content-Type", "application/ocsp-response");
     18  response.write(gGoodOCSPResponse);
     19 }
     20 
     21 function respondWithSHA1OCSP(request, response) {
     22  info("returning 200 OK with sha-1 delegated response");
     23  response.setStatusLine(request.httpVersion, 200, "OK");
     24  response.setHeader("Content-Type", "application/ocsp-response");
     25 
     26  let args = [["good-delegated", "default-ee", "delegatedSHA1Signer", 0]];
     27  let responses = generateOCSPResponses(args, "ocsp_certs");
     28  response.write(responses[0]);
     29 }
     30 
     31 function respondWithError(request, response) {
     32  info("returning 500 Internal Server Error");
     33  response.setStatusLine(request.httpVersion, 500, "Internal Server Error");
     34  let body = "Refusing to return a response";
     35  response.bodyOutputStream.write(body, body.length);
     36 }
     37 
     38 function generateGoodOCSPResponse(thisUpdateSkew) {
     39  let args = [["good", "default-ee", "unused", thisUpdateSkew]];
     40  let responses = generateOCSPResponses(args, "ocsp_certs");
     41  return responses[0];
     42 }
     43 
     44 function add_ocsp_test(
     45  aHost,
     46  aExpectedResult,
     47  aResponses,
     48  aMessage,
     49  aOriginAttributes
     50 ) {
     51  add_connection_test(
     52    aHost,
     53    aExpectedResult,
     54    function () {
     55      clearSessionCache();
     56      gFetchCount = 0;
     57      gResponsePattern = aResponses;
     58    },
     59    function () {
     60      // check the number of requests matches the size of aResponses
     61      equal(gFetchCount, aResponses.length, aMessage);
     62    },
     63    null,
     64    aOriginAttributes
     65  );
     66 }
     67 
     68 function run_test() {
     69  do_get_profile();
     70  Services.prefs.setBoolPref("security.ssl.enable_ocsp_stapling", true);
     71  Services.prefs.setIntPref("security.OCSP.enabled", 1);
     72  add_tls_server_setup("OCSPStaplingServer", "ocsp_certs");
     73 
     74  let ocspResponder = new HttpServer();
     75  ocspResponder.registerPrefixHandler("/", function (request, response) {
     76    info("gFetchCount: " + gFetchCount);
     77    let responseFunction = gResponsePattern[gFetchCount];
     78    Assert.notEqual(undefined, responseFunction);
     79 
     80    ++gFetchCount;
     81    responseFunction(request, response);
     82  });
     83  ocspResponder.start(8888);
     84 
     85  add_tests();
     86 
     87  add_test(function () {
     88    ocspResponder.stop(run_next_test);
     89  });
     90  run_next_test();
     91 }
     92 
     93 function add_tests() {
     94  // Test that verifying a certificate with a "short lifetime" doesn't result
     95  // in OCSP fetching. Due to longevity requirements in our testing
     96  // infrastructure, the certificate we encounter is valid for a very long
     97  // time, so we have to define a "short lifetime" as something very long.
     98  add_test(function () {
     99    Services.prefs.setIntPref(
    100      "security.pki.cert_short_lifetime_in_days",
    101      12000
    102    );
    103    run_next_test();
    104  });
    105 
    106  add_ocsp_test(
    107    "ocsp-stapling-none.example.com",
    108    PRErrorCodeSuccess,
    109    [],
    110    "expected zero OCSP requests for a short-lived certificate"
    111  );
    112 
    113  add_test(function () {
    114    Services.prefs.setIntPref("security.pki.cert_short_lifetime_in_days", 100);
    115    run_next_test();
    116  });
    117 
    118  // If a "short lifetime" is something more reasonable, ensure that we do OCSP
    119  // fetching for this long-lived certificate.
    120 
    121  add_ocsp_test(
    122    "ocsp-stapling-none.example.com",
    123    PRErrorCodeSuccess,
    124    [respondWithError],
    125    "expected one OCSP request for a long-lived certificate"
    126  );
    127  add_test(function () {
    128    Services.prefs.clearUserPref("security.pki.cert_short_lifetime_in_days");
    129    run_next_test();
    130  });
    131  // ---------------------------------------------------------------------------
    132 
    133  // Reset state
    134  add_test(function () {
    135    clearOCSPCache();
    136    run_next_test();
    137  });
    138 
    139  // This test assumes that OCSPStaplingServer uses the same cert for
    140  // ocsp-stapling-unknown.example.com and ocsp-stapling-none.example.com.
    141 
    142  // Get an Unknown response for the *.example.com cert and put it in the
    143  // OCSP cache.
    144  add_ocsp_test(
    145    "ocsp-stapling-unknown.example.com",
    146    SEC_ERROR_OCSP_UNKNOWN_CERT,
    147    [],
    148    "Stapled Unknown response -> a fetch should not have been attempted"
    149  );
    150 
    151  // A failure to retrieve an OCSP response must result in the cached Unknown
    152  // response being recognized and honored.
    153  add_ocsp_test(
    154    "ocsp-stapling-none.example.com",
    155    SEC_ERROR_OCSP_UNKNOWN_CERT,
    156    [respondWithError, respondWithError],
    157    "No stapled response -> a fetch should have been attempted"
    158  );
    159 
    160  // A valid Good response from the OCSP responder must override the cached
    161  // Unknown response.
    162  //
    163  // Note that We need to make sure that the Unknown response and the Good
    164  // response have different thisUpdate timestamps; otherwise, the Good
    165  // response will be seen as "not newer" and it won't replace the existing
    166  // entry.
    167  add_test(function () {
    168    gGoodOCSPResponse = generateGoodOCSPResponse(1200);
    169    run_next_test();
    170  });
    171  add_ocsp_test(
    172    "ocsp-stapling-none.example.com",
    173    PRErrorCodeSuccess,
    174    [respondWithGoodOCSP],
    175    "Cached Unknown response, no stapled response -> a fetch" +
    176      " should have been attempted"
    177  );
    178 
    179  // The Good response retrieved from the previous fetch must have replaced
    180  // the Unknown response in the cache, resulting in the catched Good response
    181  // being returned and no fetch.
    182  add_ocsp_test(
    183    "ocsp-stapling-none.example.com",
    184    PRErrorCodeSuccess,
    185    [],
    186    "Cached Good response -> a fetch should not have been attempted"
    187  );
    188 
    189  // ---------------------------------------------------------------------------
    190 
    191  // Reset state
    192  add_test(function () {
    193    clearOCSPCache();
    194    run_next_test();
    195  });
    196 
    197  // A failure to retrieve an OCSP response will result in an error entry being
    198  // added to the cache.
    199  add_ocsp_test(
    200    "ocsp-stapling-none.example.com",
    201    PRErrorCodeSuccess,
    202    [respondWithError],
    203    "No stapled response -> a fetch should have been attempted"
    204  );
    205 
    206  // The error entry will prevent a fetch from happening for a while.
    207  add_ocsp_test(
    208    "ocsp-stapling-none.example.com",
    209    PRErrorCodeSuccess,
    210    [],
    211    "Noted OCSP server failure -> a fetch should not have been attempted"
    212  );
    213 
    214  // The error entry must not prevent a stapled OCSP response from being
    215  // honored.
    216  add_ocsp_test(
    217    "ocsp-stapling-revoked.example.com",
    218    SEC_ERROR_REVOKED_CERTIFICATE,
    219    [],
    220    "Stapled Revoked response -> a fetch should not have been attempted"
    221  );
    222 
    223  // ---------------------------------------------------------------------------
    224 
    225  // Ensure OCSP responses from signers with SHA1 certificates are OK. This
    226  // is included in the OCSP caching tests since there were OCSP cache-related
    227  // regressions when sha-1 telemetry probes were added.
    228  add_test(function () {
    229    clearOCSPCache();
    230    // set security.OCSP.require so that checking the OCSP signature fails
    231    Services.prefs.setBoolPref("security.OCSP.require", true);
    232    run_next_test();
    233  });
    234 
    235  add_ocsp_test(
    236    "ocsp-stapling-none.example.com",
    237    SEC_ERROR_OCSP_INVALID_SIGNING_CERT,
    238    [respondWithSHA1OCSP],
    239    "OCSP signing cert was issued with sha1 - should fail"
    240  );
    241 
    242  add_test(function () {
    243    Services.prefs.setBoolPref("security.OCSP.require", false);
    244    run_next_test();
    245  });
    246 
    247  // ---------------------------------------------------------------------------
    248 
    249  // Reset state
    250  add_test(function () {
    251    clearOCSPCache();
    252    run_next_test();
    253  });
    254 
    255  // This test makes sure that OCSP cache are isolated by firstPartyDomain.
    256 
    257  let gObservedCnt = 0;
    258  let protocolProxyService = Cc[
    259    "@mozilla.org/network/protocol-proxy-service;1"
    260  ].getService(Ci.nsIProtocolProxyService);
    261 
    262  // Observe all channels and make sure the firstPartyDomain in their loadInfo's
    263  // origin attributes are aFirstPartyDomain.
    264  function startObservingChannels(aFirstPartyDomain) {
    265    // We use a dummy proxy filter to catch all channels, even those that do not
    266    // generate an "http-on-modify-request" notification.
    267    let proxyFilter = {
    268      applyFilter(aChannel, aProxy, aCallback) {
    269        // We have the channel; provide it to the callback.
    270        if (aChannel.originalURI.spec == "http://localhost:8888/") {
    271          gObservedCnt++;
    272          equal(
    273            aChannel.loadInfo.originAttributes.firstPartyDomain,
    274            aFirstPartyDomain,
    275            "firstPartyDomain should match"
    276          );
    277        }
    278        // Pass on aProxy unmodified.
    279        aCallback.onProxyFilterResult(aProxy);
    280      },
    281    };
    282    protocolProxyService.registerChannelFilter(proxyFilter, 0);
    283    // Return the stop() function:
    284    return () => protocolProxyService.unregisterChannelFilter(proxyFilter);
    285  }
    286 
    287  let stopObservingChannels;
    288  add_test(function () {
    289    stopObservingChannels = startObservingChannels("foo.com");
    290    run_next_test();
    291  });
    292 
    293  // A good OCSP response will be cached.
    294  add_ocsp_test(
    295    "ocsp-stapling-none.example.com",
    296    PRErrorCodeSuccess,
    297    [respondWithGoodOCSP],
    298    "No stapled response (firstPartyDomain = foo.com) -> a fetch " +
    299      "should have been attempted",
    300    { firstPartyDomain: "foo.com" }
    301  );
    302 
    303  // The cache will prevent a fetch from happening.
    304  add_ocsp_test(
    305    "ocsp-stapling-none.example.com",
    306    PRErrorCodeSuccess,
    307    [],
    308    "Noted OCSP server failure (firstPartyDomain = foo.com) -> a " +
    309      "fetch should not have been attempted",
    310    { firstPartyDomain: "foo.com" }
    311  );
    312 
    313  add_test(function () {
    314    stopObservingChannels();
    315    equal(gObservedCnt, 1, "should have observed only 1 OCSP requests");
    316    gObservedCnt = 0;
    317    run_next_test();
    318  });
    319 
    320  add_test(function () {
    321    stopObservingChannels = startObservingChannels("bar.com");
    322    run_next_test();
    323  });
    324 
    325  // But using a different firstPartyDomain should result in a fetch.
    326  add_ocsp_test(
    327    "ocsp-stapling-none.example.com",
    328    PRErrorCodeSuccess,
    329    [respondWithGoodOCSP],
    330    "No stapled response (firstPartyDomain = bar.com) -> a fetch " +
    331      "should have been attempted",
    332    { firstPartyDomain: "bar.com" }
    333  );
    334 
    335  add_test(function () {
    336    stopObservingChannels();
    337    equal(gObservedCnt, 1, "should have observed only 1 OCSP requests");
    338    gObservedCnt = 0;
    339    run_next_test();
    340  });
    341 
    342  // ---------------------------------------------------------------------------
    343 
    344  // Reset state
    345  add_test(function () {
    346    clearOCSPCache();
    347    run_next_test();
    348  });
    349 
    350  // Test that the OCSP cache is not isolated by userContextId.
    351 
    352  // A good OCSP response will be cached.
    353  add_ocsp_test(
    354    "ocsp-stapling-none.example.com",
    355    PRErrorCodeSuccess,
    356    [respondWithGoodOCSP],
    357    "No stapled response (userContextId = 1) -> a fetch " +
    358      "should have been attempted",
    359    { userContextId: 1 }
    360  );
    361 
    362  // The cache will prevent a fetch from happening.
    363  add_ocsp_test(
    364    "ocsp-stapling-none.example.com",
    365    PRErrorCodeSuccess,
    366    [],
    367    "Noted OCSP server failure (userContextId = 1) -> a " +
    368      "fetch should not have been attempted",
    369    { userContextId: 1 }
    370  );
    371 
    372  // Fetching is prevented even if in a different userContextId.
    373  add_ocsp_test(
    374    "ocsp-stapling-none.example.com",
    375    PRErrorCodeSuccess,
    376    [],
    377    "Noted OCSP server failure (userContextId = 2) -> a " +
    378      "fetch should not have been attempted",
    379    { userContextId: 2 }
    380  );
    381 
    382  // ---------------------------------------------------------------------------
    383 
    384  // Reset state
    385  add_test(function () {
    386    clearOCSPCache();
    387    run_next_test();
    388  });
    389 
    390  // This test makes sure that OCSP cache are isolated by partitionKey.
    391 
    392  add_test(function () {
    393    Services.prefs.setBoolPref(
    394      "privacy.partition.network_state.ocsp_cache",
    395      true
    396    );
    397    run_next_test();
    398  });
    399 
    400  // A good OCSP response will be cached.
    401  add_ocsp_test(
    402    "ocsp-stapling-none.example.com",
    403    PRErrorCodeSuccess,
    404    [respondWithGoodOCSP],
    405    "No stapled response (partitionKey = (https,foo.com)) -> a fetch " +
    406      "should have been attempted",
    407    { partitionKey: "(https,foo.com)" }
    408  );
    409 
    410  // The cache will prevent a fetch from happening.
    411  add_ocsp_test(
    412    "ocsp-stapling-none.example.com",
    413    PRErrorCodeSuccess,
    414    [],
    415    "Noted OCSP server failure (partitionKey = (https,foo.com)) -> a " +
    416      "fetch should not have been attempted",
    417    { partitionKey: "(https,foo.com)" }
    418  );
    419 
    420  // Using a different partitionKey should result in a fetch.
    421  add_ocsp_test(
    422    "ocsp-stapling-none.example.com",
    423    PRErrorCodeSuccess,
    424    [respondWithGoodOCSP],
    425    "Noted OCSP server failure (partitionKey = (https,bar.com)) -> a " +
    426      "fetch should have been attempted",
    427    { partitionKey: "(https,bar.com)" }
    428  );
    429 
    430  // ---------------------------------------------------------------------------
    431 
    432  // Reset state
    433  add_test(function () {
    434    Services.prefs.clearUserPref("privacy.partition.network_state.ocsp_cache");
    435    clearOCSPCache();
    436    run_next_test();
    437  });
    438 
    439  // This test makes sure that OCSP cache are isolated by partitionKey in
    440  // private mode.
    441 
    442  // A good OCSP response will be cached.
    443  add_ocsp_test(
    444    "ocsp-stapling-none.example.com",
    445    PRErrorCodeSuccess,
    446    [respondWithGoodOCSP],
    447    "No stapled response (partitionKey = (https,foo.com)) -> a fetch " +
    448      "should have been attempted",
    449    { partitionKey: "(https,foo.com)", privateBrowsingId: 1 }
    450  );
    451 
    452  // The cache will prevent a fetch from happening.
    453  add_ocsp_test(
    454    "ocsp-stapling-none.example.com",
    455    PRErrorCodeSuccess,
    456    [],
    457    "Noted OCSP server failure (partitionKey = (https,foo.com)) -> a " +
    458      "fetch should not have been attempted",
    459    { partitionKey: "(https,foo.com)", privateBrowsingId: 1 }
    460  );
    461 
    462  // Using a different partitionKey should result in a fetch.
    463  add_ocsp_test(
    464    "ocsp-stapling-none.example.com",
    465    PRErrorCodeSuccess,
    466    [respondWithGoodOCSP],
    467    "Noted OCSP server failure (partitionKey = (https,bar.com)) -> a " +
    468      "fetch should have been attempted",
    469    { partitionKey: "(https,bar.com)", privateBrowsingId: 1 }
    470  );
    471 
    472  // ---------------------------------------------------------------------------
    473 
    474  // Reset state
    475  add_test(function () {
    476    clearOCSPCache();
    477    run_next_test();
    478  });
    479 }