291 lines
9.2 KiB
JavaScript
291 lines
9.2 KiB
JavaScript
// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
|
|
// Any copyright is dedicated to the Public Domain.
|
|
// http://creativecommons.org/publicdomain/zero/1.0/
|
|
"use strict";
|
|
|
|
// Tests that PSM makes the correct determination of the security status of
|
|
// loads involving session resumption (i.e. when a TLS handshake bypasses the
|
|
// AuthCertificate callback).
|
|
|
|
do_get_profile();
|
|
const certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
|
|
Ci.nsIX509CertDB
|
|
);
|
|
|
|
registerCleanupFunction(() => {
|
|
Services.prefs.clearUserPref("security.OCSP.enabled");
|
|
});
|
|
|
|
Services.prefs.setIntPref("security.OCSP.enabled", 1);
|
|
|
|
addCertFromFile(certdb, "bad_certs/evroot.pem", "CTu,,");
|
|
addCertFromFile(certdb, "bad_certs/ev-test-intermediate.pem", ",,");
|
|
|
|
// For expired.example.com, the platform will make a connection that will fail.
|
|
// Using information gathered at that point, an override will be added and
|
|
// another connection will be made. This connection will succeed. At that point,
|
|
// as long as the session cache isn't cleared, subsequent new connections should
|
|
// use session resumption, thereby bypassing the AuthCertificate hook. We need
|
|
// to ensure that the correct security state is propagated to the new connection
|
|
// information object.
|
|
function add_resume_non_ev_with_override_test() {
|
|
// This adds the override and makes one successful connection.
|
|
add_cert_override_test("expired.example.com", SEC_ERROR_EXPIRED_CERTIFICATE);
|
|
|
|
// This connects again, using session resumption. Note that we don't clear
|
|
// the TLS session cache between these operations (that would defeat the
|
|
// purpose).
|
|
add_connection_test(
|
|
"expired.example.com",
|
|
PRErrorCodeSuccess,
|
|
null,
|
|
transportSecurityInfo => {
|
|
ok(transportSecurityInfo.resumed, "connection should be resumed");
|
|
ok(
|
|
transportSecurityInfo.securityState &
|
|
Ci.nsIWebProgressListener.STATE_CERT_USER_OVERRIDDEN,
|
|
"expired.example.com should have STATE_CERT_USER_OVERRIDDEN flag"
|
|
);
|
|
equal(
|
|
transportSecurityInfo.succeededCertChain.length,
|
|
0,
|
|
"expired.example.com should not have succeededCertChain set"
|
|
);
|
|
equal(
|
|
transportSecurityInfo.failedCertChain.length,
|
|
2,
|
|
"expired.example.com should have failedCertChain set"
|
|
);
|
|
equal(
|
|
transportSecurityInfo.overridableErrorCategory,
|
|
Ci.nsITransportSecurityInfo.ERROR_TIME,
|
|
"expired.example.com should have time overridable error category"
|
|
);
|
|
ok(
|
|
!transportSecurityInfo.isExtendedValidation,
|
|
"expired.example.com should not have isExtendedValidation set"
|
|
);
|
|
|
|
let certOverrideService = Cc[
|
|
"@mozilla.org/security/certoverride;1"
|
|
].getService(Ci.nsICertOverrideService);
|
|
certOverrideService.clearValidityOverride(
|
|
"expired.example.com",
|
|
8443,
|
|
{}
|
|
);
|
|
}
|
|
);
|
|
}
|
|
|
|
// Helper function that adds a test that connects to ev-test.example.com and
|
|
// verifies that it validates as EV (or not, if we're running a non-debug
|
|
// build). This assumes that an appropriate OCSP responder is running or that
|
|
// good responses are cached.
|
|
function add_one_ev_test(resumed) {
|
|
add_connection_test(
|
|
"ev-test.example.com",
|
|
PRErrorCodeSuccess,
|
|
null,
|
|
transportSecurityInfo => {
|
|
equal(
|
|
transportSecurityInfo.resumed,
|
|
resumed,
|
|
"connection should be resumed or not resumed as expected"
|
|
);
|
|
ok(
|
|
!(
|
|
transportSecurityInfo.securityState &
|
|
Ci.nsIWebProgressListener.STATE_CERT_USER_OVERRIDDEN
|
|
),
|
|
"ev-test.example.com should not have STATE_CERT_USER_OVERRIDDEN flag"
|
|
);
|
|
equal(
|
|
transportSecurityInfo.succeededCertChain.length,
|
|
3,
|
|
"ev-test.example.com should have succeededCertChain set"
|
|
);
|
|
equal(
|
|
transportSecurityInfo.failedCertChain.length,
|
|
0,
|
|
"ev-test.example.com should not have failedCertChain set"
|
|
);
|
|
equal(
|
|
transportSecurityInfo.overridableErrorCategory,
|
|
Ci.nsITransportSecurityInfo.ERROR_UNSET,
|
|
"ev-test.example.com should not have an overridable error category"
|
|
);
|
|
ok(
|
|
!gEVExpected || transportSecurityInfo.isExtendedValidation,
|
|
"ev-test.example.com should have isExtendedValidation set " +
|
|
"(or this is a non-debug build)"
|
|
);
|
|
}
|
|
);
|
|
}
|
|
|
|
// This test is similar, except with extended validation. We should connect
|
|
// successfully, and the certificate should be EV in debug builds. Without
|
|
// clearing the session cache, we should connect successfully again, this time
|
|
// with session resumption. The certificate should again be EV in debug builds.
|
|
function add_resume_ev_test() {
|
|
const SERVER_PORT = 8888;
|
|
let expectedRequestPaths = ["ev-test"];
|
|
let responseTypes = ["good"];
|
|
// Since we cache OCSP responses, we only ever actually serve one set.
|
|
let ocspResponder;
|
|
// If we don't wrap this in an `add_test`, the OCSP responder will be running
|
|
// while we are actually running unrelated testcases, which can disrupt them.
|
|
add_test(() => {
|
|
ocspResponder = startOCSPResponder(
|
|
SERVER_PORT,
|
|
"localhost",
|
|
"bad_certs",
|
|
expectedRequestPaths,
|
|
expectedRequestPaths.slice(),
|
|
null,
|
|
responseTypes
|
|
);
|
|
run_next_test();
|
|
});
|
|
// We should be able to connect and verify the certificate as EV (in debug
|
|
// builds).
|
|
add_one_ev_test(false);
|
|
// We should be able to connect again (using session resumption). In debug
|
|
// builds, the certificate should be noted as EV. Again, it's important that
|
|
// nothing clears the TLS cache in between these two operations.
|
|
add_one_ev_test(true);
|
|
|
|
add_test(() => {
|
|
ocspResponder.stop(run_next_test);
|
|
});
|
|
}
|
|
|
|
const GOOD_DOMAIN = "good.include-subdomains.pinning.example.com";
|
|
|
|
// Helper function that adds a test that connects to a domain that should
|
|
// succeed (but isn't EV) and verifies that its succeededCertChain gets set
|
|
// appropriately.
|
|
function add_one_non_ev_test() {
|
|
add_connection_test(
|
|
GOOD_DOMAIN,
|
|
PRErrorCodeSuccess,
|
|
null,
|
|
transportSecurityInfo => {
|
|
ok(
|
|
!(
|
|
transportSecurityInfo.securityState &
|
|
Ci.nsIWebProgressListener.STATE_CERT_USER_OVERRIDDEN
|
|
),
|
|
`${GOOD_DOMAIN} should not have STATE_CERT_USER_OVERRIDDEN flag`
|
|
);
|
|
ok(
|
|
transportSecurityInfo.succeededCertChain,
|
|
`${GOOD_DOMAIN} should have succeededCertChain set`
|
|
);
|
|
equal(
|
|
transportSecurityInfo.overridableErrorCategory,
|
|
0,
|
|
`${GOOD_DOMAIN} should not have an overridable error category set`
|
|
);
|
|
ok(
|
|
!transportSecurityInfo.isExtendedValidation,
|
|
`${GOOD_DOMAIN} should not have isExtendedValidation set`
|
|
);
|
|
}
|
|
);
|
|
}
|
|
|
|
// This test is similar, except with non-extended validation. We should connect
|
|
// successfully, and the certificate should not be EV. Without clearing the
|
|
// session cache, we should connect successfully again, this time with session
|
|
// resumption. In this case, though, we want to ensure the succeededCertChain is
|
|
// set.
|
|
function add_resume_non_ev_test() {
|
|
add_one_non_ev_test();
|
|
add_one_non_ev_test();
|
|
}
|
|
|
|
const statsPtr = getSSLStatistics();
|
|
const toInt32 = ctypes.Int64.lo;
|
|
|
|
// Connect to the same domain with two origin attributes and check if any ssl
|
|
// session is resumed.
|
|
function add_origin_attributes_test(
|
|
originAttributes1,
|
|
originAttributes2,
|
|
resumeExpected
|
|
) {
|
|
add_connection_test(
|
|
GOOD_DOMAIN,
|
|
PRErrorCodeSuccess,
|
|
clearSessionCache,
|
|
null,
|
|
null,
|
|
originAttributes1
|
|
);
|
|
|
|
let hitsBeforeConnect;
|
|
let missesBeforeConnect;
|
|
let expectedHits = resumeExpected ? 1 : 0;
|
|
let expectedMisses = 1 - expectedHits;
|
|
|
|
add_connection_test(
|
|
GOOD_DOMAIN,
|
|
PRErrorCodeSuccess,
|
|
function () {
|
|
// Add the hits and misses before connection.
|
|
let stats = statsPtr.contents;
|
|
hitsBeforeConnect = toInt32(stats.sch_sid_cache_hits);
|
|
missesBeforeConnect = toInt32(stats.sch_sid_cache_misses);
|
|
},
|
|
function () {
|
|
let stats = statsPtr.contents;
|
|
equal(
|
|
toInt32(stats.sch_sid_cache_hits),
|
|
hitsBeforeConnect + expectedHits,
|
|
"Unexpected cache hits"
|
|
);
|
|
equal(
|
|
toInt32(stats.sch_sid_cache_misses),
|
|
missesBeforeConnect + expectedMisses,
|
|
"Unexpected cache misses"
|
|
);
|
|
},
|
|
null,
|
|
originAttributes2
|
|
);
|
|
}
|
|
|
|
function add_resumption_tests() {
|
|
add_resume_ev_test();
|
|
add_resume_non_ev_test();
|
|
add_resume_non_ev_with_override_test();
|
|
add_origin_attributes_test({}, {}, true);
|
|
add_origin_attributes_test({ userContextId: 1 }, { userContextId: 2 }, false);
|
|
add_origin_attributes_test({ userContextId: 3 }, { userContextId: 3 }, true);
|
|
add_origin_attributes_test(
|
|
{ firstPartyDomain: "foo.com" },
|
|
{ firstPartyDomain: "bar.com" },
|
|
false
|
|
);
|
|
add_origin_attributes_test(
|
|
{ firstPartyDomain: "baz.com" },
|
|
{ firstPartyDomain: "baz.com" },
|
|
true
|
|
);
|
|
}
|
|
|
|
function run_test() {
|
|
add_tls_server_setup("BadCertAndPinningServer", "bad_certs");
|
|
add_resumption_tests();
|
|
// Enable external session cache and reset the status.
|
|
add_test(function () {
|
|
Services.prefs.setBoolPref("network.ssl_tokens_cache_enabled", true);
|
|
certdb.clearOCSPCache();
|
|
run_next_test();
|
|
});
|
|
// Do tests again.
|
|
add_resumption_tests();
|
|
run_next_test();
|
|
}
|