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 }