Bug 1501587 - land NSS 3b79af0fa294 UPGRADE_NSS_RELEASE, r=me
authorJ.C. Jones <jjones@mozilla.com>
Mon, 29 Oct 2018 14:29:54 -0700
changeset 499862 3725f21a07b53568bfe224e7d5d17276ead6ca98
parent 499861 8efa629aaa6610736d7aaa491cdaf6565fd2878e
child 499863 63520174c8a5ec18cb71cf9c0ebaee790dfd5323
push id10290
push userffxbld-merge
push dateMon, 03 Dec 2018 16:23:23 +0000
treeherdermozilla-beta@700bed2445e6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersme
bugs1501587
milestone65.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1501587 - land NSS 3b79af0fa294 UPGRADE_NSS_RELEASE, r=me
security/nss/TAG-INFO
security/nss/automation/abi-check/previous-nss-release
security/nss/coreconf/coreconf.dep
security/nss/cpputil/databuffer.h
security/nss/gtests/ssl_gtest/ssl_loopback_unittest.cc
security/nss/gtests/ssl_gtest/ssl_resumption_unittest.cc
security/nss/gtests/ssl_gtest/test_io.cc
security/nss/gtests/ssl_gtest/tls_agent.cc
security/nss/gtests/ssl_gtest/tls_connect.cc
security/nss/lib/freebl/freebl.gyp
security/nss/lib/freebl/freebl_base.gypi
security/nss/lib/nss/nss.h
security/nss/lib/softoken/softkver.h
security/nss/lib/ssl/ssl3con.c
security/nss/lib/ssl/sslexp.h
security/nss/lib/ssl/sslimpl.h
security/nss/lib/ssl/sslnonce.c
security/nss/lib/ssl/sslsecur.c
security/nss/lib/ssl/sslsock.c
security/nss/lib/util/nssutil.h
security/nss/tests/interop/interop.sh
--- a/security/nss/TAG-INFO
+++ b/security/nss/TAG-INFO
@@ -1,1 +1,1 @@
-704d253fa016
+3b79af0fa294
--- a/security/nss/automation/abi-check/previous-nss-release
+++ b/security/nss/automation/abi-check/previous-nss-release
@@ -1,1 +1,1 @@
-NSS_3_39_BRANCH
+NSS_3_40_BRANCH
--- a/security/nss/coreconf/coreconf.dep
+++ b/security/nss/coreconf/coreconf.dep
@@ -5,9 +5,8 @@
 
 /*
  * A dummy header file that is a dependency for all the object files.
  * Used to force a full recompilation of NSS in Mozilla's Tinderbox
  * depend builds.  See comments in rules.mk.
  */
 
 #error "Do not include this header file."
-
--- a/security/nss/cpputil/databuffer.h
+++ b/security/nss/cpputil/databuffer.h
@@ -29,17 +29,17 @@ class DataBuffer {
     if (&other != this) {
       Assign(other);
     }
     return *this;
   }
 
   void Allocate(size_t l) {
     delete[] data_;
-    data_ = new uint8_t[l ? l : 1];  // Don't depend on new [0].
+    data_ = new uint8_t[l ? l : 1]();  // Don't depend on new [0].
     len_ = l;
   }
 
   void Truncate(size_t l) { len_ = (std::min)(len_, l); }
 
   void Assign(const DataBuffer& other) { Assign(other.data(), other.len()); }
 
   void Assign(const uint8_t* d, size_t l);
--- a/security/nss/gtests/ssl_gtest/ssl_loopback_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_loopback_unittest.cc
@@ -624,16 +624,95 @@ TEST_P(TlsConnectGeneric, CheckRandoms) 
   EXPECT_NE(0, memcmp(srandom2, z, random_len));
 
   EXPECT_NE(0, memcmp(crandom1, crandom2, random_len));
   EXPECT_NE(0, memcmp(crandom1, srandom2, random_len));
   EXPECT_NE(0, memcmp(srandom1, crandom2, random_len));
   EXPECT_NE(0, memcmp(srandom1, srandom2, random_len));
 }
 
+void FailOnCloseNotify(const PRFileDesc* fd, void* arg, const SSLAlert* alert) {
+  ADD_FAILURE() << "received alert " << alert->description;
+}
+
+void CheckCloseNotify(const PRFileDesc* fd, void* arg, const SSLAlert* alert) {
+  *reinterpret_cast<bool*>(arg) = true;
+  EXPECT_EQ(close_notify, alert->description);
+  EXPECT_EQ(alert_warning, alert->level);
+}
+
+TEST_P(TlsConnectGeneric, ShutdownOneSide) {
+  Connect();
+
+  // Setup to check alerts.
+  EXPECT_EQ(SECSuccess, SSL_AlertSentCallback(server_->ssl_fd(),
+                                              FailOnCloseNotify, nullptr));
+  EXPECT_EQ(SECSuccess, SSL_AlertReceivedCallback(client_->ssl_fd(),
+                                                  FailOnCloseNotify, nullptr));
+
+  bool client_sent = false;
+  EXPECT_EQ(SECSuccess, SSL_AlertSentCallback(client_->ssl_fd(),
+                                              CheckCloseNotify, &client_sent));
+  bool server_received = false;
+  EXPECT_EQ(SECSuccess,
+            SSL_AlertReceivedCallback(server_->ssl_fd(), CheckCloseNotify,
+                                      &server_received));
+  EXPECT_EQ(PR_SUCCESS, PR_Shutdown(client_->ssl_fd(), PR_SHUTDOWN_SEND));
+
+  // Make sure that the server reads out the close_notify.
+  uint8_t buf[10];
+  EXPECT_EQ(0, PR_Read(server_->ssl_fd(), buf, sizeof(buf)));
+
+  // Reading and writing should still work in the one open direction.
+  EXPECT_TRUE(client_sent);
+  EXPECT_TRUE(server_received);
+  server_->SendData(10, 10);
+  client_->ReadBytes(10);
+
+  // Now close the other side and do the same checks.
+  bool server_sent = false;
+  EXPECT_EQ(SECSuccess, SSL_AlertSentCallback(server_->ssl_fd(),
+                                              CheckCloseNotify, &server_sent));
+  bool client_received = false;
+  EXPECT_EQ(SECSuccess,
+            SSL_AlertReceivedCallback(client_->ssl_fd(), CheckCloseNotify,
+                                      &client_received));
+  EXPECT_EQ(PR_SUCCESS, PR_Shutdown(server_->ssl_fd(), PR_SHUTDOWN_SEND));
+
+  EXPECT_EQ(0, PR_Read(client_->ssl_fd(), buf, sizeof(buf)));
+  EXPECT_TRUE(server_sent);
+  EXPECT_TRUE(client_received);
+}
+
+TEST_P(TlsConnectGeneric, ShutdownOneSideThenCloseTcp) {
+  Connect();
+
+  bool client_sent = false;
+  EXPECT_EQ(SECSuccess, SSL_AlertSentCallback(client_->ssl_fd(),
+                                              CheckCloseNotify, &client_sent));
+  bool server_received = false;
+  EXPECT_EQ(SECSuccess,
+            SSL_AlertReceivedCallback(server_->ssl_fd(), CheckCloseNotify,
+                                      &server_received));
+  EXPECT_EQ(PR_SUCCESS, PR_Shutdown(client_->ssl_fd(), PR_SHUTDOWN_SEND));
+
+  // Make sure that the server reads out the close_notify.
+  uint8_t buf[10];
+  EXPECT_EQ(0, PR_Read(server_->ssl_fd(), buf, sizeof(buf)));
+
+  // Now simulate the underlying connection closing.
+  client_->adapter()->Reset();
+
+  // Now close the other side and see that things don't explode.
+  EXPECT_EQ(PR_SUCCESS, PR_Shutdown(server_->ssl_fd(), PR_SHUTDOWN_SEND));
+
+  EXPECT_GT(0, PR_Read(client_->ssl_fd(), buf, sizeof(buf)));
+  EXPECT_EQ(PR_NOT_CONNECTED_ERROR, PR_GetError());
+}
+
 INSTANTIATE_TEST_CASE_P(
     GenericStream, TlsConnectGeneric,
     ::testing::Combine(TlsConnectTestBase::kTlsVariantsStream,
                        TlsConnectTestBase::kTlsVAll));
 INSTANTIATE_TEST_CASE_P(
     GenericDatagram, TlsConnectGeneric,
     ::testing::Combine(TlsConnectTestBase::kTlsVariantsDatagram,
                        TlsConnectTestBase::kTlsV11Plus));
--- a/security/nss/gtests/ssl_gtest/ssl_resumption_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_resumption_unittest.cc
@@ -1119,16 +1119,46 @@ void CheckGetInfoResult(uint32_t alpnSiz
   ASSERT_EQ(cert->derCert.len, token->peerCert->derCert.len);
   EXPECT_EQ(0, memcmp(cert->derCert.data, token->peerCert->derCert.data,
                       cert->derCert.len));
 
   ASSERT_EQ(alpnSize, token->alpnSelectionLen);
   EXPECT_EQ(0, memcmp("a", token->alpnSelection, token->alpnSelectionLen));
 
   ASSERT_EQ(earlyDataSize, token->maxEarlyDataSize);
+
+  ASSERT_LT(ssl_TimeUsec(), token->expirationTime);
+}
+
+// The client should generate a new, randomized session_id
+// when resuming using an external token.
+TEST_P(TlsConnectGenericResumptionToken, CheckSessionId) {
+  ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH);
+  auto original_sid = MakeTlsFilter<CaptureSessionId>(client_);
+  Connect();
+  SendReceive();
+
+  Reset();
+  ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH);
+  ExpectResumption(RESUME_TICKET);
+
+  StartConnect();
+  ASSERT_TRUE(client_->MaybeSetResumptionToken());
+  auto resumed_sid = MakeTlsFilter<CaptureSessionId>(client_);
+
+  Handshake();
+  CheckConnected();
+  SendReceive();
+
+  if (version_ < SSL_LIBRARY_VERSION_TLS_1_3) {
+    EXPECT_NE(resumed_sid->sid(), original_sid->sid());
+    EXPECT_EQ(32U, resumed_sid->sid().len());
+  } else {
+    EXPECT_EQ(0U, resumed_sid->sid().len());
+  }
 }
 
 TEST_P(TlsConnectGenericResumptionToken, ConnectResumeGetInfo) {
   ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH);
   Connect();
   SendReceive();
 
   Reset();
--- a/security/nss/gtests/ssl_gtest/test_io.cc
+++ b/security/nss/gtests/ssl_gtest/test_io.cc
@@ -26,27 +26,47 @@ namespace nss_test {
   } while (false)
 
 ScopedPRFileDesc DummyPrSocket::CreateFD() {
   static PRDescIdentity test_fd_identity =
       PR_GetUniqueIdentity("testtransportadapter");
   return DummyIOLayerMethods::CreateFD(test_fd_identity, this);
 }
 
+void DummyPrSocket::Reset() {
+  auto p = peer_.lock();
+  peer_.reset();
+  if (p) {
+    p->peer_.reset();
+    p->Reset();
+  }
+  while (!input_.empty()) {
+    input_.pop();
+  }
+  filter_ = nullptr;
+  write_error_ = 0;
+}
+
 void DummyPrSocket::PacketReceived(const DataBuffer &packet) {
   input_.push(Packet(packet));
 }
 
 int32_t DummyPrSocket::Read(PRFileDesc *f, void *data, int32_t len) {
   PR_ASSERT(variant_ == ssl_variant_stream);
   if (variant_ != ssl_variant_stream) {
     PR_SetError(PR_INVALID_METHOD_ERROR, 0);
     return -1;
   }
 
+  auto dst = peer_.lock();
+  if (!dst) {
+    PR_SetError(PR_NOT_CONNECTED_ERROR, 0);
+    return -1;
+  }
+
   if (input_.empty()) {
     LOGV("Read --> wouldblock " << len);
     PR_SetError(PR_WOULD_BLOCK_ERROR, 0);
     return -1;
   }
 
   auto &front = input_.front();
   size_t to_read =
@@ -69,16 +89,22 @@ int32_t DummyPrSocket::Recv(PRFileDesc *
     PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
     return -1;
   }
 
   if (variant() != ssl_variant_datagram) {
     return Read(f, buf, buflen);
   }
 
+  auto dst = peer_.lock();
+  if (!dst) {
+    PR_SetError(PR_NOT_CONNECTED_ERROR, 0);
+    return -1;
+  }
+
   if (input_.empty()) {
     PR_SetError(PR_WOULD_BLOCK_ERROR, 0);
     return -1;
   }
 
   auto &front = input_.front();
   if (static_cast<size_t>(buflen) < front.len()) {
     PR_ASSERT(false);
@@ -96,17 +122,17 @@ int32_t DummyPrSocket::Recv(PRFileDesc *
 int32_t DummyPrSocket::Write(PRFileDesc *f, const void *buf, int32_t length) {
   if (write_error_) {
     PR_SetError(write_error_, 0);
     return -1;
   }
 
   auto dst = peer_.lock();
   if (!dst) {
-    PR_SetError(PR_IO_ERROR, 0);
+    PR_SetError(PR_NOT_CONNECTED_ERROR, 0);
     return -1;
   }
 
   DataBuffer packet(static_cast<const uint8_t *>(buf),
                     static_cast<size_t>(length));
   DataBuffer filtered;
   PacketFilter::Action action = PacketFilter::KEEP;
   if (filter_) {
--- a/security/nss/gtests/ssl_gtest/tls_agent.cc
+++ b/security/nss/gtests/ssl_gtest/tls_agent.cc
@@ -224,16 +224,17 @@ bool TlsAgent::EnsureTlsSetup(PRFileDesc
   EXPECT_EQ(SECSuccess, rv);
   if (rv != SECSuccess) return false;
 
   return true;
 }
 
 bool TlsAgent::MaybeSetResumptionToken() {
   if (!resumption_token_.empty()) {
+    LOG("setting external resumption token");
     SECStatus rv = SSL_SetResumptionToken(ssl_fd(), resumption_token_.data(),
                                           resumption_token_.size());
 
     // rv is SECFailure with error set to SSL_ERROR_BAD_RESUMPTION_TOKEN_ERROR
     // if the resumption token was bad (expired/malformed/etc.).
     if (expect_resumption_) {
       // Only in case we expect resumption this has to be successful. We might
       // not expect resumption due to some reason but the token is totally fine.
@@ -933,18 +934,18 @@ void TlsAgent::SendDirect(const DataBuff
 void TlsAgent::SendRecordDirect(const TlsRecord& record) {
   DataBuffer buf;
 
   auto rv = record.header.Write(&buf, 0, record.buffer);
   EXPECT_EQ(record.header.header_length() + record.buffer.len(), rv);
   SendDirect(buf);
 }
 
-static bool ErrorIsNonFatal(PRErrorCode code) {
-  return code == PR_WOULD_BLOCK_ERROR || code == SSL_ERROR_RX_SHORT_DTLS_READ;
+static bool ErrorIsFatal(PRErrorCode code) {
+  return code != PR_WOULD_BLOCK_ERROR && code != SSL_ERROR_RX_SHORT_DTLS_READ;
 }
 
 void TlsAgent::SendData(size_t bytes, size_t blocksize) {
   uint8_t block[16385];  // One larger than the maximum record size.
 
   ASSERT_LE(blocksize, sizeof(block));
 
   while (bytes) {
@@ -993,38 +994,49 @@ bool TlsAgent::SendEncryptedRecord(const
   EXPECT_EQ(header.header_length() + ciphertext.len(), rv);
   SendDirect(record);
   return true;
 }
 
 void TlsAgent::ReadBytes(size_t amount) {
   uint8_t block[16384];
 
-  int32_t rv = PR_Read(ssl_fd(), block, (std::min)(amount, sizeof(block)));
-  LOGV("ReadBytes " << rv);
-  int32_t err;
+  size_t remaining = amount;
+  while (remaining > 0) {
+    int32_t rv = PR_Read(ssl_fd(), block, (std::min)(amount, sizeof(block)));
+    LOGV("ReadBytes " << rv);
 
-  if (rv >= 0) {
-    size_t count = static_cast<size_t>(rv);
-    for (size_t i = 0; i < count; ++i) {
-      ASSERT_EQ(recv_ctr_ & 0xff, block[i]);
-      recv_ctr_++;
-    }
-  } else {
-    err = PR_GetError();
-    LOG("Read error " << PORT_ErrorToName(err) << ": "
-                      << PORT_ErrorToString(err));
-    if (err != PR_WOULD_BLOCK_ERROR && expect_readwrite_error_) {
-      error_code_ = err;
-      expect_readwrite_error_ = false;
+    if (rv > 0) {
+      size_t count = static_cast<size_t>(rv);
+      for (size_t i = 0; i < count; ++i) {
+        ASSERT_EQ(recv_ctr_ & 0xff, block[i]);
+        recv_ctr_++;
+      }
+      remaining -= rv;
+    } else {
+      PRErrorCode err = 0;
+      if (rv < 0) {
+        err = PR_GetError();
+        LOG("Read error " << PORT_ErrorToName(err) << ": "
+                          << PORT_ErrorToString(err));
+        if (err != PR_WOULD_BLOCK_ERROR && expect_readwrite_error_) {
+          error_code_ = err;
+          expect_readwrite_error_ = false;
+        }
+      }
+      if (err != 0 && ErrorIsFatal(err)) {
+        // If we hit a fatal error, we're done.
+        remaining = 0;
+      }
+      break;
     }
   }
 
   // If closed, then don't bother waiting around.
-  if (rv > 0 || (rv < 0 && ErrorIsNonFatal(err))) {
+  if (remaining) {
     LOGV("Re-arming");
     Poller::Instance()->Wait(READABLE_EVENT, adapter_, this,
                              &TlsAgent::ReadableCallback);
   }
 }
 
 void TlsAgent::ResetSentBytes() { send_ctr_ = 0; }
 
--- a/security/nss/gtests/ssl_gtest/tls_connect.cc
+++ b/security/nss/gtests/ssl_gtest/tls_connect.cc
@@ -554,23 +554,25 @@ void TlsConnectTestBase::CheckResumption
   SSL3Statistics* stats = SSL_GetStatistics();
   EXPECT_EQ(resume_count, stats->hch_sid_cache_hits);
   EXPECT_EQ(resume_count, stats->hsh_sid_cache_hits);
 
   EXPECT_EQ(stateless_count, stats->hch_sid_stateless_resumes);
   EXPECT_EQ(stateless_count, stats->hsh_sid_stateless_resumes);
 
   if (expected != RESUME_NONE) {
-    if (client_->version() < SSL_LIBRARY_VERSION_TLS_1_3) {
+    if (client_->version() < SSL_LIBRARY_VERSION_TLS_1_3 &&
+        client_->GetResumptionToken().size() == 0) {
       // Check that the last two session ids match.
       ASSERT_EQ(1U + expected_resumptions_, session_ids_.size());
       EXPECT_EQ(session_ids_[session_ids_.size() - 1],
                 session_ids_[session_ids_.size() - 2]);
     } else {
-      // TLS 1.3 only uses tickets.
+      // We've either chosen TLS 1.3 or are using an external resumption token,
+      // both of which only use tickets.
       EXPECT_TRUE(expected & RESUME_TICKET);
     }
   }
 }
 
 static SECStatus NextProtoCallbackServer(void* arg, PRFileDesc* fd,
                                          const unsigned char* protos,
                                          unsigned int protos_len,
--- a/security/nss/lib/freebl/freebl.gyp
+++ b/security/nss/lib/freebl/freebl.gyp
@@ -2,30 +2,61 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 {
   'includes': [
     '../../coreconf/config.gypi'
   ],
   'targets': [
     {
+      'target_name': 'intel-gcm-s_lib',
+      'type': 'static_library',
+      'sources': [
+        'intel-aes.s',
+        'intel-gcm.s',
+      ],
+      'dependencies': [
+        '<(DEPTH)/exports.gyp:nss_exports'
+      ],
+      'conditions': [
+        [ 'cc_is_clang==1', {
+          'cflags': [
+            '-no-integrated-as',
+          ],
+          'cflags_mozilla': [
+            '-no-integrated-as',
+          ],
+          'asflags_mozilla': [
+            '-no-integrated-as',
+          ],
+        }],
+      ],
+    },
+    {
       'target_name': 'intel-gcm-wrap_c_lib',
       'type': 'static_library',
       'sources': [
         'intel-gcm-wrap.c',
       ],
       'dependencies': [
         '<(DEPTH)/exports.gyp:nss_exports'
       ],
+      'conditions': [
+        [ '(OS=="linux" or OS=="android") and target_arch=="x64"', {
+          'dependencies': [
+            'intel-gcm-s_lib',
+          ],
+        }],
+      ],
       'cflags': [
-        '-mssse3'
+        '-mssse3',
       ],
       'cflags_mozilla': [
         '-mssse3'
-      ]
+      ],
     },
     {
       # TODO: make this so that all hardware accelerated code is in here.
       'target_name': 'hw-acc-crypto',
       'type': 'static_library',
       'sources': [
         'verified/Hacl_Chacha20_Vec128.c',
       ],
--- a/security/nss/lib/freebl/freebl_base.gypi
+++ b/security/nss/lib/freebl/freebl_base.gypi
@@ -62,24 +62,22 @@
     'tlsprfalg.c',
   ],
   'conditions': [
     [ 'OS=="linux" or OS=="android"', {
       'conditions': [
         [ 'target_arch=="x64"', {
           'sources': [
             'arcfour-amd64-gas.s',
-            'intel-aes.s',
-            'intel-gcm.s',
             'mpi/mpi_amd64.c',
             'mpi/mpi_amd64_gas.s',
             'mpi/mp_comba.c',
           ],
           'conditions': [
-            [ 'cc_is_clang==1', {
+            [ 'cc_is_clang==1 and fuzz!=1', {
               'cflags': [
                 '-no-integrated-as',
               ],
               'cflags_mozilla': [
                 '-no-integrated-as',
               ],
               'asflags_mozilla': [
                 '-no-integrated-as',
--- a/security/nss/lib/nss/nss.h
+++ b/security/nss/lib/nss/nss.h
@@ -17,22 +17,22 @@
 
 /*
  * NSS's major version, minor version, patch level, build number, and whether
  * this is a beta release.
  *
  * The format of the version string should be
  *     "<major version>.<minor version>[.<patch level>[.<build number>]][ <ECC>][ <Beta>]"
  */
-#define NSS_VERSION "3.40" _NSS_CUSTOMIZED
+#define NSS_VERSION "3.41" _NSS_CUSTOMIZED " Beta"
 #define NSS_VMAJOR 3
-#define NSS_VMINOR 40
+#define NSS_VMINOR 41
 #define NSS_VPATCH 0
 #define NSS_VBUILD 0
-#define NSS_BETA PR_FALSE
+#define NSS_BETA PR_TRUE
 
 #ifndef RC_INVOKED
 
 #include "seccomon.h"
 
 typedef struct NSSInitParametersStr NSSInitParameters;
 
 /*
--- a/security/nss/lib/softoken/softkver.h
+++ b/security/nss/lib/softoken/softkver.h
@@ -12,16 +12,16 @@
 
 /*
  * Softoken's major version, minor version, patch level, build number,
  * and whether this is a beta release.
  *
  * The format of the version string should be
  *     "<major version>.<minor version>[.<patch level>[.<build number>]][ <ECC>][ <Beta>]"
  */
-#define SOFTOKEN_VERSION "3.40" SOFTOKEN_ECC_STRING
+#define SOFTOKEN_VERSION "3.41" SOFTOKEN_ECC_STRING " Beta"
 #define SOFTOKEN_VMAJOR 3
-#define SOFTOKEN_VMINOR 40
+#define SOFTOKEN_VMINOR 41
 #define SOFTOKEN_VPATCH 0
 #define SOFTOKEN_VBUILD 0
-#define SOFTOKEN_BETA PR_FALSE
+#define SOFTOKEN_BETA PR_TRUE
 
 #endif /* _SOFTKVER_H_ */
--- a/security/nss/lib/ssl/ssl3con.c
+++ b/security/nss/lib/ssl/ssl3con.c
@@ -4766,17 +4766,17 @@ ssl3_SendClientHello(sslSocket *ss, sslC
         }
     }
 
     /* Check if we have a ss->sec.ci.sid.
      * Check that it's not expired.
      * If we have an sid and it comes from an external cache, we use it. */
     if (ss->sec.ci.sid && ss->sec.ci.sid->cached == in_external_cache) {
         PORT_Assert(!ss->sec.isServer);
-        sid = ss->sec.ci.sid;
+        sid = ssl_ReferenceSID(ss->sec.ci.sid);
         SSL_TRC(3, ("%d: SSL3[%d]: using external resumption token in ClientHello",
                     SSL_GETPID(), ss->fd));
     } else if (!ss->opt.noCache) {
         /* We ignore ss->sec.ci.sid here, and use ssl_Lookup because Lookup
          * handles expired entries and other details.
          * XXX If we've been called from ssl_BeginClientHandshake, then
          * this lookup is duplicative and wasteful.
          */
@@ -4914,19 +4914,17 @@ ssl3_SendClientHello(sslSocket *ss, sslC
     isTLS = (version > SSL_LIBRARY_VERSION_3_0);
     ssl_GetSpecWriteLock(ss);
     if (ss->ssl3.cwSpec->macDef->mac == ssl_mac_null) {
         /* SSL records are not being MACed. */
         ss->ssl3.cwSpec->version = version;
     }
     ssl_ReleaseSpecWriteLock(ss);
 
-    if (ss->sec.ci.sid != NULL) {
-        ssl_FreeSID(ss->sec.ci.sid); /* decrement ref count, free if zero */
-    }
+    ssl_FreeSID(ss->sec.ci.sid); /* release the old sid */
     ss->sec.ci.sid = sid;
 
     /* HACK for SCSV in SSL 3.0.  On initial handshake, prepend SCSV,
      * only if TLS is disabled.
      */
     if (!ss->firstHsDone && !isTLS) {
         /* Must set this before calling Hello Extension Senders,
          * to suppress sending of empty RI extension.
--- a/security/nss/lib/ssl/sslexp.h
+++ b/security/nss/lib/ssl/sslexp.h
@@ -362,16 +362,17 @@ typedef SSLHelloRetryRequestAction(PR_CA
  * calling SSL_DestroyResumptionTokenInfo.
  */
 typedef struct SSLResumptionTokenInfoStr {
     PRUint16 length;
     CERTCertificate *peerCert;
     PRUint8 *alpnSelection;
     PRUint32 alpnSelectionLen;
     PRUint32 maxEarlyDataSize;
+    PRTime expirationTime; /* added in NSS 3.41 */
 } SSLResumptionTokenInfo;
 
 /*
  * Allows applications to retrieve information about a resumption token.
  * This does not require a TLS session.
  *
  * - The |tokenData| argument is a pointer to the resumption token as byte array
  *   of length |tokenLen|.
--- a/security/nss/lib/ssl/sslimpl.h
+++ b/security/nss/lib/ssl/sslimpl.h
@@ -1179,16 +1179,17 @@ extern int ssl_Do1stHandshake(sslSocket 
 extern SECStatus ssl3_InitPendingCipherSpecs(sslSocket *ss, PK11SymKey *secret,
                                              PRBool derive);
 extern void ssl_DestroyKeyMaterial(ssl3KeyMaterial *keyMaterial);
 extern sslSessionID *ssl3_NewSessionID(sslSocket *ss, PRBool is_server);
 extern sslSessionID *ssl_LookupSID(const PRIPv6Addr *addr, PRUint16 port,
                                    const char *peerID, const char *urlSvrName);
 extern void ssl_FreeSID(sslSessionID *sid);
 extern void ssl_DestroySID(sslSessionID *sid, PRBool freeIt);
+extern sslSessionID *ssl_ReferenceSID(sslSessionID *sid);
 
 extern int ssl3_SendApplicationData(sslSocket *ss, const PRUint8 *in,
                                     int len, int flags);
 
 extern PRBool ssl_FdIsBlocking(PRFileDesc *fd);
 
 extern PRBool ssl_SocketIsBlocking(sslSocket *ss);
 
@@ -1721,17 +1722,17 @@ PRBool ssl_AlpnTagAllowed(const sslSocke
 #define SSL_TRACE(msg)
 #endif
 
 void ssl_Trace(const char *format, ...);
 
 void ssl_CacheExternalToken(sslSocket *ss);
 SECStatus ssl_DecodeResumptionToken(sslSessionID *sid, const PRUint8 *encodedTicket,
                                     PRUint32 encodedTicketLen);
-PRBool ssl_IsResumptionTokenValid(sslSocket *ss);
+PRBool ssl_IsResumptionTokenUsable(sslSocket *ss, sslSessionID *sid);
 
 /* Remove when stable. */
 
 SECStatus SSLExp_SetResumptionTokenCallback(PRFileDesc *fd,
                                             SSLResumptionTokenCallback cb,
                                             void *ctx);
 SECStatus SSLExp_SetResumptionToken(PRFileDesc *fd, const PRUint8 *token,
                                     unsigned int len);
--- a/security/nss/lib/ssl/sslnonce.c
+++ b/security/nss/lib/ssl/sslnonce.c
@@ -229,19 +229,30 @@ ssl_FreeLockedSID(sslSessionID *sid)
  * Decrement reference count, and
  *    free sid if ref count is zero, and sid is not in the cache.
  * Does NOT remove from the cache first.
  * These locks are necessary because the sid _might_ be in the cache list.
  */
 void
 ssl_FreeSID(sslSessionID *sid)
 {
+    if (sid) {
+        LOCK_CACHE;
+        ssl_FreeLockedSID(sid);
+        UNLOCK_CACHE;
+    }
+}
+
+sslSessionID *
+ssl_ReferenceSID(sslSessionID *sid)
+{
     LOCK_CACHE;
-    ssl_FreeLockedSID(sid);
+    sid->references++;
     UNLOCK_CACHE;
+    return sid;
 }
 
 /************************************************************************/
 
 /*
 **  Lookup sid entry in cache by Address, port, and peerID string.
 **  If found, Increment reference count, and return pointer to caller.
 **  If it has timed out or ref count is zero, remove from list and free it.
@@ -699,20 +710,19 @@ ssl_DecodeResumptionToken(sslSessionID *
         PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
         return SECFailure;
     }
 
     return SECSuccess;
 }
 
 PRBool
-ssl_IsResumptionTokenValid(sslSocket *ss)
+ssl_IsResumptionTokenUsable(sslSocket *ss, sslSessionID *sid)
 {
     PORT_Assert(ss);
-    sslSessionID *sid = ss->sec.ci.sid;
     PORT_Assert(sid);
 
     // Check that the ticket didn't expire.
     PRTime endTime = 0;
     NewSessionTicket *ticket = &sid->u.ssl3.locked.sessionTicket;
     if (ticket->ticket_lifetime_hint != 0) {
         endTime = ticket->received_timestamp +
                   (PRTime)(ticket->ticket_lifetime_hint * PR_USEC_PER_SEC);
--- a/security/nss/lib/ssl/sslsecur.c
+++ b/security/nss/lib/ssl/sslsecur.c
@@ -680,47 +680,29 @@ ssl_SecureConnect(sslSocket *ss, const P
     }
 
     SSL_TRC(5, ("%d: SSL[%d]: secure connect completed, rv == %d",
                 SSL_GETPID(), ss->fd, rv));
     return rv;
 }
 
 /*
- * The TLS 1.2 RFC 5246, Section 7.2.1 says:
- *
- *     Unless some other fatal alert has been transmitted, each party is
- *     required to send a close_notify alert before closing the write side
- *     of the connection.  The other party MUST respond with a close_notify
- *     alert of its own and close down the connection immediately,
- *     discarding any pending writes.  It is not required for the initiator
- *     of the close to wait for the responding close_notify alert before
- *     closing the read side of the connection.
- *
- * The second sentence requires that we send a close_notify alert when we
- * have received a close_notify alert.  In practice, all SSL implementations
- * close the socket immediately after sending a close_notify alert (which is
- * allowed by the third sentence), so responding with a close_notify alert
- * would result in a write failure with the ECONNRESET error.  This is why
- * we don't respond with a close_notify alert.
- *
  * Also, in the unlikely event that the TCP pipe is full and the peer stops
  * reading, the SSL3_SendAlert call in ssl_SecureClose and ssl_SecureShutdown
  * may block indefinitely in blocking mode, and may fail (without retrying)
  * in non-blocking mode.
  */
 
 int
 ssl_SecureClose(sslSocket *ss)
 {
     int rv;
 
     if (!(ss->shutdownHow & ssl_SHUTDOWN_SEND) &&
-        ss->firstHsDone &&
-        !ss->recvdCloseNotify) {
+        ss->firstHsDone) {
 
         /* We don't want the final alert to be Nagle delayed. */
         if (!ss->delayDisabled) {
             ssl_EnableNagleDelay(ss, PR_FALSE);
             ss->delayDisabled = 1;
         }
 
         (void)SSL3_SendAlert(ss, alert_warning, close_notify);
@@ -739,18 +721,17 @@ ssl_SecureShutdown(sslSocket *ss, int ns
 
     if ((unsigned)nsprHow > PR_SHUTDOWN_BOTH) {
         PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
         return PR_FAILURE;
     }
 
     if ((sslHow & ssl_SHUTDOWN_SEND) != 0 &&
         !(ss->shutdownHow & ssl_SHUTDOWN_SEND) &&
-        ss->firstHsDone &&
-        !ss->recvdCloseNotify) {
+        ss->firstHsDone) {
 
         (void)SSL3_SendAlert(ss, alert_warning, close_notify);
     }
 
     rv = osfd->methods->shutdown(osfd, nsprHow);
 
     ss->shutdownHow |= sslHow;
 
--- a/security/nss/lib/ssl/sslsock.c
+++ b/security/nss/lib/ssl/sslsock.c
@@ -13,16 +13,17 @@
 #include "ssl.h"
 #include "sslexp.h"
 #include "sslimpl.h"
 #include "sslproto.h"
 #include "nspr.h"
 #include "private/pprio.h"
 #include "nss.h"
 #include "pk11pqg.h"
+#include "pk11pub.h"
 #include "tls13esni.h"
 
 static const sslSocketOps ssl_default_ops = { /* No SSL. */
                                               ssl_DefConnect,
                                               NULL,
                                               ssl_DefBind,
                                               ssl_DefListen,
                                               ssl_DefShutdown,
@@ -4097,75 +4098,79 @@ SSLExp_SetResumptionTokenCallback(PRFile
     return SECSuccess;
 }
 
 SECStatus
 SSLExp_SetResumptionToken(PRFileDesc *fd, const PRUint8 *token,
                           unsigned int len)
 {
     sslSocket *ss = ssl_FindSocket(fd);
+    sslSessionID *sid = NULL;
 
     if (!ss) {
         SSL_DBG(("%d: SSL[%d]: bad socket in SSL_SetResumptionToken",
                  SSL_GETPID(), fd));
         return SECFailure;
     }
 
     ssl_Get1stHandshakeLock(ss);
     ssl_GetSSL3HandshakeLock(ss);
 
     if (ss->firstHsDone || ss->ssl3.hs.ws != idle_handshake ||
         ss->sec.isServer || len == 0 || !token) {
         PORT_SetError(SEC_ERROR_INVALID_ARGS);
-        goto done;
+        goto loser;
     }
 
     // We override any previously set session.
     if (ss->sec.ci.sid) {
         ssl_FreeSID(ss->sec.ci.sid);
         ss->sec.ci.sid = NULL;
     }
 
     PRINT_BUF(50, (ss, "incoming resumption token", token, len));
 
-    ss->sec.ci.sid = ssl3_NewSessionID(ss, PR_FALSE);
-    if (!ss->sec.ci.sid) {
-        goto done;
+    sid = ssl3_NewSessionID(ss, PR_FALSE);
+    if (!sid) {
+        goto loser;
     }
 
     /* Populate NewSessionTicket values */
-    SECStatus rv = ssl_DecodeResumptionToken(ss->sec.ci.sid, token, len);
+    SECStatus rv = ssl_DecodeResumptionToken(sid, token, len);
     if (rv != SECSuccess) {
         // If decoding fails, we assume the token is bad.
         PORT_SetError(SSL_ERROR_BAD_RESUMPTION_TOKEN_ERROR);
-        ssl_FreeSID(ss->sec.ci.sid);
-        ss->sec.ci.sid = NULL;
-        goto done;
+        goto loser;
+    }
+
+    // Make sure that the token is currently usable.
+    if (!ssl_IsResumptionTokenUsable(ss, sid)) {
+        PORT_SetError(SSL_ERROR_BAD_RESUMPTION_TOKEN_ERROR);
+        goto loser;
     }
 
-    // Make sure that the token is valid.
-    if (!ssl_IsResumptionTokenValid(ss)) {
-        ssl_FreeSID(ss->sec.ci.sid);
-        ss->sec.ci.sid = NULL;
-        PORT_SetError(SSL_ERROR_BAD_RESUMPTION_TOKEN_ERROR);
-        goto done;
+    // Generate a new random session ID for this ticket.
+    rv = PK11_GenerateRandom(sid->u.ssl3.sessionID, SSL3_SESSIONID_BYTES);
+    if (rv != SECSuccess) {
+        goto loser; // Code set by PK11_GenerateRandom.
     }
-
+    sid->u.ssl3.sessionIDLength = SSL3_SESSIONID_BYTES;
     /* Use the sid->cached as marker that this is from an external cache and
      * we don't have to look up anything in the NSS internal cache. */
-    ss->sec.ci.sid->cached = in_external_cache;
-    // This has to be 2 to not free this in sendClientHello.
-    ss->sec.ci.sid->references = 2;
-    ss->sec.ci.sid->lastAccessTime = ssl_TimeSec();
+    sid->cached = in_external_cache;
+    sid->lastAccessTime = ssl_TimeSec();
+
+    ss->sec.ci.sid = sid;
 
     ssl_ReleaseSSL3HandshakeLock(ss);
     ssl_Release1stHandshakeLock(ss);
     return SECSuccess;
 
-done:
+loser:
+    ssl_FreeSID(sid);
     ssl_ReleaseSSL3HandshakeLock(ss);
     ssl_Release1stHandshakeLock(ss);
 
     return SECFailure;
 }
 
 SECStatus
 SSLExp_DestroyResumptionTokenInfo(SSLResumptionTokenInfo *token)
@@ -4212,15 +4217,16 @@ SSLExp_GetResumptionTokenInfo(const PRUi
                 token.alpnSelectionLen);
 
     if (sid.u.ssl3.locked.sessionTicket.flags & ticket_allow_early_data) {
         token.maxEarlyDataSize =
             sid.u.ssl3.locked.sessionTicket.max_early_data_size;
     } else {
         token.maxEarlyDataSize = 0;
     }
+    token.expirationTime = sid.expirationTime;
 
     token.length = PR_MIN(sizeof(SSLResumptionTokenInfo), len);
     PORT_Memcpy(tokenOut, &token, token.length);
 
     ssl_DestroySID(&sid, PR_FALSE);
     return SECSuccess;
 }
--- a/security/nss/lib/util/nssutil.h
+++ b/security/nss/lib/util/nssutil.h
@@ -14,22 +14,22 @@
 
 /*
  * NSS utilities's major version, minor version, patch level, build number,
  * and whether this is a beta release.
  *
  * The format of the version string should be
  *     "<major version>.<minor version>[.<patch level>[.<build number>]][ <Beta>]"
  */
-#define NSSUTIL_VERSION "3.40"
+#define NSSUTIL_VERSION "3.41 Beta"
 #define NSSUTIL_VMAJOR 3
-#define NSSUTIL_VMINOR 40
+#define NSSUTIL_VMINOR 41
 #define NSSUTIL_VPATCH 0
 #define NSSUTIL_VBUILD 0
-#define NSSUTIL_BETA PR_FALSE
+#define NSSUTIL_BETA PR_TRUE
 
 SEC_BEGIN_PROTOS
 
 /*
  * Returns a const string of the UTIL library version.
  */
 extern const char *NSSUTIL_GetVersion(void);
 
--- a/security/nss/tests/interop/interop.sh
+++ b/security/nss/tests/interop/interop.sh
@@ -20,17 +20,17 @@ interop_init()
     . ./init.sh
   fi
 
   mkdir -p "${HOSTDIR}/interop"
   cd "${HOSTDIR}/interop"
   INTEROP=${INTEROP:=tls_interop}
   if [ ! -d "$INTEROP" ]; then
     git clone -q https://github.com/mozilla/tls-interop "$INTEROP"
-    git -C "$INTEROP" checkout -q 8348561ae922bb21ecc1415dba15ca68bb5d18a2
+    git -C "$INTEROP" checkout -q c00685aa953c49f1e844e614746aadc783e81b19
   fi
   INTEROP=$(cd "$INTEROP";pwd -P)
 
   # We use the BoringSSL keyfiles
   BORING=${BORING:=boringssl}
   if [ ! -d "$BORING" ]; then
     git clone -q https://boringssl.googlesource.com/boringssl "$BORING"
     git -C "$BORING" checkout -q 7f4f41fa81c03e0f8ef1ab5b3d1d566b5968f107