1 // -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
2 // Any copyright is dedicated to the Public Domain.
3 // http://creativecommons.org/publicdomain/zero/1.0/
6 // Tests that PSM makes the correct determination of the security status of
7 // loads involving session resumption (i.e. when a TLS handshake bypasses the
8 // AuthCertificate callback).
11 const certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
15 registerCleanupFunction(() => {
16 Services.prefs.clearUserPref("security.OCSP.enabled");
19 Services.prefs.setIntPref("security.OCSP.enabled", 1);
21 addCertFromFile(certdb, "bad_certs/evroot.pem", "CTu,,");
22 addCertFromFile(certdb, "bad_certs/ev-test-intermediate.pem", ",,");
24 // For expired.example.com, the platform will make a connection that will fail.
25 // Using information gathered at that point, an override will be added and
26 // another connection will be made. This connection will succeed. At that point,
27 // as long as the session cache isn't cleared, subsequent new connections should
28 // use session resumption, thereby bypassing the AuthCertificate hook. We need
29 // to ensure that the correct security state is propagated to the new connection
30 // information object.
31 function add_resume_non_ev_with_override_test() {
32 // This adds the override and makes one successful connection.
33 add_cert_override_test("expired.example.com", SEC_ERROR_EXPIRED_CERTIFICATE);
35 // This connects again, using session resumption. Note that we don't clear
36 // the TLS session cache between these operations (that would defeat the
39 "expired.example.com",
42 transportSecurityInfo => {
43 ok(transportSecurityInfo.resumed, "connection should be resumed");
45 transportSecurityInfo.securityState &
46 Ci.nsIWebProgressListener.STATE_CERT_USER_OVERRIDDEN,
47 "expired.example.com should have STATE_CERT_USER_OVERRIDDEN flag"
50 transportSecurityInfo.succeededCertChain.length,
52 "expired.example.com should not have succeededCertChain set"
55 transportSecurityInfo.failedCertChain.length,
57 "expired.example.com should have failedCertChain set"
60 transportSecurityInfo.overridableErrorCategory,
61 Ci.nsITransportSecurityInfo.ERROR_TIME,
62 "expired.example.com should have time overridable error category"
65 !transportSecurityInfo.isExtendedValidation,
66 "expired.example.com should not have isExtendedValidation set"
69 let certOverrideService = Cc[
70 "@mozilla.org/security/certoverride;1"
71 ].getService(Ci.nsICertOverrideService);
72 certOverrideService.clearValidityOverride(
73 "expired.example.com",
81 // Helper function that adds a test that connects to ev-test.example.com and
82 // verifies that it validates as EV (or not, if we're running a non-debug
83 // build). This assumes that an appropriate OCSP responder is running or that
84 // good responses are cached.
85 function add_one_ev_test(resumed) {
87 "ev-test.example.com",
90 transportSecurityInfo => {
92 transportSecurityInfo.resumed,
94 "connection should be resumed or not resumed as expected"
98 transportSecurityInfo.securityState &
99 Ci.nsIWebProgressListener.STATE_CERT_USER_OVERRIDDEN
101 "ev-test.example.com should not have STATE_CERT_USER_OVERRIDDEN flag"
104 transportSecurityInfo.succeededCertChain.length,
106 "ev-test.example.com should have succeededCertChain set"
109 transportSecurityInfo.failedCertChain.length,
111 "ev-test.example.com should not have failedCertChain set"
114 transportSecurityInfo.overridableErrorCategory,
115 Ci.nsITransportSecurityInfo.ERROR_UNSET,
116 "ev-test.example.com should not have an overridable error category"
119 !gEVExpected || transportSecurityInfo.isExtendedValidation,
120 "ev-test.example.com should have isExtendedValidation set " +
121 "(or this is a non-debug build)"
127 // This test is similar, except with extended validation. We should connect
128 // successfully, and the certificate should be EV in debug builds. Without
129 // clearing the session cache, we should connect successfully again, this time
130 // with session resumption. The certificate should again be EV in debug builds.
131 function add_resume_ev_test() {
132 const SERVER_PORT = 8888;
133 let expectedRequestPaths = ["ev-test"];
134 let responseTypes = ["good"];
135 // Since we cache OCSP responses, we only ever actually serve one set.
137 // If we don't wrap this in an `add_test`, the OCSP responder will be running
138 // while we are actually running unrelated testcases, which can disrupt them.
140 ocspResponder = startOCSPResponder(
144 expectedRequestPaths,
145 expectedRequestPaths.slice(),
151 // We should be able to connect and verify the certificate as EV (in debug
153 add_one_ev_test(false);
154 // We should be able to connect again (using session resumption). In debug
155 // builds, the certificate should be noted as EV. Again, it's important that
156 // nothing clears the TLS cache in between these two operations.
157 add_one_ev_test(true);
160 ocspResponder.stop(run_next_test);
164 const GOOD_DOMAIN = "good.include-subdomains.pinning.example.com";
166 // Helper function that adds a test that connects to a domain that should
167 // succeed (but isn't EV) and verifies that its succeededCertChain gets set
169 function add_one_non_ev_test() {
174 transportSecurityInfo => {
177 transportSecurityInfo.securityState &
178 Ci.nsIWebProgressListener.STATE_CERT_USER_OVERRIDDEN
180 `${GOOD_DOMAIN} should not have STATE_CERT_USER_OVERRIDDEN flag`
183 transportSecurityInfo.succeededCertChain,
184 `${GOOD_DOMAIN} should have succeededCertChain set`
187 transportSecurityInfo.overridableErrorCategory,
189 `${GOOD_DOMAIN} should not have an overridable error category set`
192 !transportSecurityInfo.isExtendedValidation,
193 `${GOOD_DOMAIN} should not have isExtendedValidation set`
199 // This test is similar, except with non-extended validation. We should connect
200 // successfully, and the certificate should not be EV. Without clearing the
201 // session cache, we should connect successfully again, this time with session
202 // resumption. In this case, though, we want to ensure the succeededCertChain is
204 function add_resume_non_ev_test() {
205 add_one_non_ev_test();
206 add_one_non_ev_test();
209 const statsPtr = getSSLStatistics();
210 const toInt32 = ctypes.Int64.lo;
212 // Connect to the same domain with two origin attributes and check if any ssl
213 // session is resumed.
214 function add_origin_attributes_test(
228 let hitsBeforeConnect;
229 let missesBeforeConnect;
230 let expectedHits = resumeExpected ? 1 : 0;
231 let expectedMisses = 1 - expectedHits;
237 // Add the hits and misses before connection.
238 let stats = statsPtr.contents;
239 hitsBeforeConnect = toInt32(stats.sch_sid_cache_hits);
240 missesBeforeConnect = toInt32(stats.sch_sid_cache_misses);
243 let stats = statsPtr.contents;
245 toInt32(stats.sch_sid_cache_hits),
246 hitsBeforeConnect + expectedHits,
247 "Unexpected cache hits"
250 toInt32(stats.sch_sid_cache_misses),
251 missesBeforeConnect + expectedMisses,
252 "Unexpected cache misses"
260 function add_resumption_tests() {
261 add_resume_ev_test();
262 add_resume_non_ev_test();
263 add_resume_non_ev_with_override_test();
264 add_origin_attributes_test({}, {}, true);
265 add_origin_attributes_test({ userContextId: 1 }, { userContextId: 2 }, false);
266 add_origin_attributes_test({ userContextId: 3 }, { userContextId: 3 }, true);
267 add_origin_attributes_test(
268 { firstPartyDomain: "foo.com" },
269 { firstPartyDomain: "bar.com" },
272 add_origin_attributes_test(
273 { firstPartyDomain: "baz.com" },
274 { firstPartyDomain: "baz.com" },
279 function run_test() {
280 add_tls_server_setup("BadCertAndPinningServer", "bad_certs");
281 add_resumption_tests();
282 // Enable external session cache and reset the status.
283 add_test(function () {
284 Services.prefs.setBoolPref("network.ssl_tokens_cache_enabled", true);
285 certdb.clearOCSPCache();
289 add_resumption_tests();