security/manager/ssl/src/nsNSSIOLayer.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/security/manager/ssl/src/nsNSSIOLayer.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,2456 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
     1.5 + *
     1.6 + * This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include "nsNSSIOLayer.h"
    1.11 +
    1.12 +#include "pkix/pkixtypes.h"
    1.13 +#include "nsNSSComponent.h"
    1.14 +#include "mozilla/Casting.h"
    1.15 +#include "mozilla/DebugOnly.h"
    1.16 +#include "mozilla/Telemetry.h"
    1.17 +
    1.18 +#include "prlog.h"
    1.19 +#include "prnetdb.h"
    1.20 +#include "nsIPrefService.h"
    1.21 +#include "nsIClientAuthDialogs.h"
    1.22 +#include "nsClientAuthRemember.h"
    1.23 +#include "nsISSLErrorListener.h"
    1.24 +
    1.25 +#include "nsNetUtil.h"
    1.26 +#include "nsPrintfCString.h"
    1.27 +#include "SSLServerCertVerification.h"
    1.28 +#include "nsNSSCertHelper.h"
    1.29 +#include "nsNSSCleaner.h"
    1.30 +
    1.31 +#ifndef MOZ_NO_EV_CERTS
    1.32 +#include "nsIDocShell.h"
    1.33 +#include "nsIDocShellTreeItem.h"
    1.34 +#include "nsISecureBrowserUI.h"
    1.35 +#include "nsIInterfaceRequestorUtils.h"
    1.36 +#endif
    1.37 +
    1.38 +#include "nsCharSeparatedTokenizer.h"
    1.39 +#include "nsIConsoleService.h"
    1.40 +#include "PSMRunnable.h"
    1.41 +#include "ScopedNSSTypes.h"
    1.42 +#include "SharedSSLState.h"
    1.43 +#include "mozilla/Preferences.h"
    1.44 +#include "nsContentUtils.h"
    1.45 +
    1.46 +#include "ssl.h"
    1.47 +#include "sslproto.h"
    1.48 +#include "secerr.h"
    1.49 +#include "sslerr.h"
    1.50 +#include "secder.h"
    1.51 +#include "keyhi.h"
    1.52 +
    1.53 +#include <algorithm>
    1.54 +
    1.55 +using namespace mozilla;
    1.56 +using namespace mozilla::psm;
    1.57 +
    1.58 +//#define DEBUG_SSL_VERBOSE //Enable this define to get minimal
    1.59 +                            //reports when doing SSL read/write
    1.60 +
    1.61 +//#define DUMP_BUFFER  //Enable this define along with
    1.62 +                       //DEBUG_SSL_VERBOSE to dump SSL
    1.63 +                       //read/write buffer to a log.
    1.64 +                       //Uses PR_LOG except on Mac where
    1.65 +                       //we always write out to our own
    1.66 +                       //file.
    1.67 +
    1.68 +namespace {
    1.69 +
    1.70 +NSSCleanupAutoPtrClass(void, PR_FREEIF)
    1.71 +
    1.72 +void
    1.73 +getSiteKey(const nsACString& hostName, uint16_t port,
    1.74 +           /*out*/ nsCSubstring& key)
    1.75 +{
    1.76 +  key = hostName;
    1.77 +  key.AppendASCII(":");
    1.78 +  key.AppendInt(port);
    1.79 +}
    1.80 +
    1.81 +// SSM_UserCertChoice: enum for cert choice info
    1.82 +typedef enum {ASK, AUTO} SSM_UserCertChoice;
    1.83 +
    1.84 +// Forward secrecy provides us with a proof of posession of the private key
    1.85 +// from the server. Without of proof of posession of the private key of the
    1.86 +// server, any MitM can force us to false start in a connection that the real
    1.87 +// server never participates in, since with RSA key exchange a MitM can
    1.88 +// complete the server's first round of the handshake without knowing the
    1.89 +// server's public key This would be used, for example, to greatly accelerate
    1.90 +// the attacks on RC4 or other attacks that allow a MitM to decrypt encrypted
    1.91 +// data without having the server's private key. Without false start, such
    1.92 +// attacks are naturally rate limited by network latency and may also be rate
    1.93 +// limited explicitly by the server's DoS or other security mechanisms.
    1.94 +// Further, because the server that has the private key must participate in the
    1.95 +// handshake, the server could detect these kinds of attacks if they they are
    1.96 +// repeated rapidly and/or frequently, by noticing lots of invalid or
    1.97 +// incomplete handshakes.
    1.98 +//
    1.99 +// With this in mind, when we choose not to require forward secrecy (when the
   1.100 +// pref's value is false), then we will still only false start for RSA key
   1.101 +// exchange only if the most recent handshake we've previously done used RSA
   1.102 +// key exchange. This way, we prevent any (EC)DHE-to-RSA downgrade attacks for
   1.103 +// servers that consistently choose (EC)DHE key exchange. In order to prevent
   1.104 +// downgrade from ECDHE_*_GCM cipher suites, we need to also consider downgrade
   1.105 +// from TLS 1.2 to earlier versions (bug 861310).
   1.106 +static const bool FALSE_START_REQUIRE_FORWARD_SECRECY_DEFAULT = true;
   1.107 +
   1.108 +// XXX(perf bug 940787): We currently require NPN because there is a very
   1.109 +// high (perfect so far) correlation between servers that are false-start-
   1.110 +// tolerant and servers that support NPN, according to Google. Without this, we
   1.111 +// will run into interop issues with a small percentage of servers that stop
   1.112 +// responding when we attempt to false start.
   1.113 +static const bool FALSE_START_REQUIRE_NPN_DEFAULT = true;
   1.114 +
   1.115 +} // unnamed namespace
   1.116 +
   1.117 +#ifdef PR_LOGGING
   1.118 +extern PRLogModuleInfo* gPIPNSSLog;
   1.119 +#endif
   1.120 +
   1.121 +nsNSSSocketInfo::nsNSSSocketInfo(SharedSSLState& aState, uint32_t providerFlags)
   1.122 +  : mFd(nullptr),
   1.123 +    mCertVerificationState(before_cert_verification),
   1.124 +    mSharedState(aState),
   1.125 +    mForSTARTTLS(false),
   1.126 +    mHandshakePending(true),
   1.127 +    mRememberClientAuthCertificate(false),
   1.128 +    mPreliminaryHandshakeDone(false),
   1.129 +    mNPNCompleted(false),
   1.130 +    mFalseStartCallbackCalled(false),
   1.131 +    mFalseStarted(false),
   1.132 +    mIsFullHandshake(false),
   1.133 +    mHandshakeCompleted(false),
   1.134 +    mJoined(false),
   1.135 +    mSentClientCert(false),
   1.136 +    mNotedTimeUntilReady(false),
   1.137 +    mKEAUsed(nsISSLSocketControl::KEY_EXCHANGE_UNKNOWN),
   1.138 +    mKEAExpected(nsISSLSocketControl::KEY_EXCHANGE_UNKNOWN),
   1.139 +    mSSLVersionUsed(nsISSLSocketControl::SSL_VERSION_UNKNOWN),
   1.140 +    mProviderFlags(providerFlags),
   1.141 +    mSocketCreationTimestamp(TimeStamp::Now()),
   1.142 +    mPlaintextBytesRead(0)
   1.143 +{
   1.144 +  mTLSVersionRange.min = 0;
   1.145 +  mTLSVersionRange.max = 0;
   1.146 +}
   1.147 +
   1.148 +NS_IMPL_ISUPPORTS_INHERITED(nsNSSSocketInfo, TransportSecurityInfo,
   1.149 +                            nsISSLSocketControl,
   1.150 +                            nsIClientAuthUserDecision)
   1.151 +
   1.152 +NS_IMETHODIMP
   1.153 +nsNSSSocketInfo::GetProviderFlags(uint32_t* aProviderFlags)
   1.154 +{
   1.155 +  *aProviderFlags = mProviderFlags;
   1.156 +  return NS_OK;
   1.157 +}
   1.158 +
   1.159 +NS_IMETHODIMP
   1.160 +nsNSSSocketInfo::GetKEAUsed(int16_t* aKea)
   1.161 +{
   1.162 +  *aKea = mKEAUsed;
   1.163 +  return NS_OK;
   1.164 +}
   1.165 +
   1.166 +NS_IMETHODIMP
   1.167 +nsNSSSocketInfo::GetKEAExpected(int16_t* aKea)
   1.168 +{
   1.169 +  *aKea = mKEAExpected;
   1.170 +  return NS_OK;
   1.171 +}
   1.172 +
   1.173 +NS_IMETHODIMP
   1.174 +nsNSSSocketInfo::SetKEAExpected(int16_t aKea)
   1.175 +{
   1.176 +  mKEAExpected = aKea;
   1.177 +  return NS_OK;
   1.178 +}
   1.179 +
   1.180 +NS_IMETHODIMP
   1.181 +nsNSSSocketInfo::GetSSLVersionUsed(int16_t* aSSLVersionUsed)
   1.182 +{
   1.183 +  *aSSLVersionUsed = mSSLVersionUsed;
   1.184 +  return NS_OK;
   1.185 +}
   1.186 +
   1.187 +NS_IMETHODIMP
   1.188 +nsNSSSocketInfo::GetRememberClientAuthCertificate(bool* aRemember)
   1.189 +{
   1.190 +  NS_ENSURE_ARG_POINTER(aRemember);
   1.191 +  *aRemember = mRememberClientAuthCertificate;
   1.192 +  return NS_OK;
   1.193 +}
   1.194 +
   1.195 +NS_IMETHODIMP
   1.196 +nsNSSSocketInfo::SetRememberClientAuthCertificate(bool aRemember)
   1.197 +{
   1.198 +  mRememberClientAuthCertificate = aRemember;
   1.199 +  return NS_OK;
   1.200 +}
   1.201 +
   1.202 +NS_IMETHODIMP
   1.203 +nsNSSSocketInfo::GetNotificationCallbacks(nsIInterfaceRequestor** aCallbacks)
   1.204 +{
   1.205 +  *aCallbacks = mCallbacks;
   1.206 +  NS_IF_ADDREF(*aCallbacks);
   1.207 +  return NS_OK;
   1.208 +}
   1.209 +
   1.210 +NS_IMETHODIMP
   1.211 +nsNSSSocketInfo::SetNotificationCallbacks(nsIInterfaceRequestor* aCallbacks)
   1.212 +{
   1.213 +  if (!aCallbacks) {
   1.214 +    mCallbacks = nullptr;
   1.215 +    return NS_OK;
   1.216 +  }
   1.217 +
   1.218 +  mCallbacks = aCallbacks;
   1.219 +
   1.220 +  return NS_OK;
   1.221 +}
   1.222 +
   1.223 +#ifndef MOZ_NO_EV_CERTS
   1.224 +static void
   1.225 +getSecureBrowserUI(nsIInterfaceRequestor* callbacks,
   1.226 +                   nsISecureBrowserUI** result)
   1.227 +{
   1.228 +  NS_ASSERTION(result, "result parameter to getSecureBrowserUI is null");
   1.229 +  *result = nullptr;
   1.230 +
   1.231 +  NS_ASSERTION(NS_IsMainThread(),
   1.232 +               "getSecureBrowserUI called off the main thread");
   1.233 +
   1.234 +  if (!callbacks)
   1.235 +    return;
   1.236 +
   1.237 +  nsCOMPtr<nsISecureBrowserUI> secureUI = do_GetInterface(callbacks);
   1.238 +  if (secureUI) {
   1.239 +    secureUI.forget(result);
   1.240 +    return;
   1.241 +  }
   1.242 +
   1.243 +  nsCOMPtr<nsIDocShellTreeItem> item = do_GetInterface(callbacks);
   1.244 +  if (item) {
   1.245 +    nsCOMPtr<nsIDocShellTreeItem> rootItem;
   1.246 +    (void) item->GetSameTypeRootTreeItem(getter_AddRefs(rootItem));
   1.247 +
   1.248 +    nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(rootItem);
   1.249 +    if (docShell) {
   1.250 +      (void) docShell->GetSecurityUI(result);
   1.251 +    }
   1.252 +  }
   1.253 +}
   1.254 +#endif
   1.255 +
   1.256 +void
   1.257 +nsNSSSocketInfo::NoteTimeUntilReady()
   1.258 +{
   1.259 +  if (mNotedTimeUntilReady)
   1.260 +    return;
   1.261 +
   1.262 +  mNotedTimeUntilReady = true;
   1.263 +
   1.264 +  // This will include TCP and proxy tunnel wait time
   1.265 +  Telemetry::AccumulateTimeDelta(Telemetry::SSL_TIME_UNTIL_READY,
   1.266 +                                 mSocketCreationTimestamp, TimeStamp::Now());
   1.267 +  PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
   1.268 +         ("[%p] nsNSSSocketInfo::NoteTimeUntilReady\n", mFd));
   1.269 +}
   1.270 +
   1.271 +void
   1.272 +nsNSSSocketInfo::SetHandshakeCompleted()
   1.273 +{
   1.274 +  if (!mHandshakeCompleted) {
   1.275 +    enum HandshakeType {
   1.276 +      Resumption = 1,
   1.277 +      FalseStarted = 2,
   1.278 +      ChoseNotToFalseStart = 3,
   1.279 +      NotAllowedToFalseStart = 4,
   1.280 +    };
   1.281 +
   1.282 +    HandshakeType handshakeType = !IsFullHandshake() ? Resumption
   1.283 +                                : mFalseStarted ? FalseStarted
   1.284 +                                : mFalseStartCallbackCalled ? ChoseNotToFalseStart
   1.285 +                                : NotAllowedToFalseStart;
   1.286 +
   1.287 +    // This will include TCP and proxy tunnel wait time
   1.288 +    Telemetry::AccumulateTimeDelta(Telemetry::SSL_TIME_UNTIL_HANDSHAKE_FINISHED,
   1.289 +                                   mSocketCreationTimestamp, TimeStamp::Now());
   1.290 +
   1.291 +    // If the handshake is completed for the first time from just 1 callback
   1.292 +    // that means that TLS session resumption must have been used.
   1.293 +    Telemetry::Accumulate(Telemetry::SSL_RESUMED_SESSION,
   1.294 +                          handshakeType == Resumption);
   1.295 +    Telemetry::Accumulate(Telemetry::SSL_HANDSHAKE_TYPE, handshakeType);
   1.296 +  }
   1.297 +
   1.298 +
   1.299 +    // Remove the plain text layer as it is not needed anymore.
   1.300 +    // The plain text layer is not always present - so its not a fatal error
   1.301 +    // if it cannot be removed
   1.302 +    PRFileDesc* poppedPlaintext =
   1.303 +      PR_GetIdentitiesLayer(mFd, nsSSLIOLayerHelpers::nsSSLPlaintextLayerIdentity);
   1.304 +    if (poppedPlaintext) {
   1.305 +      PR_PopIOLayer(mFd, nsSSLIOLayerHelpers::nsSSLPlaintextLayerIdentity);
   1.306 +      poppedPlaintext->dtor(poppedPlaintext);
   1.307 +    }
   1.308 +
   1.309 +    mHandshakeCompleted = true;
   1.310 +
   1.311 +    PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
   1.312 +           ("[%p] nsNSSSocketInfo::SetHandshakeCompleted\n", (void*) mFd));
   1.313 +
   1.314 +    mIsFullHandshake = false; // reset for next handshake on this connection
   1.315 +}
   1.316 +
   1.317 +void
   1.318 +nsNSSSocketInfo::SetNegotiatedNPN(const char* value, uint32_t length)
   1.319 +{
   1.320 +  if (!value) {
   1.321 +    mNegotiatedNPN.Truncate();
   1.322 +  } else {
   1.323 +    mNegotiatedNPN.Assign(value, length);
   1.324 +  }
   1.325 +  mNPNCompleted = true;
   1.326 +}
   1.327 +
   1.328 +NS_IMETHODIMP
   1.329 +nsNSSSocketInfo::GetNegotiatedNPN(nsACString& aNegotiatedNPN)
   1.330 +{
   1.331 +  if (!mNPNCompleted)
   1.332 +    return NS_ERROR_NOT_CONNECTED;
   1.333 +
   1.334 +  aNegotiatedNPN = mNegotiatedNPN;
   1.335 +  return NS_OK;
   1.336 +}
   1.337 +
   1.338 +NS_IMETHODIMP
   1.339 +nsNSSSocketInfo::JoinConnection(const nsACString& npnProtocol,
   1.340 +                                const nsACString& hostname,
   1.341 +                                int32_t port,
   1.342 +                                bool* _retval)
   1.343 +{
   1.344 +  *_retval = false;
   1.345 +
   1.346 +  // Different ports may not be joined together
   1.347 +  if (port != GetPort())
   1.348 +    return NS_OK;
   1.349 +
   1.350 +  // Make sure NPN has been completed and matches requested npnProtocol
   1.351 +  if (!mNPNCompleted || !mNegotiatedNPN.Equals(npnProtocol))
   1.352 +    return NS_OK;
   1.353 +
   1.354 +  // If this is the same hostname then the certicate status does not
   1.355 +  // need to be considered. They are joinable.
   1.356 +  if (hostname.Equals(GetHostName())) {
   1.357 +    *_retval = true;
   1.358 +    return NS_OK;
   1.359 +  }
   1.360 +
   1.361 +  // Before checking the server certificate we need to make sure the
   1.362 +  // handshake has completed.
   1.363 +  if (!mHandshakeCompleted || !SSLStatus() || !SSLStatus()->mServerCert)
   1.364 +    return NS_OK;
   1.365 +
   1.366 +  // If the cert has error bits (e.g. it is untrusted) then do not join.
   1.367 +  // The value of mHaveCertErrorBits is only reliable because we know that
   1.368 +  // the handshake completed.
   1.369 +  if (SSLStatus()->mHaveCertErrorBits)
   1.370 +    return NS_OK;
   1.371 +
   1.372 +  // If the connection is using client certificates then do not join
   1.373 +  // because the user decides on whether to send client certs to hosts on a
   1.374 +  // per-domain basis.
   1.375 +  if (mSentClientCert)
   1.376 +    return NS_OK;
   1.377 +
   1.378 +  // Ensure that the server certificate covers the hostname that would
   1.379 +  // like to join this connection
   1.380 +
   1.381 +  ScopedCERTCertificate nssCert;
   1.382 +
   1.383 +  nsCOMPtr<nsIX509Cert2> cert2 = do_QueryInterface(SSLStatus()->mServerCert);
   1.384 +  if (cert2)
   1.385 +    nssCert = cert2->GetCert();
   1.386 +
   1.387 +  if (!nssCert)
   1.388 +    return NS_OK;
   1.389 +
   1.390 +  if (CERT_VerifyCertName(nssCert, PromiseFlatCString(hostname).get()) !=
   1.391 +      SECSuccess)
   1.392 +    return NS_OK;
   1.393 +
   1.394 +  // All tests pass - this is joinable
   1.395 +  mJoined = true;
   1.396 +  *_retval = true;
   1.397 +  return NS_OK;
   1.398 +}
   1.399 +
   1.400 +bool
   1.401 +nsNSSSocketInfo::GetForSTARTTLS()
   1.402 +{
   1.403 +  return mForSTARTTLS;
   1.404 +}
   1.405 +
   1.406 +void
   1.407 +nsNSSSocketInfo::SetForSTARTTLS(bool aForSTARTTLS)
   1.408 +{
   1.409 +  mForSTARTTLS = aForSTARTTLS;
   1.410 +}
   1.411 +
   1.412 +NS_IMETHODIMP
   1.413 +nsNSSSocketInfo::ProxyStartSSL()
   1.414 +{
   1.415 +  return ActivateSSL();
   1.416 +}
   1.417 +
   1.418 +NS_IMETHODIMP
   1.419 +nsNSSSocketInfo::StartTLS()
   1.420 +{
   1.421 +  return ActivateSSL();
   1.422 +}
   1.423 +
   1.424 +NS_IMETHODIMP
   1.425 +nsNSSSocketInfo::SetNPNList(nsTArray<nsCString>& protocolArray)
   1.426 +{
   1.427 +  nsNSSShutDownPreventionLock locker;
   1.428 +  if (isAlreadyShutDown())
   1.429 +    return NS_ERROR_NOT_AVAILABLE;
   1.430 +  if (!mFd)
   1.431 +    return NS_ERROR_FAILURE;
   1.432 +
   1.433 +  // the npn list is a concatenated list of 8 bit byte strings.
   1.434 +  nsCString npnList;
   1.435 +
   1.436 +  for (uint32_t index = 0; index < protocolArray.Length(); ++index) {
   1.437 +    if (protocolArray[index].IsEmpty() ||
   1.438 +        protocolArray[index].Length() > 255)
   1.439 +      return NS_ERROR_ILLEGAL_VALUE;
   1.440 +
   1.441 +    npnList.Append(protocolArray[index].Length());
   1.442 +    npnList.Append(protocolArray[index]);
   1.443 +  }
   1.444 +
   1.445 +  if (SSL_SetNextProtoNego(
   1.446 +        mFd,
   1.447 +        reinterpret_cast<const unsigned char*>(npnList.get()),
   1.448 +        npnList.Length()) != SECSuccess)
   1.449 +    return NS_ERROR_FAILURE;
   1.450 +
   1.451 +  return NS_OK;
   1.452 +}
   1.453 +
   1.454 +nsresult
   1.455 +nsNSSSocketInfo::ActivateSSL()
   1.456 +{
   1.457 +  nsNSSShutDownPreventionLock locker;
   1.458 +  if (isAlreadyShutDown())
   1.459 +    return NS_ERROR_NOT_AVAILABLE;
   1.460 +
   1.461 +  if (SECSuccess != SSL_OptionSet(mFd, SSL_SECURITY, true))
   1.462 +    return NS_ERROR_FAILURE;
   1.463 +  if (SECSuccess != SSL_ResetHandshake(mFd, false))
   1.464 +    return NS_ERROR_FAILURE;
   1.465 +
   1.466 +  mHandshakePending = true;
   1.467 +
   1.468 +  return NS_OK;
   1.469 +}
   1.470 +
   1.471 +nsresult
   1.472 +nsNSSSocketInfo::GetFileDescPtr(PRFileDesc** aFilePtr)
   1.473 +{
   1.474 +  *aFilePtr = mFd;
   1.475 +  return NS_OK;
   1.476 +}
   1.477 +
   1.478 +nsresult
   1.479 +nsNSSSocketInfo::SetFileDescPtr(PRFileDesc* aFilePtr)
   1.480 +{
   1.481 +  mFd = aFilePtr;
   1.482 +  return NS_OK;
   1.483 +}
   1.484 +
   1.485 +#ifndef MOZ_NO_EV_CERTS
   1.486 +class PreviousCertRunnable : public SyncRunnableBase
   1.487 +{
   1.488 +public:
   1.489 +  PreviousCertRunnable(nsIInterfaceRequestor* callbacks)
   1.490 +    : mCallbacks(callbacks)
   1.491 +  {
   1.492 +  }
   1.493 +
   1.494 +  virtual void RunOnTargetThread()
   1.495 +  {
   1.496 +    nsCOMPtr<nsISecureBrowserUI> secureUI;
   1.497 +    getSecureBrowserUI(mCallbacks, getter_AddRefs(secureUI));
   1.498 +    nsCOMPtr<nsISSLStatusProvider> statusProvider = do_QueryInterface(secureUI);
   1.499 +    if (statusProvider) {
   1.500 +      nsCOMPtr<nsISSLStatus> status;
   1.501 +      (void) statusProvider->GetSSLStatus(getter_AddRefs(status));
   1.502 +      if (status) {
   1.503 +        (void) status->GetServerCert(getter_AddRefs(mPreviousCert));
   1.504 +      }
   1.505 +    }
   1.506 +  }
   1.507 +
   1.508 +  nsCOMPtr<nsIX509Cert> mPreviousCert; // out
   1.509 +private:
   1.510 +  nsCOMPtr<nsIInterfaceRequestor> mCallbacks; // in
   1.511 +};
   1.512 +#endif
   1.513 +
   1.514 +void
   1.515 +nsNSSSocketInfo::GetPreviousCert(nsIX509Cert** _result)
   1.516 +{
   1.517 +  NS_ASSERTION(_result, "_result parameter to GetPreviousCert is null");
   1.518 +  *_result = nullptr;
   1.519 +
   1.520 +#ifndef MOZ_NO_EV_CERTS
   1.521 +  RefPtr<PreviousCertRunnable> runnable(new PreviousCertRunnable(mCallbacks));
   1.522 +  DebugOnly<nsresult> rv = runnable->DispatchToMainThreadAndWait();
   1.523 +  NS_ASSERTION(NS_SUCCEEDED(rv), "runnable->DispatchToMainThreadAndWait() failed");
   1.524 +  runnable->mPreviousCert.forget(_result);
   1.525 +#endif
   1.526 +}
   1.527 +
   1.528 +void
   1.529 +nsNSSSocketInfo::SetCertVerificationWaiting()
   1.530 +{
   1.531 +  // mCertVerificationState may be before_cert_verification for the first
   1.532 +  // handshake on the connection, or after_cert_verification for subsequent
   1.533 +  // renegotiation handshakes.
   1.534 +  NS_ASSERTION(mCertVerificationState != waiting_for_cert_verification,
   1.535 +               "Invalid state transition to waiting_for_cert_verification");
   1.536 +  mCertVerificationState = waiting_for_cert_verification;
   1.537 +}
   1.538 +
   1.539 +// Be careful that SetCertVerificationResult does NOT get called while we are
   1.540 +// processing a SSL callback function, because SSL_AuthCertificateComplete will
   1.541 +// attempt to acquire locks that are already held by libssl when it calls
   1.542 +// callbacks.
   1.543 +void
   1.544 +nsNSSSocketInfo::SetCertVerificationResult(PRErrorCode errorCode,
   1.545 +                                           SSLErrorMessageType errorMessageType)
   1.546 +{
   1.547 +  NS_ASSERTION(mCertVerificationState == waiting_for_cert_verification,
   1.548 +               "Invalid state transition to cert_verification_finished");
   1.549 +
   1.550 +  if (mFd) {
   1.551 +    SECStatus rv = SSL_AuthCertificateComplete(mFd, errorCode);
   1.552 +    // Only replace errorCode if there was originally no error
   1.553 +    if (rv != SECSuccess && errorCode == 0) {
   1.554 +      errorCode = PR_GetError();
   1.555 +      errorMessageType = PlainErrorMessage;
   1.556 +      if (errorCode == 0) {
   1.557 +        NS_ERROR("SSL_AuthCertificateComplete didn't set error code");
   1.558 +        errorCode = PR_INVALID_STATE_ERROR;
   1.559 +      }
   1.560 +    }
   1.561 +  }
   1.562 +
   1.563 +  if (errorCode) {
   1.564 +    SetCanceled(errorCode, errorMessageType);
   1.565 +  }
   1.566 +
   1.567 +  if (mPlaintextBytesRead && !errorCode) {
   1.568 +    Telemetry::Accumulate(Telemetry::SSL_BYTES_BEFORE_CERT_CALLBACK,
   1.569 +                          SafeCast<uint32_t>(mPlaintextBytesRead));
   1.570 +  }
   1.571 +
   1.572 +  mCertVerificationState = after_cert_verification;
   1.573 +}
   1.574 +
   1.575 +SharedSSLState&
   1.576 +nsNSSSocketInfo::SharedState()
   1.577 +{
   1.578 +  return mSharedState;
   1.579 +}
   1.580 +
   1.581 +void nsSSLIOLayerHelpers::Cleanup()
   1.582 +{
   1.583 +  mTLSIntoleranceInfo.Clear();
   1.584 +
   1.585 +  if (mRenegoUnrestrictedSites) {
   1.586 +    delete mRenegoUnrestrictedSites;
   1.587 +    mRenegoUnrestrictedSites = nullptr;
   1.588 +  }
   1.589 +}
   1.590 +
   1.591 +static void
   1.592 +nsHandleSSLError(nsNSSSocketInfo* socketInfo,
   1.593 +                 ::mozilla::psm::SSLErrorMessageType errtype,
   1.594 +                 PRErrorCode err)
   1.595 +{
   1.596 +  if (!NS_IsMainThread()) {
   1.597 +    NS_ERROR("nsHandleSSLError called off the main thread");
   1.598 +    return;
   1.599 +  }
   1.600 +
   1.601 +  // SetCanceled is only called by the main thread or the socket transport
   1.602 +  // thread. Whenever this function is called on the main thread, the SSL
   1.603 +  // thread is blocked on it. So, no mutex is necessary for
   1.604 +  // SetCanceled()/GetError*().
   1.605 +  if (socketInfo->GetErrorCode()) {
   1.606 +    // If the socket has been flagged as canceled,
   1.607 +    // the code who did was responsible for setting the error code.
   1.608 +    return;
   1.609 +  }
   1.610 +
   1.611 +  nsresult rv;
   1.612 +  NS_DEFINE_CID(nssComponentCID, NS_NSSCOMPONENT_CID);
   1.613 +  nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(nssComponentCID, &rv));
   1.614 +  if (NS_FAILED(rv))
   1.615 +    return;
   1.616 +
   1.617 +  // Try to get a nsISSLErrorListener implementation from the socket consumer.
   1.618 +  nsCOMPtr<nsIInterfaceRequestor> cb;
   1.619 +  socketInfo->GetNotificationCallbacks(getter_AddRefs(cb));
   1.620 +  if (cb) {
   1.621 +    nsCOMPtr<nsISSLErrorListener> sel = do_GetInterface(cb);
   1.622 +    if (sel) {
   1.623 +      nsIInterfaceRequestor* csi = static_cast<nsIInterfaceRequestor*>(socketInfo);
   1.624 +
   1.625 +      nsCString hostWithPortString;
   1.626 +      getSiteKey(socketInfo->GetHostName(), socketInfo->GetPort(),
   1.627 +                 hostWithPortString);
   1.628 +
   1.629 +      bool suppressMessage = false; // obsolete, ignored
   1.630 +      rv = sel->NotifySSLError(csi, err, hostWithPortString, &suppressMessage);
   1.631 +    }
   1.632 +  }
   1.633 +
   1.634 +  // We must cancel first, which sets the error code.
   1.635 +  socketInfo->SetCanceled(err, PlainErrorMessage);
   1.636 +  nsXPIDLString errorString;
   1.637 +  socketInfo->GetErrorLogMessage(err, errtype, errorString);
   1.638 +
   1.639 +  if (!errorString.IsEmpty()) {
   1.640 +    nsContentUtils::LogSimpleConsoleError(errorString, "SSL");
   1.641 +  }
   1.642 +}
   1.643 +
   1.644 +namespace {
   1.645 +
   1.646 +enum Operation { reading, writing, not_reading_or_writing };
   1.647 +
   1.648 +int32_t checkHandshake(int32_t bytesTransfered, bool wasReading,
   1.649 +                       PRFileDesc* ssl_layer_fd,
   1.650 +                       nsNSSSocketInfo* socketInfo);
   1.651 +
   1.652 +nsNSSSocketInfo*
   1.653 +getSocketInfoIfRunning(PRFileDesc* fd, Operation op,
   1.654 +                       const nsNSSShutDownPreventionLock& /*proofOfLock*/)
   1.655 +{
   1.656 +  if (!fd || !fd->lower || !fd->secret ||
   1.657 +      fd->identity != nsSSLIOLayerHelpers::nsSSLIOLayerIdentity) {
   1.658 +    NS_ERROR("bad file descriptor passed to getSocketInfoIfRunning");
   1.659 +    PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0);
   1.660 +    return nullptr;
   1.661 +  }
   1.662 +
   1.663 +  nsNSSSocketInfo* socketInfo = (nsNSSSocketInfo*) fd->secret;
   1.664 +
   1.665 +  if (socketInfo->isAlreadyShutDown() || socketInfo->isPK11LoggedOut()) {
   1.666 +    PR_SetError(PR_SOCKET_SHUTDOWN_ERROR, 0);
   1.667 +    return nullptr;
   1.668 +  }
   1.669 +
   1.670 +  if (socketInfo->GetErrorCode()) {
   1.671 +    PRErrorCode err = socketInfo->GetErrorCode();
   1.672 +    PR_SetError(err, 0);
   1.673 +    if (op == reading || op == writing) {
   1.674 +      // We must do TLS intolerance checks for reads and writes, for timeouts
   1.675 +      // in particular.
   1.676 +      (void) checkHandshake(-1, op == reading, fd, socketInfo);
   1.677 +    }
   1.678 +
   1.679 +    // If we get here, it is probably because cert verification failed and this
   1.680 +    // is the first I/O attempt since that failure.
   1.681 +    return nullptr;
   1.682 +  }
   1.683 +
   1.684 +  return socketInfo;
   1.685 +}
   1.686 +
   1.687 +} // unnnamed namespace
   1.688 +
   1.689 +static PRStatus
   1.690 +nsSSLIOLayerConnect(PRFileDesc* fd, const PRNetAddr* addr,
   1.691 +                    PRIntervalTime timeout)
   1.692 +{
   1.693 +  PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p] connecting SSL socket\n",
   1.694 +         (void*) fd));
   1.695 +  nsNSSShutDownPreventionLock locker;
   1.696 +  if (!getSocketInfoIfRunning(fd, not_reading_or_writing, locker))
   1.697 +    return PR_FAILURE;
   1.698 +
   1.699 +  PRStatus status = fd->lower->methods->connect(fd->lower, addr, timeout);
   1.700 +  if (status != PR_SUCCESS) {
   1.701 +    PR_LOG(gPIPNSSLog, PR_LOG_ERROR, ("[%p] Lower layer connect error: %d\n",
   1.702 +                                      (void*) fd, PR_GetError()));
   1.703 +    return status;
   1.704 +  }
   1.705 +
   1.706 +  PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p] Connect\n", (void*) fd));
   1.707 +  return status;
   1.708 +}
   1.709 +
   1.710 +void
   1.711 +nsSSLIOLayerHelpers::rememberTolerantAtVersion(const nsACString& hostName,
   1.712 +                                               int16_t port, uint16_t tolerant)
   1.713 +{
   1.714 +  nsCString key;
   1.715 +  getSiteKey(hostName, port, key);
   1.716 +
   1.717 +  MutexAutoLock lock(mutex);
   1.718 +
   1.719 +  IntoleranceEntry entry;
   1.720 +  if (mTLSIntoleranceInfo.Get(key, &entry)) {
   1.721 +    entry.AssertInvariant();
   1.722 +    entry.tolerant = std::max(entry.tolerant, tolerant);
   1.723 +    if (entry.intolerant != 0 && entry.intolerant <= entry.tolerant) {
   1.724 +      entry.intolerant = entry.tolerant + 1;
   1.725 +    }
   1.726 +  } else {
   1.727 +    entry.tolerant = tolerant;
   1.728 +    entry.intolerant = 0;
   1.729 +  }
   1.730 +
   1.731 +  entry.AssertInvariant();
   1.732 +
   1.733 +  mTLSIntoleranceInfo.Put(key, entry);
   1.734 +}
   1.735 +
   1.736 +// returns true if we should retry the handshake
   1.737 +bool
   1.738 +nsSSLIOLayerHelpers::rememberIntolerantAtVersion(const nsACString& hostName,
   1.739 +                                                 int16_t port,
   1.740 +                                                 uint16_t minVersion,
   1.741 +                                                 uint16_t intolerant)
   1.742 +{
   1.743 +  nsCString key;
   1.744 +  getSiteKey(hostName, port, key);
   1.745 +
   1.746 +  MutexAutoLock lock(mutex);
   1.747 +
   1.748 +  if (intolerant <= minVersion) {
   1.749 +    // We can't fall back any further. Assume that intolerance isn't the issue.
   1.750 +    IntoleranceEntry entry;
   1.751 +    if (mTLSIntoleranceInfo.Get(key, &entry)) {
   1.752 +      entry.AssertInvariant();
   1.753 +      entry.intolerant = 0;
   1.754 +      entry.AssertInvariant();
   1.755 +      mTLSIntoleranceInfo.Put(key, entry);
   1.756 +    }
   1.757 +
   1.758 +    return false;
   1.759 +  }
   1.760 +
   1.761 +  IntoleranceEntry entry;
   1.762 +  if (mTLSIntoleranceInfo.Get(key, &entry)) {
   1.763 +    entry.AssertInvariant();
   1.764 +    if (intolerant <= entry.tolerant) {
   1.765 +      // We already know the server is tolerant at an equal or higher version.
   1.766 +      return false;
   1.767 +    }
   1.768 +    if ((entry.intolerant != 0 && intolerant >= entry.intolerant)) {
   1.769 +      // We already know that the server is intolerant at a lower version.
   1.770 +      return true;
   1.771 +    }
   1.772 +  } else {
   1.773 +    entry.tolerant = 0;
   1.774 +  }
   1.775 +
   1.776 +  entry.intolerant = intolerant;
   1.777 +  entry.AssertInvariant();
   1.778 +  mTLSIntoleranceInfo.Put(key, entry);
   1.779 +
   1.780 +  return true;
   1.781 +}
   1.782 +
   1.783 +void
   1.784 +nsSSLIOLayerHelpers::adjustForTLSIntolerance(const nsACString& hostName,
   1.785 +                                             int16_t port,
   1.786 +                                             /*in/out*/ SSLVersionRange& range)
   1.787 +{
   1.788 +  IntoleranceEntry entry;
   1.789 +
   1.790 +  {
   1.791 +    nsCString key;
   1.792 +    getSiteKey(hostName, port, key);
   1.793 +
   1.794 +    MutexAutoLock lock(mutex);
   1.795 +    if (!mTLSIntoleranceInfo.Get(key, &entry)) {
   1.796 +      return;
   1.797 +    }
   1.798 +  }
   1.799 +
   1.800 +  entry.AssertInvariant();
   1.801 +
   1.802 +  if (entry.intolerant != 0) {
   1.803 +    // We've tried connecting at a higher range but failed, so try at the
   1.804 +    // version we haven't tried yet, unless we have reached the minimum.
   1.805 +    if (range.min < entry.intolerant) {
   1.806 +      range.max = entry.intolerant - 1;
   1.807 +    }
   1.808 +  }
   1.809 +}
   1.810 +
   1.811 +bool nsSSLIOLayerHelpers::nsSSLIOLayerInitialized = false;
   1.812 +PRDescIdentity nsSSLIOLayerHelpers::nsSSLIOLayerIdentity;
   1.813 +PRDescIdentity nsSSLIOLayerHelpers::nsSSLPlaintextLayerIdentity;
   1.814 +PRIOMethods nsSSLIOLayerHelpers::nsSSLIOLayerMethods;
   1.815 +PRIOMethods nsSSLIOLayerHelpers::nsSSLPlaintextLayerMethods;
   1.816 +
   1.817 +static PRStatus
   1.818 +nsSSLIOLayerClose(PRFileDesc* fd)
   1.819 +{
   1.820 +  nsNSSShutDownPreventionLock locker;
   1.821 +  if (!fd)
   1.822 +    return PR_FAILURE;
   1.823 +
   1.824 +  PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p] Shutting down socket\n",
   1.825 +         (void*) fd));
   1.826 +
   1.827 +  nsNSSSocketInfo* socketInfo = (nsNSSSocketInfo*) fd->secret;
   1.828 +  NS_ASSERTION(socketInfo,"nsNSSSocketInfo was null for an fd");
   1.829 +
   1.830 +  return socketInfo->CloseSocketAndDestroy(locker);
   1.831 +}
   1.832 +
   1.833 +PRStatus
   1.834 +nsNSSSocketInfo::CloseSocketAndDestroy(
   1.835 +    const nsNSSShutDownPreventionLock& /*proofOfLock*/)
   1.836 +{
   1.837 +  nsNSSShutDownList::trackSSLSocketClose();
   1.838 +
   1.839 +  PRFileDesc* popped = PR_PopIOLayer(mFd, PR_TOP_IO_LAYER);
   1.840 +  NS_ASSERTION(popped &&
   1.841 +               popped->identity == nsSSLIOLayerHelpers::nsSSLIOLayerIdentity,
   1.842 +               "SSL Layer not on top of stack");
   1.843 +
   1.844 +  // The plain text layer is not always present - so its not a fatal error
   1.845 +  // if it cannot be removed
   1.846 +  PRFileDesc* poppedPlaintext =
   1.847 +    PR_GetIdentitiesLayer(mFd, nsSSLIOLayerHelpers::nsSSLPlaintextLayerIdentity);
   1.848 +  if (poppedPlaintext) {
   1.849 +    PR_PopIOLayer(mFd, nsSSLIOLayerHelpers::nsSSLPlaintextLayerIdentity);
   1.850 +    poppedPlaintext->dtor(poppedPlaintext);
   1.851 +  }
   1.852 +
   1.853 +  PRStatus status = mFd->methods->close(mFd);
   1.854 +
   1.855 +  // the nsNSSSocketInfo instance can out-live the connection, so we need some
   1.856 +  // indication that the connection has been closed. mFd == nullptr is that
   1.857 +  // indication. This is needed, for example, when the connection is closed
   1.858 +  // before we have finished validating the server's certificate.
   1.859 +  mFd = nullptr;
   1.860 +
   1.861 +  if (status != PR_SUCCESS) return status;
   1.862 +
   1.863 +  popped->identity = PR_INVALID_IO_LAYER;
   1.864 +  NS_RELEASE_THIS();
   1.865 +  popped->dtor(popped);
   1.866 +
   1.867 +  return PR_SUCCESS;
   1.868 +}
   1.869 +
   1.870 +#if defined(DEBUG_SSL_VERBOSE) && defined(DUMP_BUFFER)
   1.871 +// Dumps a (potentially binary) buffer using SSM_DEBUG.  (We could have used
   1.872 +// the version in ssltrace.c, but that's specifically tailored to SSLTRACE.)
   1.873 +#define DUMPBUF_LINESIZE 24
   1.874 +static void
   1.875 +nsDumpBuffer(unsigned char* buf, int len)
   1.876 +{
   1.877 +  char hexbuf[DUMPBUF_LINESIZE*3+1];
   1.878 +  char chrbuf[DUMPBUF_LINESIZE+1];
   1.879 +  static const char* hex = "0123456789abcdef";
   1.880 +  int i = 0;
   1.881 +  int l = 0;
   1.882 +  char ch;
   1.883 +  char* c;
   1.884 +  char* h;
   1.885 +  if (len == 0)
   1.886 +    return;
   1.887 +  hexbuf[DUMPBUF_LINESIZE*3] = '\0';
   1.888 +  chrbuf[DUMPBUF_LINESIZE] = '\0';
   1.889 +  (void) memset(hexbuf, 0x20, DUMPBUF_LINESIZE*3);
   1.890 +  (void) memset(chrbuf, 0x20, DUMPBUF_LINESIZE);
   1.891 +  h = hexbuf;
   1.892 +  c = chrbuf;
   1.893 +
   1.894 +  while (i < len) {
   1.895 +    ch = buf[i];
   1.896 +
   1.897 +    if (l == DUMPBUF_LINESIZE) {
   1.898 +      PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("%s%s\n", hexbuf, chrbuf));
   1.899 +      (void) memset(hexbuf, 0x20, DUMPBUF_LINESIZE*3);
   1.900 +      (void) memset(chrbuf, 0x20, DUMPBUF_LINESIZE);
   1.901 +      h = hexbuf;
   1.902 +      c = chrbuf;
   1.903 +      l = 0;
   1.904 +    }
   1.905 +
   1.906 +    // Convert a character to hex.
   1.907 +    *h++ = hex[(ch >> 4) & 0xf];
   1.908 +    *h++ = hex[ch & 0xf];
   1.909 +    h++;
   1.910 +
   1.911 +    // Put the character (if it's printable) into the character buffer.
   1.912 +    if ((ch >= 0x20) && (ch <= 0x7e)) {
   1.913 +      *c++ = ch;
   1.914 +    } else {
   1.915 +      *c++ = '.';
   1.916 +    }
   1.917 +    i++; l++;
   1.918 +  }
   1.919 +  PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("%s%s\n", hexbuf, chrbuf));
   1.920 +}
   1.921 +
   1.922 +#define DEBUG_DUMP_BUFFER(buf,len) nsDumpBuffer(buf,len)
   1.923 +#else
   1.924 +#define DEBUG_DUMP_BUFFER(buf,len)
   1.925 +#endif
   1.926 +
   1.927 +class SSLErrorRunnable : public SyncRunnableBase
   1.928 +{
   1.929 + public:
   1.930 +  SSLErrorRunnable(nsNSSSocketInfo* infoObject,
   1.931 +                   ::mozilla::psm::SSLErrorMessageType errtype,
   1.932 +                   PRErrorCode errorCode)
   1.933 +    : mInfoObject(infoObject)
   1.934 +    , mErrType(errtype)
   1.935 +    , mErrorCode(errorCode)
   1.936 +  {
   1.937 +  }
   1.938 +
   1.939 +  virtual void RunOnTargetThread()
   1.940 +  {
   1.941 +    nsHandleSSLError(mInfoObject, mErrType, mErrorCode);
   1.942 +  }
   1.943 +
   1.944 +  RefPtr<nsNSSSocketInfo> mInfoObject;
   1.945 +  ::mozilla::psm::SSLErrorMessageType mErrType;
   1.946 +  const PRErrorCode mErrorCode;
   1.947 +};
   1.948 +
   1.949 +namespace {
   1.950 +
   1.951 +bool
   1.952 +retryDueToTLSIntolerance(PRErrorCode err, nsNSSSocketInfo* socketInfo)
   1.953 +{
   1.954 +  // This function is supposed to decide which error codes should
   1.955 +  // be used to conclude server is TLS intolerant.
   1.956 +  // Note this only happens during the initial SSL handshake.
   1.957 +
   1.958 +  SSLVersionRange range = socketInfo->GetTLSVersionRange();
   1.959 +
   1.960 +  uint32_t reason;
   1.961 +  switch (err) {
   1.962 +    case SSL_ERROR_BAD_MAC_ALERT: reason = 1; break;
   1.963 +    case SSL_ERROR_BAD_MAC_READ: reason = 2; break;
   1.964 +    case SSL_ERROR_HANDSHAKE_FAILURE_ALERT: reason = 3; break;
   1.965 +    case SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT: reason = 4; break;
   1.966 +    case SSL_ERROR_CLIENT_KEY_EXCHANGE_FAILURE: reason = 5; break;
   1.967 +    case SSL_ERROR_ILLEGAL_PARAMETER_ALERT: reason = 6; break;
   1.968 +    case SSL_ERROR_NO_CYPHER_OVERLAP: reason = 7; break;
   1.969 +    case SSL_ERROR_BAD_SERVER: reason = 8; break;
   1.970 +    case SSL_ERROR_BAD_BLOCK_PADDING: reason = 9; break;
   1.971 +    case SSL_ERROR_UNSUPPORTED_VERSION: reason = 10; break;
   1.972 +    case SSL_ERROR_PROTOCOL_VERSION_ALERT: reason = 11; break;
   1.973 +    case SSL_ERROR_RX_MALFORMED_FINISHED: reason = 12; break;
   1.974 +    case SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE: reason = 13; break;
   1.975 +    case SSL_ERROR_DECODE_ERROR_ALERT: reason = 14; break;
   1.976 +    case SSL_ERROR_RX_UNKNOWN_ALERT: reason = 15; break;
   1.977 +
   1.978 +    case PR_CONNECT_RESET_ERROR: reason = 16; goto conditional;
   1.979 +    case PR_END_OF_FILE_ERROR: reason = 17; goto conditional;
   1.980 +
   1.981 +      // When not using a proxy we'll see a connection reset error.
   1.982 +      // When using a proxy, we'll see an end of file error.
   1.983 +      // In addition check for some error codes where it is reasonable
   1.984 +      // to retry without TLS.
   1.985 +
   1.986 +      // Don't allow STARTTLS connections to fall back on connection resets or
   1.987 +      // EOF. Also, don't fall back from TLS 1.0 to SSL 3.0 for connection
   1.988 +      // resets, because connection resets have too many false positives,
   1.989 +      // and we want to maximize how often we send TLS 1.0+ with extensions
   1.990 +      // if at all reasonable. Unfortunately, it appears we have to allow
   1.991 +      // fallback from TLS 1.2 and TLS 1.1 for connection resets due to bad
   1.992 +      // servers and possibly bad intermediaries.
   1.993 +    conditional:
   1.994 +      if ((err == PR_CONNECT_RESET_ERROR &&
   1.995 +           range.max <= SSL_LIBRARY_VERSION_TLS_1_0) ||
   1.996 +          socketInfo->GetForSTARTTLS()) {
   1.997 +        return false;
   1.998 +      }
   1.999 +      break;
  1.1000 +
  1.1001 +    default:
  1.1002 +      return false;
  1.1003 +  }
  1.1004 +
  1.1005 +  Telemetry::ID pre;
  1.1006 +  Telemetry::ID post;
  1.1007 +  switch (range.max) {
  1.1008 +    case SSL_LIBRARY_VERSION_TLS_1_2:
  1.1009 +      pre = Telemetry::SSL_TLS12_INTOLERANCE_REASON_PRE;
  1.1010 +      post = Telemetry::SSL_TLS12_INTOLERANCE_REASON_POST;
  1.1011 +      break;
  1.1012 +    case SSL_LIBRARY_VERSION_TLS_1_1:
  1.1013 +      pre = Telemetry::SSL_TLS11_INTOLERANCE_REASON_PRE;
  1.1014 +      post = Telemetry::SSL_TLS11_INTOLERANCE_REASON_POST;
  1.1015 +      break;
  1.1016 +    case SSL_LIBRARY_VERSION_TLS_1_0:
  1.1017 +      pre = Telemetry::SSL_TLS10_INTOLERANCE_REASON_PRE;
  1.1018 +      post = Telemetry::SSL_TLS10_INTOLERANCE_REASON_POST;
  1.1019 +      break;
  1.1020 +    case SSL_LIBRARY_VERSION_3_0:
  1.1021 +      pre = Telemetry::SSL_SSL30_INTOLERANCE_REASON_PRE;
  1.1022 +      post = Telemetry::SSL_SSL30_INTOLERANCE_REASON_POST;
  1.1023 +      break;
  1.1024 +    default:
  1.1025 +      MOZ_CRASH("impossible TLS version");
  1.1026 +      return false;
  1.1027 +  }
  1.1028 +
  1.1029 +  // The difference between _PRE and _POST represents how often we avoided
  1.1030 +  // TLS intolerance fallback due to remembered tolerance.
  1.1031 +  Telemetry::Accumulate(pre, reason);
  1.1032 +
  1.1033 +  if (!socketInfo->SharedState().IOLayerHelpers()
  1.1034 +                 .rememberIntolerantAtVersion(socketInfo->GetHostName(),
  1.1035 +                                              socketInfo->GetPort(),
  1.1036 +                                              range.min, range.max)) {
  1.1037 +    return false;
  1.1038 +  }
  1.1039 +
  1.1040 +  Telemetry::Accumulate(post, reason);
  1.1041 +
  1.1042 +  return true;
  1.1043 +}
  1.1044 +
  1.1045 +int32_t
  1.1046 +checkHandshake(int32_t bytesTransfered, bool wasReading,
  1.1047 +               PRFileDesc* ssl_layer_fd, nsNSSSocketInfo* socketInfo)
  1.1048 +{
  1.1049 +  const PRErrorCode originalError = PR_GetError();
  1.1050 +  PRErrorCode err = originalError;
  1.1051 +
  1.1052 +  // This is where we work around all of those SSL servers that don't
  1.1053 +  // conform to the SSL spec and shutdown a connection when we request
  1.1054 +  // SSL v3.1 (aka TLS).  The spec says the client says what version
  1.1055 +  // of the protocol we're willing to perform, in our case SSL v3.1
  1.1056 +  // In its response, the server says which version it wants to perform.
  1.1057 +  // Many servers out there only know how to do v3.0.  Next, we're supposed
  1.1058 +  // to send back the version of the protocol we requested (ie v3.1).  At
  1.1059 +  // this point many servers's implementations are broken and they shut
  1.1060 +  // down the connection when they don't see the version they sent back.
  1.1061 +  // This is supposed to prevent a man in the middle from forcing one
  1.1062 +  // side to dumb down to a lower level of the protocol.  Unfortunately,
  1.1063 +  // there are enough broken servers out there that such a gross work-around
  1.1064 +  // is necessary.  :(
  1.1065 +
  1.1066 +  // Do NOT assume TLS intolerance on a closed connection after bad cert ui was shown.
  1.1067 +  // Simply retry.
  1.1068 +  // This depends on the fact that Cert UI will not be shown again,
  1.1069 +  // should the user override the bad cert.
  1.1070 +
  1.1071 +  bool handleHandshakeResultNow = socketInfo->IsHandshakePending();
  1.1072 +
  1.1073 +  bool wantRetry = false;
  1.1074 +
  1.1075 +  if (0 > bytesTransfered) {
  1.1076 +    if (handleHandshakeResultNow) {
  1.1077 +      if (PR_WOULD_BLOCK_ERROR == err) {
  1.1078 +        PR_SetError(err, 0);
  1.1079 +        return bytesTransfered;
  1.1080 +      }
  1.1081 +
  1.1082 +      wantRetry = retryDueToTLSIntolerance(err, socketInfo);
  1.1083 +    }
  1.1084 +
  1.1085 +    // This is the common place where we trigger non-cert-errors on a SSL
  1.1086 +    // socket. This might be reached at any time of the connection.
  1.1087 +    //
  1.1088 +    // The socketInfo->GetErrorCode() check is here to ensure we don't try to
  1.1089 +    // do the synchronous dispatch to the main thread unnecessarily after we've
  1.1090 +    // already handled a certificate error. (SSLErrorRunnable calls
  1.1091 +    // nsHandleSSLError, which has logic to avoid replacing the error message,
  1.1092 +    // so without the !socketInfo->GetErrorCode(), it would just be an
  1.1093 +    // expensive no-op.)
  1.1094 +    if (!wantRetry && (IS_SSL_ERROR(err) || IS_SEC_ERROR(err)) &&
  1.1095 +        !socketInfo->GetErrorCode()) {
  1.1096 +      RefPtr<SyncRunnableBase> runnable(new SSLErrorRunnable(socketInfo,
  1.1097 +                                                             PlainErrorMessage,
  1.1098 +                                                             err));
  1.1099 +      (void) runnable->DispatchToMainThreadAndWait();
  1.1100 +    }
  1.1101 +  } else if (wasReading && 0 == bytesTransfered) {
  1.1102 +    // zero bytes on reading, socket closed
  1.1103 +    if (handleHandshakeResultNow) {
  1.1104 +      wantRetry = retryDueToTLSIntolerance(PR_END_OF_FILE_ERROR, socketInfo);
  1.1105 +    }
  1.1106 +  }
  1.1107 +
  1.1108 +  if (wantRetry) {
  1.1109 +    PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
  1.1110 +           ("[%p] checkHandshake: will retry with lower max TLS version\n",
  1.1111 +            ssl_layer_fd));
  1.1112 +    // We want to cause the network layer to retry the connection.
  1.1113 +    err = PR_CONNECT_RESET_ERROR;
  1.1114 +    if (wasReading)
  1.1115 +      bytesTransfered = -1;
  1.1116 +  }
  1.1117 +
  1.1118 +  // TLS intolerant servers only cause the first transfer to fail, so let's
  1.1119 +  // set the HandshakePending attribute to false so that we don't try the logic
  1.1120 +  // above again in a subsequent transfer.
  1.1121 +  if (handleHandshakeResultNow) {
  1.1122 +    socketInfo->SetHandshakeNotPending();
  1.1123 +  }
  1.1124 +
  1.1125 +  if (bytesTransfered < 0) {
  1.1126 +    // Remember that we encountered an error so that getSocketInfoIfRunning
  1.1127 +    // will correctly cause us to fail if another part of Gecko
  1.1128 +    // (erroneously) calls an I/O function (PR_Send/PR_Recv/etc.) again on
  1.1129 +    // this socket. Note that we use the original error because if we use
  1.1130 +    // PR_CONNECT_RESET_ERROR, we'll repeated try to reconnect.
  1.1131 +    if (originalError != PR_WOULD_BLOCK_ERROR && !socketInfo->GetErrorCode()) {
  1.1132 +      socketInfo->SetCanceled(originalError, PlainErrorMessage);
  1.1133 +    }
  1.1134 +    PR_SetError(err, 0);
  1.1135 +  }
  1.1136 +
  1.1137 +  return bytesTransfered;
  1.1138 +}
  1.1139 +
  1.1140 +}
  1.1141 +
  1.1142 +static int16_t
  1.1143 +nsSSLIOLayerPoll(PRFileDesc* fd, int16_t in_flags, int16_t* out_flags)
  1.1144 +{
  1.1145 +  nsNSSShutDownPreventionLock locker;
  1.1146 +
  1.1147 +  if (!out_flags) {
  1.1148 +    NS_WARNING("nsSSLIOLayerPoll called with null out_flags");
  1.1149 +    return 0;
  1.1150 +  }
  1.1151 +
  1.1152 +  *out_flags = 0;
  1.1153 +
  1.1154 +  nsNSSSocketInfo* socketInfo =
  1.1155 +    getSocketInfoIfRunning(fd, not_reading_or_writing, locker);
  1.1156 +
  1.1157 +  if (!socketInfo) {
  1.1158 +    // If we get here, it is probably because certificate validation failed
  1.1159 +    // and this is the first I/O operation after the failure.
  1.1160 +    PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
  1.1161 +            ("[%p] polling SSL socket right after certificate verification failed "
  1.1162 +                  "or NSS shutdown or SDR logout %d\n",
  1.1163 +             fd, (int) in_flags));
  1.1164 +
  1.1165 +    NS_ASSERTION(in_flags & PR_POLL_EXCEPT,
  1.1166 +                 "caller did not poll for EXCEPT (canceled)");
  1.1167 +    // Since this poll method cannot return errors, we want the caller to call
  1.1168 +    // PR_Send/PR_Recv right away to get the error, so we tell that we are
  1.1169 +    // ready for whatever I/O they are asking for. (See getSocketInfoIfRunning).
  1.1170 +    *out_flags = in_flags | PR_POLL_EXCEPT; // see also bug 480619
  1.1171 +    return in_flags;
  1.1172 +  }
  1.1173 +
  1.1174 +  PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
  1.1175 +         (socketInfo->IsWaitingForCertVerification()
  1.1176 +            ?  "[%p] polling SSL socket during certificate verification using lower %d\n"
  1.1177 +            :  "[%p] poll SSL socket using lower %d\n",
  1.1178 +         fd, (int) in_flags));
  1.1179 +
  1.1180 +  // We want the handshake to continue during certificate validation, so we
  1.1181 +  // don't need to do anything special here. libssl automatically blocks when
  1.1182 +  // it reaches any point that would be unsafe to send/receive something before
  1.1183 +  // cert validation is complete.
  1.1184 +  int16_t result = fd->lower->methods->poll(fd->lower, in_flags, out_flags);
  1.1185 +  PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p] poll SSL socket returned %d\n",
  1.1186 +                                    (void*) fd, (int) result));
  1.1187 +  return result;
  1.1188 +}
  1.1189 +
  1.1190 +nsSSLIOLayerHelpers::nsSSLIOLayerHelpers()
  1.1191 +  : mRenegoUnrestrictedSites(nullptr)
  1.1192 +  , mTreatUnsafeNegotiationAsBroken(false)
  1.1193 +  , mWarnLevelMissingRFC5746(1)
  1.1194 +  , mTLSIntoleranceInfo(16)
  1.1195 +  , mFalseStartRequireNPN(true)
  1.1196 +  , mFalseStartRequireForwardSecrecy(false)
  1.1197 +  , mutex("nsSSLIOLayerHelpers.mutex")
  1.1198 +{
  1.1199 +}
  1.1200 +
  1.1201 +static int
  1.1202 +_PSM_InvalidInt(void)
  1.1203 +{
  1.1204 +    PR_ASSERT(!"I/O method is invalid");
  1.1205 +    PR_SetError(PR_INVALID_METHOD_ERROR, 0);
  1.1206 +    return -1;
  1.1207 +}
  1.1208 +
  1.1209 +static int64_t
  1.1210 +_PSM_InvalidInt64(void)
  1.1211 +{
  1.1212 +    PR_ASSERT(!"I/O method is invalid");
  1.1213 +    PR_SetError(PR_INVALID_METHOD_ERROR, 0);
  1.1214 +    return -1;
  1.1215 +}
  1.1216 +
  1.1217 +static PRStatus
  1.1218 +_PSM_InvalidStatus(void)
  1.1219 +{
  1.1220 +    PR_ASSERT(!"I/O method is invalid");
  1.1221 +    PR_SetError(PR_INVALID_METHOD_ERROR, 0);
  1.1222 +    return PR_FAILURE;
  1.1223 +}
  1.1224 +
  1.1225 +static PRFileDesc*
  1.1226 +_PSM_InvalidDesc(void)
  1.1227 +{
  1.1228 +    PR_ASSERT(!"I/O method is invalid");
  1.1229 +    PR_SetError(PR_INVALID_METHOD_ERROR, 0);
  1.1230 +    return nullptr;
  1.1231 +}
  1.1232 +
  1.1233 +static PRStatus
  1.1234 +PSMGetsockname(PRFileDesc* fd, PRNetAddr* addr)
  1.1235 +{
  1.1236 +  nsNSSShutDownPreventionLock locker;
  1.1237 +  if (!getSocketInfoIfRunning(fd, not_reading_or_writing, locker))
  1.1238 +    return PR_FAILURE;
  1.1239 +
  1.1240 +  return fd->lower->methods->getsockname(fd->lower, addr);
  1.1241 +}
  1.1242 +
  1.1243 +static PRStatus
  1.1244 +PSMGetpeername(PRFileDesc* fd, PRNetAddr* addr)
  1.1245 +{
  1.1246 +  nsNSSShutDownPreventionLock locker;
  1.1247 +  if (!getSocketInfoIfRunning(fd, not_reading_or_writing, locker))
  1.1248 +    return PR_FAILURE;
  1.1249 +
  1.1250 +  return fd->lower->methods->getpeername(fd->lower, addr);
  1.1251 +}
  1.1252 +
  1.1253 +static PRStatus
  1.1254 +PSMGetsocketoption(PRFileDesc* fd, PRSocketOptionData* data)
  1.1255 +{
  1.1256 +  nsNSSShutDownPreventionLock locker;
  1.1257 +  if (!getSocketInfoIfRunning(fd, not_reading_or_writing, locker))
  1.1258 +    return PR_FAILURE;
  1.1259 +
  1.1260 +  return fd->lower->methods->getsocketoption(fd, data);
  1.1261 +}
  1.1262 +
  1.1263 +static PRStatus
  1.1264 +PSMSetsocketoption(PRFileDesc* fd, const PRSocketOptionData* data)
  1.1265 +{
  1.1266 +  nsNSSShutDownPreventionLock locker;
  1.1267 +  if (!getSocketInfoIfRunning(fd, not_reading_or_writing, locker))
  1.1268 +    return PR_FAILURE;
  1.1269 +
  1.1270 +  return fd->lower->methods->setsocketoption(fd, data);
  1.1271 +}
  1.1272 +
  1.1273 +static int32_t
  1.1274 +PSMRecv(PRFileDesc* fd, void* buf, int32_t amount, int flags,
  1.1275 +        PRIntervalTime timeout)
  1.1276 +{
  1.1277 +  nsNSSShutDownPreventionLock locker;
  1.1278 +  nsNSSSocketInfo* socketInfo = getSocketInfoIfRunning(fd, reading, locker);
  1.1279 +  if (!socketInfo)
  1.1280 +    return -1;
  1.1281 +
  1.1282 +  if (flags != PR_MSG_PEEK && flags != 0) {
  1.1283 +    PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
  1.1284 +    return -1;
  1.1285 +  }
  1.1286 +
  1.1287 +  int32_t bytesRead = fd->lower->methods->recv(fd->lower, buf, amount, flags,
  1.1288 +                                               timeout);
  1.1289 +
  1.1290 +  PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p] read %d bytes\n", (void*) fd,
  1.1291 +         bytesRead));
  1.1292 +
  1.1293 +#ifdef DEBUG_SSL_VERBOSE
  1.1294 +  DEBUG_DUMP_BUFFER((unsigned char*) buf, bytesRead);
  1.1295 +#endif
  1.1296 +
  1.1297 +  return checkHandshake(bytesRead, true, fd, socketInfo);
  1.1298 +}
  1.1299 +
  1.1300 +static int32_t
  1.1301 +PSMSend(PRFileDesc* fd, const void* buf, int32_t amount, int flags,
  1.1302 +        PRIntervalTime timeout)
  1.1303 +{
  1.1304 +  nsNSSShutDownPreventionLock locker;
  1.1305 +  nsNSSSocketInfo* socketInfo = getSocketInfoIfRunning(fd, writing, locker);
  1.1306 +  if (!socketInfo)
  1.1307 +    return -1;
  1.1308 +
  1.1309 +  if (flags != 0) {
  1.1310 +    PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
  1.1311 +    return -1;
  1.1312 +  }
  1.1313 +
  1.1314 +#ifdef DEBUG_SSL_VERBOSE
  1.1315 +  DEBUG_DUMP_BUFFER((unsigned char*) buf, amount);
  1.1316 +#endif
  1.1317 +
  1.1318 +  int32_t bytesWritten = fd->lower->methods->send(fd->lower, buf, amount,
  1.1319 +                                                  flags, timeout);
  1.1320 +
  1.1321 +  PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p] wrote %d bytes\n",
  1.1322 +         fd, bytesWritten));
  1.1323 +
  1.1324 +  return checkHandshake(bytesWritten, false, fd, socketInfo);
  1.1325 +}
  1.1326 +
  1.1327 +static int32_t
  1.1328 +nsSSLIOLayerRead(PRFileDesc* fd, void* buf, int32_t amount)
  1.1329 +{
  1.1330 +  return PSMRecv(fd, buf, amount, 0, PR_INTERVAL_NO_TIMEOUT);
  1.1331 +}
  1.1332 +
  1.1333 +static int32_t
  1.1334 +nsSSLIOLayerWrite(PRFileDesc* fd, const void* buf, int32_t amount)
  1.1335 +{
  1.1336 +  return PSMSend(fd, buf, amount, 0, PR_INTERVAL_NO_TIMEOUT);
  1.1337 +}
  1.1338 +
  1.1339 +static PRStatus
  1.1340 +PSMConnectcontinue(PRFileDesc* fd, int16_t out_flags)
  1.1341 +{
  1.1342 +  nsNSSShutDownPreventionLock locker;
  1.1343 +  if (!getSocketInfoIfRunning(fd, not_reading_or_writing, locker)) {
  1.1344 +    return PR_FAILURE;
  1.1345 +  }
  1.1346 +
  1.1347 +  return fd->lower->methods->connectcontinue(fd, out_flags);
  1.1348 +}
  1.1349 +
  1.1350 +static int
  1.1351 +PSMAvailable(void)
  1.1352 +{
  1.1353 +  // This is called through PR_Available(), but is not implemented in PSM
  1.1354 +  PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
  1.1355 +  return -1;
  1.1356 +}
  1.1357 +
  1.1358 +static int64_t
  1.1359 +PSMAvailable64(void)
  1.1360 +{
  1.1361 +  // This is called through PR_Available(), but is not implemented in PSM
  1.1362 +  PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
  1.1363 +  return -1;
  1.1364 +}
  1.1365 +
  1.1366 +namespace {
  1.1367 +
  1.1368 +class PrefObserver : public nsIObserver {
  1.1369 +public:
  1.1370 +  NS_DECL_THREADSAFE_ISUPPORTS
  1.1371 +  NS_DECL_NSIOBSERVER
  1.1372 +  PrefObserver(nsSSLIOLayerHelpers* aOwner) : mOwner(aOwner) {}
  1.1373 +  virtual ~PrefObserver() {}
  1.1374 +private:
  1.1375 +  nsSSLIOLayerHelpers* mOwner;
  1.1376 +};
  1.1377 +
  1.1378 +} // unnamed namespace
  1.1379 +
  1.1380 +NS_IMPL_ISUPPORTS(PrefObserver, nsIObserver)
  1.1381 +
  1.1382 +NS_IMETHODIMP
  1.1383 +PrefObserver::Observe(nsISupports* aSubject, const char* aTopic,
  1.1384 +                      const char16_t* someData)
  1.1385 +{
  1.1386 +  if (nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0) {
  1.1387 +    NS_ConvertUTF16toUTF8 prefName(someData);
  1.1388 +
  1.1389 +    if (prefName.Equals("security.ssl.renego_unrestricted_hosts")) {
  1.1390 +      nsCString unrestricted_hosts;
  1.1391 +      Preferences::GetCString("security.ssl.renego_unrestricted_hosts", &unrestricted_hosts);
  1.1392 +      if (!unrestricted_hosts.IsEmpty()) {
  1.1393 +        mOwner->setRenegoUnrestrictedSites(unrestricted_hosts);
  1.1394 +      }
  1.1395 +    } else if (prefName.Equals("security.ssl.treat_unsafe_negotiation_as_broken")) {
  1.1396 +      bool enabled;
  1.1397 +      Preferences::GetBool("security.ssl.treat_unsafe_negotiation_as_broken", &enabled);
  1.1398 +      mOwner->setTreatUnsafeNegotiationAsBroken(enabled);
  1.1399 +    } else if (prefName.Equals("security.ssl.warn_missing_rfc5746")) {
  1.1400 +      int32_t warnLevel = 1;
  1.1401 +      Preferences::GetInt("security.ssl.warn_missing_rfc5746", &warnLevel);
  1.1402 +      mOwner->setWarnLevelMissingRFC5746(warnLevel);
  1.1403 +    } else if (prefName.Equals("security.ssl.false_start.require-npn")) {
  1.1404 +      mOwner->mFalseStartRequireNPN =
  1.1405 +        Preferences::GetBool("security.ssl.false_start.require-npn",
  1.1406 +                             FALSE_START_REQUIRE_NPN_DEFAULT);
  1.1407 +    } else if (prefName.Equals("security.ssl.false_start.require-forward-secrecy")) {
  1.1408 +      mOwner->mFalseStartRequireForwardSecrecy =
  1.1409 +        Preferences::GetBool("security.ssl.false_start.require-forward-secrecy",
  1.1410 +                             FALSE_START_REQUIRE_FORWARD_SECRECY_DEFAULT);
  1.1411 +    }
  1.1412 +  }
  1.1413 +  return NS_OK;
  1.1414 +}
  1.1415 +
  1.1416 +static int32_t
  1.1417 +PlaintextRecv(PRFileDesc* fd, void* buf, int32_t amount, int flags,
  1.1418 +              PRIntervalTime timeout)
  1.1419 +{
  1.1420 +  // The shutdownlocker is not needed here because it will already be
  1.1421 +  // held higher in the stack
  1.1422 +  nsNSSSocketInfo* socketInfo = nullptr;
  1.1423 +
  1.1424 +  int32_t bytesRead = fd->lower->methods->recv(fd->lower, buf, amount, flags,
  1.1425 +                                               timeout);
  1.1426 +  if (fd->identity == nsSSLIOLayerHelpers::nsSSLPlaintextLayerIdentity)
  1.1427 +    socketInfo = (nsNSSSocketInfo*) fd->secret;
  1.1428 +
  1.1429 +  if ((bytesRead > 0) && socketInfo)
  1.1430 +    socketInfo->AddPlaintextBytesRead(bytesRead);
  1.1431 +  return bytesRead;
  1.1432 +}
  1.1433 +
  1.1434 +nsSSLIOLayerHelpers::~nsSSLIOLayerHelpers()
  1.1435 +{
  1.1436 +  // mPrefObserver will only be set if this->Init was called. The GTest tests
  1.1437 +  // do not call Init.
  1.1438 +  if (mPrefObserver) {
  1.1439 +    Preferences::RemoveObserver(mPrefObserver,
  1.1440 +      "security.ssl.renego_unrestricted_hosts");
  1.1441 +    Preferences::RemoveObserver(mPrefObserver,
  1.1442 +        "security.ssl.treat_unsafe_negotiation_as_broken");
  1.1443 +    Preferences::RemoveObserver(mPrefObserver,
  1.1444 +        "security.ssl.warn_missing_rfc5746");
  1.1445 +    Preferences::RemoveObserver(mPrefObserver,
  1.1446 +        "security.ssl.false_start.require-npn");
  1.1447 +    Preferences::RemoveObserver(mPrefObserver,
  1.1448 +        "security.ssl.false_start.require-forward-secrecy");
  1.1449 +  }
  1.1450 +}
  1.1451 +
  1.1452 +nsresult
  1.1453 +nsSSLIOLayerHelpers::Init()
  1.1454 +{
  1.1455 +  if (!nsSSLIOLayerInitialized) {
  1.1456 +    nsSSLIOLayerInitialized = true;
  1.1457 +    nsSSLIOLayerIdentity = PR_GetUniqueIdentity("NSS layer");
  1.1458 +    nsSSLIOLayerMethods  = *PR_GetDefaultIOMethods();
  1.1459 +
  1.1460 +    nsSSLIOLayerMethods.available = (PRAvailableFN) PSMAvailable;
  1.1461 +    nsSSLIOLayerMethods.available64 = (PRAvailable64FN) PSMAvailable64;
  1.1462 +    nsSSLIOLayerMethods.fsync = (PRFsyncFN) _PSM_InvalidStatus;
  1.1463 +    nsSSLIOLayerMethods.seek = (PRSeekFN) _PSM_InvalidInt;
  1.1464 +    nsSSLIOLayerMethods.seek64 = (PRSeek64FN) _PSM_InvalidInt64;
  1.1465 +    nsSSLIOLayerMethods.fileInfo = (PRFileInfoFN) _PSM_InvalidStatus;
  1.1466 +    nsSSLIOLayerMethods.fileInfo64 = (PRFileInfo64FN) _PSM_InvalidStatus;
  1.1467 +    nsSSLIOLayerMethods.writev = (PRWritevFN) _PSM_InvalidInt;
  1.1468 +    nsSSLIOLayerMethods.accept = (PRAcceptFN) _PSM_InvalidDesc;
  1.1469 +    nsSSLIOLayerMethods.bind = (PRBindFN) _PSM_InvalidStatus;
  1.1470 +    nsSSLIOLayerMethods.listen = (PRListenFN) _PSM_InvalidStatus;
  1.1471 +    nsSSLIOLayerMethods.shutdown = (PRShutdownFN) _PSM_InvalidStatus;
  1.1472 +    nsSSLIOLayerMethods.recvfrom = (PRRecvfromFN) _PSM_InvalidInt;
  1.1473 +    nsSSLIOLayerMethods.sendto = (PRSendtoFN) _PSM_InvalidInt;
  1.1474 +    nsSSLIOLayerMethods.acceptread = (PRAcceptreadFN) _PSM_InvalidInt;
  1.1475 +    nsSSLIOLayerMethods.transmitfile = (PRTransmitfileFN) _PSM_InvalidInt;
  1.1476 +    nsSSLIOLayerMethods.sendfile = (PRSendfileFN) _PSM_InvalidInt;
  1.1477 +
  1.1478 +    nsSSLIOLayerMethods.getsockname = PSMGetsockname;
  1.1479 +    nsSSLIOLayerMethods.getpeername = PSMGetpeername;
  1.1480 +    nsSSLIOLayerMethods.getsocketoption = PSMGetsocketoption;
  1.1481 +    nsSSLIOLayerMethods.setsocketoption = PSMSetsocketoption;
  1.1482 +    nsSSLIOLayerMethods.recv = PSMRecv;
  1.1483 +    nsSSLIOLayerMethods.send = PSMSend;
  1.1484 +    nsSSLIOLayerMethods.connectcontinue = PSMConnectcontinue;
  1.1485 +
  1.1486 +    nsSSLIOLayerMethods.connect = nsSSLIOLayerConnect;
  1.1487 +    nsSSLIOLayerMethods.close = nsSSLIOLayerClose;
  1.1488 +    nsSSLIOLayerMethods.write = nsSSLIOLayerWrite;
  1.1489 +    nsSSLIOLayerMethods.read = nsSSLIOLayerRead;
  1.1490 +    nsSSLIOLayerMethods.poll = nsSSLIOLayerPoll;
  1.1491 +
  1.1492 +    nsSSLPlaintextLayerIdentity = PR_GetUniqueIdentity("Plaintxext PSM layer");
  1.1493 +    nsSSLPlaintextLayerMethods  = *PR_GetDefaultIOMethods();
  1.1494 +    nsSSLPlaintextLayerMethods.recv = PlaintextRecv;
  1.1495 +  }
  1.1496 +
  1.1497 +  mRenegoUnrestrictedSites = new nsTHashtable<nsCStringHashKey>(16);
  1.1498 +
  1.1499 +  nsCString unrestricted_hosts;
  1.1500 +  Preferences::GetCString("security.ssl.renego_unrestricted_hosts", &unrestricted_hosts);
  1.1501 +  if (!unrestricted_hosts.IsEmpty()) {
  1.1502 +    setRenegoUnrestrictedSites(unrestricted_hosts);
  1.1503 +  }
  1.1504 +
  1.1505 +  bool enabled = false;
  1.1506 +  Preferences::GetBool("security.ssl.treat_unsafe_negotiation_as_broken", &enabled);
  1.1507 +  setTreatUnsafeNegotiationAsBroken(enabled);
  1.1508 +
  1.1509 +  int32_t warnLevel = 1;
  1.1510 +  Preferences::GetInt("security.ssl.warn_missing_rfc5746", &warnLevel);
  1.1511 +  setWarnLevelMissingRFC5746(warnLevel);
  1.1512 +
  1.1513 +  mFalseStartRequireNPN =
  1.1514 +    Preferences::GetBool("security.ssl.false_start.require-npn",
  1.1515 +                         FALSE_START_REQUIRE_NPN_DEFAULT);
  1.1516 +  mFalseStartRequireForwardSecrecy =
  1.1517 +    Preferences::GetBool("security.ssl.false_start.require-forward-secrecy",
  1.1518 +                         FALSE_START_REQUIRE_FORWARD_SECRECY_DEFAULT);
  1.1519 +
  1.1520 +  mPrefObserver = new PrefObserver(this);
  1.1521 +  Preferences::AddStrongObserver(mPrefObserver,
  1.1522 +                                 "security.ssl.renego_unrestricted_hosts");
  1.1523 +  Preferences::AddStrongObserver(mPrefObserver,
  1.1524 +                                 "security.ssl.treat_unsafe_negotiation_as_broken");
  1.1525 +  Preferences::AddStrongObserver(mPrefObserver,
  1.1526 +                                 "security.ssl.warn_missing_rfc5746");
  1.1527 +  Preferences::AddStrongObserver(mPrefObserver,
  1.1528 +                                 "security.ssl.false_start.require-npn");
  1.1529 +  Preferences::AddStrongObserver(mPrefObserver,
  1.1530 +                                 "security.ssl.false_start.require-forward-secrecy");
  1.1531 +  return NS_OK;
  1.1532 +}
  1.1533 +
  1.1534 +void
  1.1535 +nsSSLIOLayerHelpers::clearStoredData()
  1.1536 +{
  1.1537 +  mRenegoUnrestrictedSites->Clear();
  1.1538 +  mTLSIntoleranceInfo.Clear();
  1.1539 +}
  1.1540 +
  1.1541 +void
  1.1542 +nsSSLIOLayerHelpers::setRenegoUnrestrictedSites(const nsCString& str)
  1.1543 +{
  1.1544 +  MutexAutoLock lock(mutex);
  1.1545 +
  1.1546 +  if (mRenegoUnrestrictedSites) {
  1.1547 +    delete mRenegoUnrestrictedSites;
  1.1548 +    mRenegoUnrestrictedSites = nullptr;
  1.1549 +  }
  1.1550 +
  1.1551 +  mRenegoUnrestrictedSites = new nsTHashtable<nsCStringHashKey>();
  1.1552 +  if (!mRenegoUnrestrictedSites)
  1.1553 +    return;
  1.1554 +
  1.1555 +  nsCCharSeparatedTokenizer toker(str, ',');
  1.1556 +
  1.1557 +  while (toker.hasMoreTokens()) {
  1.1558 +    const nsCSubstring& host = toker.nextToken();
  1.1559 +    if (!host.IsEmpty()) {
  1.1560 +      mRenegoUnrestrictedSites->PutEntry(host);
  1.1561 +    }
  1.1562 +  }
  1.1563 +}
  1.1564 +
  1.1565 +bool
  1.1566 +nsSSLIOLayerHelpers::isRenegoUnrestrictedSite(const nsCString& str)
  1.1567 +{
  1.1568 +  MutexAutoLock lock(mutex);
  1.1569 +  return mRenegoUnrestrictedSites->Contains(str);
  1.1570 +}
  1.1571 +
  1.1572 +void
  1.1573 +nsSSLIOLayerHelpers::setTreatUnsafeNegotiationAsBroken(bool broken)
  1.1574 +{
  1.1575 +  MutexAutoLock lock(mutex);
  1.1576 +  mTreatUnsafeNegotiationAsBroken = broken;
  1.1577 +}
  1.1578 +
  1.1579 +bool
  1.1580 +nsSSLIOLayerHelpers::treatUnsafeNegotiationAsBroken()
  1.1581 +{
  1.1582 +  MutexAutoLock lock(mutex);
  1.1583 +  return mTreatUnsafeNegotiationAsBroken;
  1.1584 +}
  1.1585 +
  1.1586 +void
  1.1587 +nsSSLIOLayerHelpers::setWarnLevelMissingRFC5746(int32_t level)
  1.1588 +{
  1.1589 +  MutexAutoLock lock(mutex);
  1.1590 +  mWarnLevelMissingRFC5746 = level;
  1.1591 +}
  1.1592 +
  1.1593 +int32_t
  1.1594 +nsSSLIOLayerHelpers::getWarnLevelMissingRFC5746()
  1.1595 +{
  1.1596 +  MutexAutoLock lock(mutex);
  1.1597 +  return mWarnLevelMissingRFC5746;
  1.1598 +}
  1.1599 +
  1.1600 +nsresult
  1.1601 +nsSSLIOLayerNewSocket(int32_t family,
  1.1602 +                      const char* host,
  1.1603 +                      int32_t port,
  1.1604 +                      nsIProxyInfo *proxy,
  1.1605 +                      PRFileDesc** fd,
  1.1606 +                      nsISupports** info,
  1.1607 +                      bool forSTARTTLS,
  1.1608 +                      uint32_t flags)
  1.1609 +{
  1.1610 +
  1.1611 +  PRFileDesc* sock = PR_OpenTCPSocket(family);
  1.1612 +  if (!sock) return NS_ERROR_OUT_OF_MEMORY;
  1.1613 +
  1.1614 +  nsresult rv = nsSSLIOLayerAddToSocket(family, host, port, proxy,
  1.1615 +                                        sock, info, forSTARTTLS, flags);
  1.1616 +  if (NS_FAILED(rv)) {
  1.1617 +    PR_Close(sock);
  1.1618 +    return rv;
  1.1619 +  }
  1.1620 +
  1.1621 +  *fd = sock;
  1.1622 +  return NS_OK;
  1.1623 +}
  1.1624 +
  1.1625 +// Creates CA names strings from (CERTDistNames* caNames)
  1.1626 +//
  1.1627 +// - arena: arena to allocate strings on
  1.1628 +// - caNameStrings: filled with CA names strings on return
  1.1629 +// - caNames: CERTDistNames to extract strings from
  1.1630 +// - return: SECSuccess if successful; error code otherwise
  1.1631 +//
  1.1632 +// Note: copied in its entirety from Nova code
  1.1633 +static SECStatus
  1.1634 +nsConvertCANamesToStrings(PLArenaPool* arena, char** caNameStrings,
  1.1635 +                          CERTDistNames* caNames)
  1.1636 +{
  1.1637 +    SECItem* dername;
  1.1638 +    SECStatus rv;
  1.1639 +    int headerlen;
  1.1640 +    uint32_t contentlen;
  1.1641 +    SECItem newitem;
  1.1642 +    int n;
  1.1643 +    char* namestring;
  1.1644 +
  1.1645 +    for (n = 0; n < caNames->nnames; n++) {
  1.1646 +        newitem.data = nullptr;
  1.1647 +        dername = &caNames->names[n];
  1.1648 +
  1.1649 +        rv = DER_Lengths(dername, &headerlen, &contentlen);
  1.1650 +
  1.1651 +        if (rv != SECSuccess) {
  1.1652 +            goto loser;
  1.1653 +        }
  1.1654 +
  1.1655 +        if (headerlen + contentlen != dername->len) {
  1.1656 +            // This must be from an enterprise 2.x server, which sent
  1.1657 +            // incorrectly formatted der without the outer wrapper of type and
  1.1658 +            // length. Fix it up by adding the top level header.
  1.1659 +            if (dername->len <= 127) {
  1.1660 +                newitem.data = (unsigned char*) PR_Malloc(dername->len + 2);
  1.1661 +                if (!newitem.data) {
  1.1662 +                    goto loser;
  1.1663 +                }
  1.1664 +                newitem.data[0] = (unsigned char) 0x30;
  1.1665 +                newitem.data[1] = (unsigned char) dername->len;
  1.1666 +                (void) memcpy(&newitem.data[2], dername->data, dername->len);
  1.1667 +            } else if (dername->len <= 255) {
  1.1668 +                newitem.data = (unsigned char*) PR_Malloc(dername->len + 3);
  1.1669 +                if (!newitem.data) {
  1.1670 +                    goto loser;
  1.1671 +                }
  1.1672 +                newitem.data[0] = (unsigned char) 0x30;
  1.1673 +                newitem.data[1] = (unsigned char) 0x81;
  1.1674 +                newitem.data[2] = (unsigned char) dername->len;
  1.1675 +                (void) memcpy(&newitem.data[3], dername->data, dername->len);
  1.1676 +            } else {
  1.1677 +                // greater than 256, better be less than 64k
  1.1678 +                newitem.data = (unsigned char*) PR_Malloc(dername->len + 4);
  1.1679 +                if (!newitem.data) {
  1.1680 +                    goto loser;
  1.1681 +                }
  1.1682 +                newitem.data[0] = (unsigned char) 0x30;
  1.1683 +                newitem.data[1] = (unsigned char) 0x82;
  1.1684 +                newitem.data[2] = (unsigned char) ((dername->len >> 8) & 0xff);
  1.1685 +                newitem.data[3] = (unsigned char) (dername->len & 0xff);
  1.1686 +                memcpy(&newitem.data[4], dername->data, dername->len);
  1.1687 +            }
  1.1688 +            dername = &newitem;
  1.1689 +        }
  1.1690 +
  1.1691 +        namestring = CERT_DerNameToAscii(dername);
  1.1692 +        if (!namestring) {
  1.1693 +            // XXX - keep going until we fail to convert the name
  1.1694 +            caNameStrings[n] = const_cast<char*>("");
  1.1695 +        } else {
  1.1696 +            caNameStrings[n] = PORT_ArenaStrdup(arena, namestring);
  1.1697 +            PR_Free(namestring);
  1.1698 +            if (!caNameStrings[n]) {
  1.1699 +                goto loser;
  1.1700 +            }
  1.1701 +        }
  1.1702 +
  1.1703 +        if (newitem.data) {
  1.1704 +            PR_Free(newitem.data);
  1.1705 +        }
  1.1706 +    }
  1.1707 +
  1.1708 +    return SECSuccess;
  1.1709 +loser:
  1.1710 +    if (newitem.data) {
  1.1711 +        PR_Free(newitem.data);
  1.1712 +    }
  1.1713 +    return SECFailure;
  1.1714 +}
  1.1715 +
  1.1716 +// Sets certChoice by reading the preference
  1.1717 +//
  1.1718 +// If done properly, this function will read the identifier strings for ASK and
  1.1719 +// AUTO modes read the selected strings from the preference, compare the
  1.1720 +// strings, and determine in which mode it is in. We currently use ASK mode for
  1.1721 +// UI apps and AUTO mode for UI-less apps without really asking for
  1.1722 +// preferences.
  1.1723 +nsresult
  1.1724 +nsGetUserCertChoice(SSM_UserCertChoice* certChoice)
  1.1725 +{
  1.1726 +  char* mode = nullptr;
  1.1727 +  nsresult ret;
  1.1728 +
  1.1729 +  NS_ENSURE_ARG_POINTER(certChoice);
  1.1730 +
  1.1731 +  nsCOMPtr<nsIPrefBranch> pref = do_GetService(NS_PREFSERVICE_CONTRACTID);
  1.1732 +
  1.1733 +  ret = pref->GetCharPref("security.default_personal_cert", &mode);
  1.1734 +  if (NS_FAILED(ret)) {
  1.1735 +    goto loser;
  1.1736 +  }
  1.1737 +
  1.1738 +  if (PL_strcmp(mode, "Select Automatically") == 0) {
  1.1739 +    *certChoice = AUTO;
  1.1740 +  } else if (PL_strcmp(mode, "Ask Every Time") == 0) {
  1.1741 +    *certChoice = ASK;
  1.1742 +  } else {
  1.1743 +    // Most likely we see a nickname from a migrated cert.
  1.1744 +    // We do not currently support that, ask the user which cert to use.
  1.1745 +    *certChoice = ASK;
  1.1746 +  }
  1.1747 +
  1.1748 +loser:
  1.1749 +  if (mode) {
  1.1750 +    nsMemory::Free(mode);
  1.1751 +  }
  1.1752 +  return ret;
  1.1753 +}
  1.1754 +
  1.1755 +static bool
  1.1756 +hasExplicitKeyUsageNonRepudiation(CERTCertificate* cert)
  1.1757 +{
  1.1758 +  // There is no extension, v1 or v2 certificate
  1.1759 +  if (!cert->extensions)
  1.1760 +    return false;
  1.1761 +
  1.1762 +  SECStatus srv;
  1.1763 +  SECItem keyUsageItem;
  1.1764 +  keyUsageItem.data = nullptr;
  1.1765 +
  1.1766 +  srv = CERT_FindKeyUsageExtension(cert, &keyUsageItem);
  1.1767 +  if (srv == SECFailure)
  1.1768 +    return false;
  1.1769 +
  1.1770 +  unsigned char keyUsage = keyUsageItem.data[0];
  1.1771 +  PORT_Free (keyUsageItem.data);
  1.1772 +
  1.1773 +  return !!(keyUsage & KU_NON_REPUDIATION);
  1.1774 +}
  1.1775 +
  1.1776 +class ClientAuthDataRunnable : public SyncRunnableBase
  1.1777 +{
  1.1778 +public:
  1.1779 +  ClientAuthDataRunnable(CERTDistNames* caNames,
  1.1780 +                         CERTCertificate** pRetCert,
  1.1781 +                         SECKEYPrivateKey** pRetKey,
  1.1782 +                         nsNSSSocketInfo* info,
  1.1783 +                         CERTCertificate* serverCert)
  1.1784 +    : mRV(SECFailure)
  1.1785 +    , mErrorCodeToReport(SEC_ERROR_NO_MEMORY)
  1.1786 +    , mPRetCert(pRetCert)
  1.1787 +    , mPRetKey(pRetKey)
  1.1788 +    , mCANames(caNames)
  1.1789 +    , mSocketInfo(info)
  1.1790 +    , mServerCert(serverCert)
  1.1791 +  {
  1.1792 +  }
  1.1793 +
  1.1794 +  SECStatus mRV;                        // out
  1.1795 +  PRErrorCode mErrorCodeToReport;       // out
  1.1796 +  CERTCertificate** const mPRetCert;    // in/out
  1.1797 +  SECKEYPrivateKey** const mPRetKey;    // in/out
  1.1798 +protected:
  1.1799 +  virtual void RunOnTargetThread();
  1.1800 +private:
  1.1801 +  CERTDistNames* const mCANames;        // in
  1.1802 +  nsNSSSocketInfo* const mSocketInfo;   // in
  1.1803 +  CERTCertificate* const mServerCert;   // in
  1.1804 +};
  1.1805 +
  1.1806 +// This callback function is used to pull client certificate
  1.1807 +// information upon server request
  1.1808 +//
  1.1809 +// - arg: SSL data connection
  1.1810 +// - socket: SSL socket we're dealing with
  1.1811 +// - caNames: list of CA names
  1.1812 +// - pRetCert: returns a pointer to a pointer to a valid certificate if
  1.1813 +//             successful; otherwise nullptr
  1.1814 +// - pRetKey: returns a pointer to a pointer to the corresponding key if
  1.1815 +//            successful; otherwise nullptr
  1.1816 +SECStatus
  1.1817 +nsNSS_SSLGetClientAuthData(void* arg, PRFileDesc* socket,
  1.1818 +                           CERTDistNames* caNames, CERTCertificate** pRetCert,
  1.1819 +                           SECKEYPrivateKey** pRetKey)
  1.1820 +{
  1.1821 +  nsNSSShutDownPreventionLock locker;
  1.1822 +
  1.1823 +  if (!socket || !caNames || !pRetCert || !pRetKey) {
  1.1824 +    PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
  1.1825 +    return SECFailure;
  1.1826 +  }
  1.1827 +
  1.1828 +  RefPtr<nsNSSSocketInfo> info(
  1.1829 +    reinterpret_cast<nsNSSSocketInfo*>(socket->higher->secret));
  1.1830 +
  1.1831 +  CERTCertificate* serverCert = SSL_PeerCertificate(socket);
  1.1832 +  if (!serverCert) {
  1.1833 +    NS_NOTREACHED("Missing server certificate should have been detected during "
  1.1834 +                  "server cert authentication.");
  1.1835 +    PR_SetError(SSL_ERROR_NO_CERTIFICATE, 0);
  1.1836 +    return SECFailure;
  1.1837 +  }
  1.1838 +
  1.1839 +  if (info->GetJoined()) {
  1.1840 +    // We refuse to send a client certificate when there are multiple hostnames
  1.1841 +    // joined on this connection, because we only show the user one hostname
  1.1842 +    // (mHostName) in the client certificate UI.
  1.1843 +
  1.1844 +    PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
  1.1845 +           ("[%p] Not returning client cert due to previous join\n", socket));
  1.1846 +    *pRetCert = nullptr;
  1.1847 +    *pRetKey = nullptr;
  1.1848 +    return SECSuccess;
  1.1849 +  }
  1.1850 +
  1.1851 +  // XXX: This should be done asynchronously; see bug 696976
  1.1852 +  RefPtr<ClientAuthDataRunnable> runnable(
  1.1853 +    new ClientAuthDataRunnable(caNames, pRetCert, pRetKey, info, serverCert));
  1.1854 +  nsresult rv = runnable->DispatchToMainThreadAndWait();
  1.1855 +  if (NS_FAILED(rv)) {
  1.1856 +    PR_SetError(SEC_ERROR_NO_MEMORY, 0);
  1.1857 +    return SECFailure;
  1.1858 +  }
  1.1859 +
  1.1860 +  if (runnable->mRV != SECSuccess) {
  1.1861 +    PR_SetError(runnable->mErrorCodeToReport, 0);
  1.1862 +  } else if (*runnable->mPRetCert || *runnable->mPRetKey) {
  1.1863 +    // Make joinConnection prohibit joining after we've sent a client cert
  1.1864 +    info->SetSentClientCert();
  1.1865 +  }
  1.1866 +
  1.1867 +  return runnable->mRV;
  1.1868 +}
  1.1869 +
  1.1870 +void
  1.1871 +ClientAuthDataRunnable::RunOnTargetThread()
  1.1872 +{
  1.1873 +  PLArenaPool* arena = nullptr;
  1.1874 +  char** caNameStrings;
  1.1875 +  mozilla::pkix::ScopedCERTCertificate cert;
  1.1876 +  ScopedSECKEYPrivateKey privKey;
  1.1877 +  mozilla::pkix::ScopedCERTCertList certList;
  1.1878 +  CERTCertListNode* node;
  1.1879 +  ScopedCERTCertNicknames nicknames;
  1.1880 +  int keyError = 0; // used for private key retrieval error
  1.1881 +  SSM_UserCertChoice certChoice;
  1.1882 +  int32_t NumberOfCerts = 0;
  1.1883 +  void* wincx = mSocketInfo;
  1.1884 +  nsresult rv;
  1.1885 +
  1.1886 +  // create caNameStrings
  1.1887 +  arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
  1.1888 +  if (!arena) {
  1.1889 +    goto loser;
  1.1890 +  }
  1.1891 +
  1.1892 +  caNameStrings = (char**) PORT_ArenaAlloc(arena,
  1.1893 +                                           sizeof(char*) * (mCANames->nnames));
  1.1894 +  if (!caNameStrings) {
  1.1895 +    goto loser;
  1.1896 +  }
  1.1897 +
  1.1898 +  mRV = nsConvertCANamesToStrings(arena, caNameStrings, mCANames);
  1.1899 +  if (mRV != SECSuccess) {
  1.1900 +    goto loser;
  1.1901 +  }
  1.1902 +
  1.1903 +  // get the preference
  1.1904 +  if (NS_FAILED(nsGetUserCertChoice(&certChoice))) {
  1.1905 +    goto loser;
  1.1906 +  }
  1.1907 +
  1.1908 +  // find valid user cert and key pair
  1.1909 +  if (certChoice == AUTO) {
  1.1910 +    // automatically find the right cert
  1.1911 +
  1.1912 +    // find all user certs that are valid and for SSL
  1.1913 +    certList = CERT_FindUserCertsByUsage(CERT_GetDefaultCertDB(),
  1.1914 +                                         certUsageSSLClient, false,
  1.1915 +                                         true, wincx);
  1.1916 +    if (!certList) {
  1.1917 +      goto noCert;
  1.1918 +    }
  1.1919 +
  1.1920 +    // filter the list to those issued by CAs supported by the server
  1.1921 +    mRV = CERT_FilterCertListByCANames(certList.get(), mCANames->nnames,
  1.1922 +                                       caNameStrings, certUsageSSLClient);
  1.1923 +    if (mRV != SECSuccess) {
  1.1924 +      goto noCert;
  1.1925 +    }
  1.1926 +
  1.1927 +    // make sure the list is not empty
  1.1928 +    node = CERT_LIST_HEAD(certList);
  1.1929 +    if (CERT_LIST_END(node, certList)) {
  1.1930 +      goto noCert;
  1.1931 +    }
  1.1932 +
  1.1933 +    ScopedCERTCertificate low_prio_nonrep_cert;
  1.1934 +
  1.1935 +    // loop through the list until we find a cert with a key
  1.1936 +    while (!CERT_LIST_END(node, certList)) {
  1.1937 +      // if the certificate has restriction and we do not satisfy it we do not
  1.1938 +      // use it
  1.1939 +      privKey = PK11_FindKeyByAnyCert(node->cert, wincx);
  1.1940 +      if (privKey) {
  1.1941 +        if (hasExplicitKeyUsageNonRepudiation(node->cert)) {
  1.1942 +          privKey = nullptr;
  1.1943 +          // Not a prefered cert
  1.1944 +          if (!low_prio_nonrep_cert) { // did not yet find a low prio cert
  1.1945 +            low_prio_nonrep_cert = CERT_DupCertificate(node->cert);
  1.1946 +          }
  1.1947 +        } else {
  1.1948 +          // this is a good cert to present
  1.1949 +          cert = CERT_DupCertificate(node->cert);
  1.1950 +          break;
  1.1951 +        }
  1.1952 +      }
  1.1953 +      keyError = PR_GetError();
  1.1954 +      if (keyError == SEC_ERROR_BAD_PASSWORD) {
  1.1955 +        // problem with password: bail
  1.1956 +        goto loser;
  1.1957 +      }
  1.1958 +
  1.1959 +      node = CERT_LIST_NEXT(node);
  1.1960 +    }
  1.1961 +
  1.1962 +    if (!cert && low_prio_nonrep_cert) {
  1.1963 +      cert = low_prio_nonrep_cert.forget();
  1.1964 +      privKey = PK11_FindKeyByAnyCert(cert.get(), wincx);
  1.1965 +    }
  1.1966 +
  1.1967 +    if (!cert) {
  1.1968 +      goto noCert;
  1.1969 +    }
  1.1970 +  } else { // Not Auto => ask
  1.1971 +    // Get the SSL Certificate
  1.1972 +
  1.1973 +    nsXPIDLCString hostname;
  1.1974 +    mSocketInfo->GetHostName(getter_Copies(hostname));
  1.1975 +
  1.1976 +    RefPtr<nsClientAuthRememberService> cars =
  1.1977 +      mSocketInfo->SharedState().GetClientAuthRememberService();
  1.1978 +
  1.1979 +    bool hasRemembered = false;
  1.1980 +    nsCString rememberedDBKey;
  1.1981 +    if (cars) {
  1.1982 +      bool found;
  1.1983 +      rv = cars->HasRememberedDecision(hostname, mServerCert,
  1.1984 +        rememberedDBKey, &found);
  1.1985 +      if (NS_SUCCEEDED(rv) && found) {
  1.1986 +        hasRemembered = true;
  1.1987 +      }
  1.1988 +    }
  1.1989 +
  1.1990 +    bool canceled = false;
  1.1991 +
  1.1992 +    if (hasRemembered) {
  1.1993 +      if (rememberedDBKey.IsEmpty()) {
  1.1994 +        canceled = true;
  1.1995 +      } else {
  1.1996 +        nsCOMPtr<nsIX509CertDB> certdb;
  1.1997 +        certdb = do_GetService(NS_X509CERTDB_CONTRACTID);
  1.1998 +        if (certdb) {
  1.1999 +          nsCOMPtr<nsIX509Cert> found_cert;
  1.2000 +          nsresult find_rv =
  1.2001 +            certdb->FindCertByDBKey(rememberedDBKey.get(), nullptr,
  1.2002 +            getter_AddRefs(found_cert));
  1.2003 +          if (NS_SUCCEEDED(find_rv) && found_cert) {
  1.2004 +            nsNSSCertificate* obj_cert =
  1.2005 +              reinterpret_cast<nsNSSCertificate*>(found_cert.get());
  1.2006 +            if (obj_cert) {
  1.2007 +              cert = obj_cert->GetCert();
  1.2008 +            }
  1.2009 +          }
  1.2010 +
  1.2011 +          if (!cert) {
  1.2012 +            hasRemembered = false;
  1.2013 +          }
  1.2014 +        }
  1.2015 +      }
  1.2016 +    }
  1.2017 +
  1.2018 +    if (!hasRemembered) {
  1.2019 +      // user selects a cert to present
  1.2020 +      nsIClientAuthDialogs* dialogs = nullptr;
  1.2021 +      int32_t selectedIndex = -1;
  1.2022 +      char16_t** certNicknameList = nullptr;
  1.2023 +      char16_t** certDetailsList = nullptr;
  1.2024 +
  1.2025 +      // find all user certs that are for SSL
  1.2026 +      // note that we are allowing expired certs in this list
  1.2027 +      certList = CERT_FindUserCertsByUsage(CERT_GetDefaultCertDB(),
  1.2028 +        certUsageSSLClient, false,
  1.2029 +        false, wincx);
  1.2030 +      if (!certList) {
  1.2031 +        goto noCert;
  1.2032 +      }
  1.2033 +
  1.2034 +      if (mCANames->nnames != 0) {
  1.2035 +        // filter the list to those issued by CAs supported by the server
  1.2036 +        mRV = CERT_FilterCertListByCANames(certList.get(),
  1.2037 +                                           mCANames->nnames,
  1.2038 +                                           caNameStrings,
  1.2039 +                                           certUsageSSLClient);
  1.2040 +        if (mRV != SECSuccess) {
  1.2041 +          goto loser;
  1.2042 +        }
  1.2043 +      }
  1.2044 +
  1.2045 +      if (CERT_LIST_END(CERT_LIST_HEAD(certList), certList)) {
  1.2046 +        // list is empty - no matching certs
  1.2047 +        goto noCert;
  1.2048 +      }
  1.2049 +
  1.2050 +      // filter it further for hostname restriction
  1.2051 +      node = CERT_LIST_HEAD(certList.get());
  1.2052 +      while (!CERT_LIST_END(node, certList.get())) {
  1.2053 +        ++NumberOfCerts;
  1.2054 +        node = CERT_LIST_NEXT(node);
  1.2055 +      }
  1.2056 +      if (CERT_LIST_END(CERT_LIST_HEAD(certList.get()), certList.get())) {
  1.2057 +        goto noCert;
  1.2058 +      }
  1.2059 +
  1.2060 +      nicknames = getNSSCertNicknamesFromCertList(certList.get());
  1.2061 +
  1.2062 +      if (!nicknames) {
  1.2063 +        goto loser;
  1.2064 +      }
  1.2065 +
  1.2066 +      NS_ASSERTION(nicknames->numnicknames == NumberOfCerts, "nicknames->numnicknames != NumberOfCerts");
  1.2067 +
  1.2068 +      // Get CN and O of the subject and O of the issuer
  1.2069 +      char* ccn = CERT_GetCommonName(&mServerCert->subject);
  1.2070 +      void* v = ccn;
  1.2071 +      voidCleaner ccnCleaner(v);
  1.2072 +      NS_ConvertUTF8toUTF16 cn(ccn);
  1.2073 +
  1.2074 +      int32_t port;
  1.2075 +      mSocketInfo->GetPort(&port);
  1.2076 +
  1.2077 +      nsString cn_host_port;
  1.2078 +      if (ccn && strcmp(ccn, hostname) == 0) {
  1.2079 +        cn_host_port.Append(cn);
  1.2080 +        cn_host_port.AppendLiteral(":");
  1.2081 +        cn_host_port.AppendInt(port);
  1.2082 +      } else {
  1.2083 +        cn_host_port.Append(cn);
  1.2084 +        cn_host_port.AppendLiteral(" (");
  1.2085 +        cn_host_port.AppendLiteral(":");
  1.2086 +        cn_host_port.AppendInt(port);
  1.2087 +        cn_host_port.AppendLiteral(")");
  1.2088 +      }
  1.2089 +
  1.2090 +      char* corg = CERT_GetOrgName(&mServerCert->subject);
  1.2091 +      NS_ConvertUTF8toUTF16 org(corg);
  1.2092 +      if (corg) PORT_Free(corg);
  1.2093 +
  1.2094 +      char* cissuer = CERT_GetOrgName(&mServerCert->issuer);
  1.2095 +      NS_ConvertUTF8toUTF16 issuer(cissuer);
  1.2096 +      if (cissuer) PORT_Free(cissuer);
  1.2097 +
  1.2098 +      certNicknameList =
  1.2099 +        (char16_t**)nsMemory::Alloc(sizeof(char16_t*)* nicknames->numnicknames);
  1.2100 +      if (!certNicknameList)
  1.2101 +        goto loser;
  1.2102 +      certDetailsList =
  1.2103 +        (char16_t**)nsMemory::Alloc(sizeof(char16_t*)* nicknames->numnicknames);
  1.2104 +      if (!certDetailsList) {
  1.2105 +        nsMemory::Free(certNicknameList);
  1.2106 +        goto loser;
  1.2107 +      }
  1.2108 +
  1.2109 +      int32_t CertsToUse;
  1.2110 +      for (CertsToUse = 0, node = CERT_LIST_HEAD(certList);
  1.2111 +        !CERT_LIST_END(node, certList) && CertsToUse < nicknames->numnicknames;
  1.2112 +        node = CERT_LIST_NEXT(node)
  1.2113 +        ) {
  1.2114 +        RefPtr<nsNSSCertificate> tempCert(nsNSSCertificate::Create(node->cert));
  1.2115 +
  1.2116 +        if (!tempCert)
  1.2117 +          continue;
  1.2118 +
  1.2119 +        NS_ConvertUTF8toUTF16 i_nickname(nicknames->nicknames[CertsToUse]);
  1.2120 +        nsAutoString nickWithSerial, details;
  1.2121 +
  1.2122 +        if (NS_FAILED(tempCert->FormatUIStrings(i_nickname, nickWithSerial, details)))
  1.2123 +          continue;
  1.2124 +
  1.2125 +        certNicknameList[CertsToUse] = ToNewUnicode(nickWithSerial);
  1.2126 +        if (!certNicknameList[CertsToUse])
  1.2127 +          continue;
  1.2128 +        certDetailsList[CertsToUse] = ToNewUnicode(details);
  1.2129 +        if (!certDetailsList[CertsToUse]) {
  1.2130 +          nsMemory::Free(certNicknameList[CertsToUse]);
  1.2131 +          continue;
  1.2132 +        }
  1.2133 +
  1.2134 +        ++CertsToUse;
  1.2135 +      }
  1.2136 +
  1.2137 +      // Throw up the client auth dialog and get back the index of the selected cert
  1.2138 +      nsresult rv = getNSSDialogs((void**)&dialogs,
  1.2139 +        NS_GET_IID(nsIClientAuthDialogs),
  1.2140 +        NS_CLIENTAUTHDIALOGS_CONTRACTID);
  1.2141 +
  1.2142 +      if (NS_FAILED(rv)) {
  1.2143 +        NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(CertsToUse, certNicknameList);
  1.2144 +        NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(CertsToUse, certDetailsList);
  1.2145 +        goto loser;
  1.2146 +      }
  1.2147 +
  1.2148 +      {
  1.2149 +        nsPSMUITracker tracker;
  1.2150 +        if (tracker.isUIForbidden()) {
  1.2151 +          rv = NS_ERROR_NOT_AVAILABLE;
  1.2152 +        } else {
  1.2153 +          rv = dialogs->ChooseCertificate(mSocketInfo, cn_host_port.get(),
  1.2154 +            org.get(), issuer.get(),
  1.2155 +            (const char16_t**)certNicknameList,
  1.2156 +            (const char16_t**)certDetailsList,
  1.2157 +            CertsToUse, &selectedIndex, &canceled);
  1.2158 +        }
  1.2159 +      }
  1.2160 +
  1.2161 +      NS_RELEASE(dialogs);
  1.2162 +      NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(CertsToUse, certNicknameList);
  1.2163 +      NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(CertsToUse, certDetailsList);
  1.2164 +
  1.2165 +      if (NS_FAILED(rv)) goto loser;
  1.2166 +
  1.2167 +      // even if the user has canceled, we want to remember that, to avoid repeating prompts
  1.2168 +      bool wantRemember = false;
  1.2169 +      mSocketInfo->GetRememberClientAuthCertificate(&wantRemember);
  1.2170 +
  1.2171 +      int i;
  1.2172 +      if (!canceled)
  1.2173 +      for (i = 0, node = CERT_LIST_HEAD(certList);
  1.2174 +        !CERT_LIST_END(node, certList);
  1.2175 +        ++i, node = CERT_LIST_NEXT(node)) {
  1.2176 +
  1.2177 +        if (i == selectedIndex) {
  1.2178 +          cert = CERT_DupCertificate(node->cert);
  1.2179 +          break;
  1.2180 +        }
  1.2181 +      }
  1.2182 +
  1.2183 +      if (cars && wantRemember) {
  1.2184 +        cars->RememberDecision(hostname, mServerCert,
  1.2185 +          canceled ? nullptr : cert.get());
  1.2186 +      }
  1.2187 +    }
  1.2188 +
  1.2189 +    if (canceled) { rv = NS_ERROR_NOT_AVAILABLE; goto loser; }
  1.2190 +
  1.2191 +    if (!cert) {
  1.2192 +      goto loser;
  1.2193 +    }
  1.2194 +
  1.2195 +    // go get the private key
  1.2196 +    privKey = PK11_FindKeyByAnyCert(cert.get(), wincx);
  1.2197 +    if (!privKey) {
  1.2198 +      keyError = PR_GetError();
  1.2199 +      if (keyError == SEC_ERROR_BAD_PASSWORD) {
  1.2200 +        // problem with password: bail
  1.2201 +        goto loser;
  1.2202 +      } else {
  1.2203 +        goto noCert;
  1.2204 +      }
  1.2205 +    }
  1.2206 +  }
  1.2207 +  goto done;
  1.2208 +
  1.2209 +noCert:
  1.2210 +loser:
  1.2211 +  if (mRV == SECSuccess) {
  1.2212 +    mRV = SECFailure;
  1.2213 +  }
  1.2214 +done:
  1.2215 +  int error = PR_GetError();
  1.2216 +
  1.2217 +  if (arena) {
  1.2218 +    PORT_FreeArena(arena, false);
  1.2219 +  }
  1.2220 +
  1.2221 +  *mPRetCert = cert.release();
  1.2222 +  *mPRetKey = privKey.forget();
  1.2223 +
  1.2224 +  if (mRV == SECFailure) {
  1.2225 +    mErrorCodeToReport = error;
  1.2226 +  }
  1.2227 +}
  1.2228 +
  1.2229 +static PRFileDesc*
  1.2230 +nsSSLIOLayerImportFD(PRFileDesc* fd,
  1.2231 +                     nsNSSSocketInfo* infoObject,
  1.2232 +                     const char* host)
  1.2233 +{
  1.2234 +  nsNSSShutDownPreventionLock locker;
  1.2235 +  PRFileDesc* sslSock = SSL_ImportFD(nullptr, fd);
  1.2236 +  if (!sslSock) {
  1.2237 +    NS_ASSERTION(false, "NSS: Error importing socket");
  1.2238 +    return nullptr;
  1.2239 +  }
  1.2240 +  SSL_SetPKCS11PinArg(sslSock, (nsIInterfaceRequestor*) infoObject);
  1.2241 +  SSL_HandshakeCallback(sslSock, HandshakeCallback, infoObject);
  1.2242 +  SSL_SetCanFalseStartCallback(sslSock, CanFalseStartCallback, infoObject);
  1.2243 +
  1.2244 +  // Disable this hook if we connect anonymously. See bug 466080.
  1.2245 +  uint32_t flags = 0;
  1.2246 +  infoObject->GetProviderFlags(&flags);
  1.2247 +  if (flags & nsISocketProvider::ANONYMOUS_CONNECT) {
  1.2248 +      SSL_GetClientAuthDataHook(sslSock, nullptr, infoObject);
  1.2249 +  } else {
  1.2250 +      SSL_GetClientAuthDataHook(sslSock,
  1.2251 +                            (SSLGetClientAuthData) nsNSS_SSLGetClientAuthData,
  1.2252 +                            infoObject);
  1.2253 +  }
  1.2254 +  if (SECSuccess != SSL_AuthCertificateHook(sslSock, AuthCertificateHook,
  1.2255 +                                            infoObject)) {
  1.2256 +    NS_NOTREACHED("failed to configure AuthCertificateHook");
  1.2257 +    goto loser;
  1.2258 +  }
  1.2259 +
  1.2260 +  if (SECSuccess != SSL_SetURL(sslSock, host)) {
  1.2261 +    NS_NOTREACHED("SSL_SetURL failed");
  1.2262 +    goto loser;
  1.2263 +  }
  1.2264 +
  1.2265 +  // This is an optimization to make sure the identity info dataset is parsed
  1.2266 +  // and loaded on a separate thread and can be overlapped with network latency.
  1.2267 +  EnsureServerVerificationInitialized();
  1.2268 +
  1.2269 +  return sslSock;
  1.2270 +loser:
  1.2271 +  if (sslSock) {
  1.2272 +    PR_Close(sslSock);
  1.2273 +  }
  1.2274 +  return nullptr;
  1.2275 +}
  1.2276 +
  1.2277 +static nsresult
  1.2278 +nsSSLIOLayerSetOptions(PRFileDesc* fd, bool forSTARTTLS,
  1.2279 +                       bool haveProxy, const char* host, int32_t port,
  1.2280 +                       nsNSSSocketInfo* infoObject)
  1.2281 +{
  1.2282 +  nsNSSShutDownPreventionLock locker;
  1.2283 +  if (forSTARTTLS || haveProxy) {
  1.2284 +    if (SECSuccess != SSL_OptionSet(fd, SSL_SECURITY, false)) {
  1.2285 +      return NS_ERROR_FAILURE;
  1.2286 +    }
  1.2287 +  }
  1.2288 +
  1.2289 +  // Let's see if we're trying to connect to a site we know is
  1.2290 +  // TLS intolerant.
  1.2291 +  nsAutoCString key;
  1.2292 +  key = nsDependentCString(host) + NS_LITERAL_CSTRING(":") + nsPrintfCString("%d", port);
  1.2293 +
  1.2294 +  SSLVersionRange range;
  1.2295 +  if (SSL_VersionRangeGet(fd, &range) != SECSuccess) {
  1.2296 +    return NS_ERROR_FAILURE;
  1.2297 +  }
  1.2298 +
  1.2299 + uint16_t maxEnabledVersion = range.max;
  1.2300 +
  1.2301 +  infoObject->SharedState().IOLayerHelpers()
  1.2302 +    .adjustForTLSIntolerance(infoObject->GetHostName(), infoObject->GetPort(),
  1.2303 +                             range);
  1.2304 +  PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
  1.2305 +         ("[%p] nsSSLIOLayerSetOptions: using TLS version range (0x%04x,0x%04x)\n",
  1.2306 +          fd, static_cast<unsigned int>(range.min),
  1.2307 +              static_cast<unsigned int>(range.max)));
  1.2308 +
  1.2309 +  if (SSL_VersionRangeSet(fd, &range) != SECSuccess) {
  1.2310 +    return NS_ERROR_FAILURE;
  1.2311 +  }
  1.2312 +  infoObject->SetTLSVersionRange(range);
  1.2313 +
  1.2314 +  // when adjustForTLSIntolerance tweaks the maximum version downward,
  1.2315 +  // we tell the server using this SCSV so they can detect a downgrade attack
  1.2316 +  if (range.max < maxEnabledVersion) {
  1.2317 +    PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
  1.2318 +           ("[%p] nsSSLIOLayerSetOptions: enabling TLS_FALLBACK_SCSV\n", fd));
  1.2319 +    if (SECSuccess != SSL_OptionSet(fd, SSL_ENABLE_FALLBACK_SCSV, true)) {
  1.2320 +      return NS_ERROR_FAILURE;
  1.2321 +    }
  1.2322 +  }
  1.2323 +
  1.2324 +  bool enabled = infoObject->SharedState().IsOCSPStaplingEnabled();
  1.2325 +  if (SECSuccess != SSL_OptionSet(fd, SSL_ENABLE_OCSP_STAPLING, enabled)) {
  1.2326 +    return NS_ERROR_FAILURE;
  1.2327 +  }
  1.2328 +
  1.2329 +  if (SECSuccess != SSL_OptionSet(fd, SSL_HANDSHAKE_AS_CLIENT, true)) {
  1.2330 +    return NS_ERROR_FAILURE;
  1.2331 +  }
  1.2332 +
  1.2333 +  nsSSLIOLayerHelpers& ioHelpers = infoObject->SharedState().IOLayerHelpers();
  1.2334 +  if (ioHelpers.isRenegoUnrestrictedSite(nsDependentCString(host))) {
  1.2335 +    if (SECSuccess != SSL_OptionSet(fd, SSL_REQUIRE_SAFE_NEGOTIATION, false)) {
  1.2336 +      return NS_ERROR_FAILURE;
  1.2337 +    }
  1.2338 +    if (SECSuccess != SSL_OptionSet(fd, SSL_ENABLE_RENEGOTIATION, SSL_RENEGOTIATE_UNRESTRICTED)) {
  1.2339 +      return NS_ERROR_FAILURE;
  1.2340 +    }
  1.2341 +  }
  1.2342 +
  1.2343 +  // Set the Peer ID so that SSL proxy connections work properly and to
  1.2344 +  // separate anonymous and/or private browsing connections.
  1.2345 +  uint32_t flags = infoObject->GetProviderFlags();
  1.2346 +  nsAutoCString peerId;
  1.2347 +  if (flags & nsISocketProvider::ANONYMOUS_CONNECT) { // See bug 466080
  1.2348 +    peerId.Append("anon:");
  1.2349 +  }
  1.2350 +  if (flags & nsISocketProvider::NO_PERMANENT_STORAGE) {
  1.2351 +    peerId.Append("private:");
  1.2352 +  }
  1.2353 +  peerId.Append(host);
  1.2354 +  peerId.Append(':');
  1.2355 +  peerId.AppendInt(port);
  1.2356 +  if (SECSuccess != SSL_SetSockPeerID(fd, peerId.get())) {
  1.2357 +    return NS_ERROR_FAILURE;
  1.2358 +  }
  1.2359 +
  1.2360 +  return NS_OK;
  1.2361 +}
  1.2362 +
  1.2363 +nsresult
  1.2364 +nsSSLIOLayerAddToSocket(int32_t family,
  1.2365 +                        const char* host,
  1.2366 +                        int32_t port,
  1.2367 +                        nsIProxyInfo* proxy,
  1.2368 +                        PRFileDesc* fd,
  1.2369 +                        nsISupports** info,
  1.2370 +                        bool forSTARTTLS,
  1.2371 +                        uint32_t providerFlags)
  1.2372 +{
  1.2373 +  nsNSSShutDownPreventionLock locker;
  1.2374 +  PRFileDesc* layer = nullptr;
  1.2375 +  PRFileDesc* plaintextLayer = nullptr;
  1.2376 +  nsresult rv;
  1.2377 +  PRStatus stat;
  1.2378 +
  1.2379 +  SharedSSLState* sharedState =
  1.2380 +    providerFlags & nsISocketProvider::NO_PERMANENT_STORAGE ? PrivateSSLState() : PublicSSLState();
  1.2381 +  nsNSSSocketInfo* infoObject = new nsNSSSocketInfo(*sharedState, providerFlags);
  1.2382 +  if (!infoObject) return NS_ERROR_FAILURE;
  1.2383 +
  1.2384 +  NS_ADDREF(infoObject);
  1.2385 +  infoObject->SetForSTARTTLS(forSTARTTLS);
  1.2386 +  infoObject->SetHostName(host);
  1.2387 +  infoObject->SetPort(port);
  1.2388 +
  1.2389 +  bool haveProxy = false;
  1.2390 +  if (proxy) {
  1.2391 +    nsCString proxyHost;
  1.2392 +    proxy->GetHost(proxyHost);
  1.2393 +    haveProxy = !proxyHost.IsEmpty();
  1.2394 +  }
  1.2395 +
  1.2396 +  // A plaintext observer shim is inserted so we can observe some protocol
  1.2397 +  // details without modifying nss
  1.2398 +  plaintextLayer = PR_CreateIOLayerStub(nsSSLIOLayerHelpers::nsSSLPlaintextLayerIdentity,
  1.2399 +                                        &nsSSLIOLayerHelpers::nsSSLPlaintextLayerMethods);
  1.2400 +  if (plaintextLayer) {
  1.2401 +    plaintextLayer->secret = (PRFilePrivate*) infoObject;
  1.2402 +    stat = PR_PushIOLayer(fd, PR_TOP_IO_LAYER, plaintextLayer);
  1.2403 +    if (stat == PR_FAILURE) {
  1.2404 +      plaintextLayer->dtor(plaintextLayer);
  1.2405 +      plaintextLayer = nullptr;
  1.2406 +    }
  1.2407 +  }
  1.2408 +
  1.2409 +  PRFileDesc* sslSock = nsSSLIOLayerImportFD(fd, infoObject, host);
  1.2410 +  if (!sslSock) {
  1.2411 +    NS_ASSERTION(false, "NSS: Error importing socket");
  1.2412 +    goto loser;
  1.2413 +  }
  1.2414 +
  1.2415 +  infoObject->SetFileDescPtr(sslSock);
  1.2416 +
  1.2417 +  rv = nsSSLIOLayerSetOptions(sslSock, forSTARTTLS, haveProxy, host, port,
  1.2418 +                              infoObject);
  1.2419 +
  1.2420 +  if (NS_FAILED(rv))
  1.2421 +    goto loser;
  1.2422 +
  1.2423 +  // Now, layer ourselves on top of the SSL socket...
  1.2424 +  layer = PR_CreateIOLayerStub(nsSSLIOLayerHelpers::nsSSLIOLayerIdentity,
  1.2425 +                               &nsSSLIOLayerHelpers::nsSSLIOLayerMethods);
  1.2426 +  if (!layer)
  1.2427 +    goto loser;
  1.2428 +
  1.2429 +  layer->secret = (PRFilePrivate*) infoObject;
  1.2430 +  stat = PR_PushIOLayer(sslSock, PR_GetLayersIdentity(sslSock), layer);
  1.2431 +
  1.2432 +  if (stat == PR_FAILURE) {
  1.2433 +    goto loser;
  1.2434 +  }
  1.2435 +
  1.2436 +  nsNSSShutDownList::trackSSLSocketCreate();
  1.2437 +
  1.2438 +  PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p] Socket set up\n", (void*) sslSock));
  1.2439 +  infoObject->QueryInterface(NS_GET_IID(nsISupports), (void**) (info));
  1.2440 +
  1.2441 +  // We are going use a clear connection first //
  1.2442 +  if (forSTARTTLS || haveProxy) {
  1.2443 +    infoObject->SetHandshakeNotPending();
  1.2444 +  }
  1.2445 +
  1.2446 +  infoObject->SharedState().NoteSocketCreated();
  1.2447 +
  1.2448 +  return NS_OK;
  1.2449 + loser:
  1.2450 +  NS_IF_RELEASE(infoObject);
  1.2451 +  if (layer) {
  1.2452 +    layer->dtor(layer);
  1.2453 +  }
  1.2454 +  if (plaintextLayer) {
  1.2455 +    PR_PopIOLayer(fd, nsSSLIOLayerHelpers::nsSSLPlaintextLayerIdentity);
  1.2456 +    plaintextLayer->dtor(plaintextLayer);
  1.2457 +  }
  1.2458 +  return NS_ERROR_FAILURE;
  1.2459 +}

mercurial