summaryrefslogtreecommitdiffstats
path: root/security/manager/ssl/tests/unit/test_session_resumption.js
blob: eea6b5f54f8bfc373a94b2b962a4f8523ca2c41b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
// -*- 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);

do_register_cleanup(() => {
  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",
                         Ci.nsICertOverrideService.ERROR_TIME,
                         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.securityState &
         Ci.nsIWebProgressListener.STATE_CERT_USER_OVERRIDDEN,
         "expired.example.com should have STATE_CERT_USER_OVERRIDDEN flag");
      let sslStatus = transportSecurityInfo
                        .QueryInterface(Ci.nsISSLStatusProvider)
                        .SSLStatus;
      ok(!sslStatus.isDomainMismatch,
         "expired.example.com should not have isDomainMismatch set");
      ok(sslStatus.isNotValidAtThisTime,
         "expired.example.com should have isNotValidAtThisTime set");
      ok(!sslStatus.isUntrusted,
         "expired.example.com should not have isUntrusted set");
      ok(!sslStatus.isExtendedValidation,
         "expired.example.com should not have isExtendedValidation set");
    }
  );
}

// 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() {
  add_connection_test("ev-test.example.com", PRErrorCodeSuccess, null,
    (transportSecurityInfo) => {
      ok(!(transportSecurityInfo.securityState &
           Ci.nsIWebProgressListener.STATE_CERT_USER_OVERRIDDEN),
         "ev-test.example.com should not have STATE_CERT_USER_OVERRIDDEN flag");
      let sslStatus = transportSecurityInfo
                        .QueryInterface(Ci.nsISSLStatusProvider)
                        .SSLStatus;
      ok(!sslStatus.isDomainMismatch,
         "ev-test.example.com should not have isDomainMismatch set");
      ok(!sslStatus.isNotValidAtThisTime,
         "ev-test.example.com should not have isNotValidAtThisTime set");
      ok(!sslStatus.isUntrusted,
         "ev-test.example.com should not have isUntrusted set");
      ok(!gEVExpected || sslStatus.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 = gEVExpected ? [ "ev-test-intermediate", "ev-test" ]
                                         : [ "ev-test" ];
  let responseTypes = gEVExpected ? [ "good", "good" ] : [ "good" ];
  // Since we cache OCSP responses, we only ever actually serve one set.
  let ocspResponder = startOCSPResponder(SERVER_PORT, "localhost", "bad_certs",
                                         expectedRequestPaths,
                                         expectedRequestPaths.slice(),
                                         null, responseTypes);
  // We should be able to connect and verify the certificate as EV (in debug
  // builds).
  add_one_ev_test();
  // 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();

  add_test(() => {
    ocspResponder.stop(run_next_test);
  });
}

const statsPtr = getSSLStatistics();
const toInt32 = ctypes.Int64.lo;
const GOOD_DOMAIN = "good.include-subdomains.pinning.example.com";

// 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 run_test() {
  add_tls_server_setup("BadCertServer", "bad_certs");
  add_resume_non_ev_with_override_test();
  add_resume_ev_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);
  run_next_test();
}