Bug 1316283 - Isolate SSL session cache by origin attributes. r=keeler
authorJonathan Hao <jhao@mozilla.com>
Fri, 25 Nov 2016 20:07:57 +0800
changeset 445401 9aba8184664ddfca0ae5c95d9ab5f7e8daab049e
parent 445400 2e51142370f3412d4db29ef10b8e09d665a06052
child 445402 08264c30352ddcb6c2c98b5def210fbfac6fc983
push id37495
push userbmo:jyavenard@mozilla.com
push dateTue, 29 Nov 2016 14:52:39 +0000
reviewerskeeler
bugs1316283
milestone53.0a1
Bug 1316283 - Isolate SSL session cache by origin attributes. r=keeler
security/manager/ssl/nsNSSIOLayer.cpp
security/manager/ssl/tests/unit/head_psm.js
security/manager/ssl/tests/unit/test_session_resumption.js
security/nss.symbols
--- a/security/manager/ssl/nsNSSIOLayer.cpp
+++ b/security/manager/ssl/nsNSSIOLayer.cpp
@@ -2561,16 +2561,19 @@ nsSSLIOLayerSetOptions(PRFileDesc* fd, b
     peerId.AppendLiteral("private:");
   }
   if (flags & nsISocketProvider::MITM_OK) {
     peerId.AppendLiteral("bypassAuth:");
   }
   peerId.Append(host);
   peerId.Append(':');
   peerId.AppendInt(port);
+  nsAutoCString suffix;
+  infoObject->GetOriginAttributes().CreateSuffix(suffix);
+  peerId.Append(suffix);
   if (SECSuccess != SSL_SetSockPeerID(fd, peerId.get())) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
 nsresult
--- a/security/manager/ssl/tests/unit/head_psm.js
+++ b/security/manager/ssl/tests/unit/head_psm.js
@@ -204,34 +204,36 @@ function checkCertErrorGeneric(certdb, c
 
 function checkEVStatus(certDB, cert, usage, isEVExpected) {
   let hasEVPolicy = {};
   checkCertErrorGeneric(certDB, cert, PRErrorCodeSuccess, usage, hasEVPolicy);
   Assert.equal(hasEVPolicy.value, isEVExpected,
                "Actual and expected EV status should match");
 }
 
-function _getLibraryFunctionWithNoArguments(functionName, libraryName) {
+function _getLibraryFunctionWithNoArguments(functionName, libraryName,
+                                            returnType) {
   // Open the NSS library. copied from services/crypto/modules/WeaveCrypto.js
   let path = ctypes.libraryName(libraryName);
 
   // XXX really want to be able to pass specific dlopen flags here.
   let nsslib;
   try {
     nsslib = ctypes.open(path);
   } catch (e) {
     // In case opening the library without a full path fails,
     // try again with a full path.
     let file = Services.dirsvc.get("GreBinD", Ci.nsILocalFile);
     file.append(path);
     nsslib = ctypes.open(file.path);
   }
 
   let SECStatus = ctypes.int;
-  let func = nsslib.declare(functionName, ctypes.default_abi, SECStatus);
+  let func = nsslib.declare(functionName, ctypes.default_abi,
+                            returnType || SECStatus);
   return func;
 }
 
 function clearOCSPCache() {
   let certdb = Cc["@mozilla.org/security/x509certdb;1"]
                  .getService(Ci.nsIX509CertDB);
   certdb.clearOCSPCache();
 }
@@ -246,16 +248,49 @@ function clearSessionCache() {
     SSL_ClearSessionCache =
       _getLibraryFunctionWithNoArguments("SSL_ClearSessionCache", "nss3");
   }
   if (!SSL_ClearSessionCache || SSL_ClearSessionCache() != 0) {
     throw new Error("Failed to clear SSL session cache");
   }
 }
 
+function getSSLStatistics() {
+  let SSL3Statistics = new ctypes.StructType("SSL3Statistics",
+                           [ { "sch_sid_cache_hits": ctypes.long },
+                             { "sch_sid_cache_misses": ctypes.long },
+                             { "sch_sid_cache_not_ok": ctypes.long },
+                             { "hsh_sid_cache_hits": ctypes.long },
+                             { "hsh_sid_cache_misses": ctypes.long },
+                             { "hsh_sid_cache_not_ok": ctypes.long },
+                             { "hch_sid_cache_hits": ctypes.long },
+                             { "hch_sid_cache_misses": ctypes.long },
+                             { "hch_sid_cache_not_ok": ctypes.long },
+                             { "sch_sid_stateless_resumes": ctypes.long },
+                             { "hsh_sid_stateless_resumes": ctypes.long },
+                             { "hch_sid_stateless_resumes": ctypes.long },
+                             { "hch_sid_ticket_parse_failures": ctypes.long }]);
+  let SSL3StatisticsPtr = new ctypes.PointerType(SSL3Statistics);
+  let SSL_GetStatistics = null;
+  try {
+    SSL_GetStatistics = _getLibraryFunctionWithNoArguments("SSL_GetStatistics",
+                                                           "ssl3",
+                                                           SSL3StatisticsPtr);
+  } catch(e) {
+    // On Windows, this is actually in the nss3 library.
+    SSL_GetStatistics = _getLibraryFunctionWithNoArguments("SSL_GetStatistics",
+                                                           "nss3",
+                                                           SSL3StatisticsPtr);
+  }
+  if (!SSL_GetStatistics) {
+    throw new Error("Failed to get SSL statistics");
+  }
+  return SSL_GetStatistics();
+}
+
 // Set up a TLS testing environment that has a TLS server running and
 // ready to accept connections. This async function starts the server and
 // waits for the server to indicate that it is ready.
 //
 // Each test should have its own subdomain of example.com, for example
 // my-first-connection-test.example.com. The server can use the server
 // name (passed through the SNI TLS extension) to determine what behavior
 // the server side of the text should exhibit. See TLSServer.h for more
--- a/security/manager/ssl/tests/unit/test_session_resumption.js
+++ b/security/manager/ssl/tests/unit/test_session_resumption.js
@@ -104,14 +104,56 @@ function add_resume_ev_test() {
   // 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();
 }
--- a/security/nss.symbols
+++ b/security/nss.symbols
@@ -664,16 +664,17 @@ SSL_ExportKeyingMaterial
 SSL_ForceHandshake
 SSL_GetChannelInfo
 SSL_GetCipherSuiteInfo
 SSL_GetClientAuthDataHook
 SSL_GetImplementedCiphers
 SSL_GetNextProto
 SSL_GetNumImplementedCiphers
 SSL_GetSRTPCipher
+SSL_GetStatistics
 SSL_HandshakeCallback
 SSL_HandshakeNegotiatedExtension
 SSL_ImplementedCiphers @DATA@
 SSL_ImportFD
 SSL_NamedGroupConfig
 SSL_NumImplementedCiphers @DATA@
 SSL_OptionSet
 SSL_OptionSetDefault