security/manager/ssl/src/nsNSSIOLayer.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
michael@0 2 *
michael@0 3 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "nsNSSIOLayer.h"
michael@0 8
michael@0 9 #include "pkix/pkixtypes.h"
michael@0 10 #include "nsNSSComponent.h"
michael@0 11 #include "mozilla/Casting.h"
michael@0 12 #include "mozilla/DebugOnly.h"
michael@0 13 #include "mozilla/Telemetry.h"
michael@0 14
michael@0 15 #include "prlog.h"
michael@0 16 #include "prnetdb.h"
michael@0 17 #include "nsIPrefService.h"
michael@0 18 #include "nsIClientAuthDialogs.h"
michael@0 19 #include "nsClientAuthRemember.h"
michael@0 20 #include "nsISSLErrorListener.h"
michael@0 21
michael@0 22 #include "nsNetUtil.h"
michael@0 23 #include "nsPrintfCString.h"
michael@0 24 #include "SSLServerCertVerification.h"
michael@0 25 #include "nsNSSCertHelper.h"
michael@0 26 #include "nsNSSCleaner.h"
michael@0 27
michael@0 28 #ifndef MOZ_NO_EV_CERTS
michael@0 29 #include "nsIDocShell.h"
michael@0 30 #include "nsIDocShellTreeItem.h"
michael@0 31 #include "nsISecureBrowserUI.h"
michael@0 32 #include "nsIInterfaceRequestorUtils.h"
michael@0 33 #endif
michael@0 34
michael@0 35 #include "nsCharSeparatedTokenizer.h"
michael@0 36 #include "nsIConsoleService.h"
michael@0 37 #include "PSMRunnable.h"
michael@0 38 #include "ScopedNSSTypes.h"
michael@0 39 #include "SharedSSLState.h"
michael@0 40 #include "mozilla/Preferences.h"
michael@0 41 #include "nsContentUtils.h"
michael@0 42
michael@0 43 #include "ssl.h"
michael@0 44 #include "sslproto.h"
michael@0 45 #include "secerr.h"
michael@0 46 #include "sslerr.h"
michael@0 47 #include "secder.h"
michael@0 48 #include "keyhi.h"
michael@0 49
michael@0 50 #include <algorithm>
michael@0 51
michael@0 52 using namespace mozilla;
michael@0 53 using namespace mozilla::psm;
michael@0 54
michael@0 55 //#define DEBUG_SSL_VERBOSE //Enable this define to get minimal
michael@0 56 //reports when doing SSL read/write
michael@0 57
michael@0 58 //#define DUMP_BUFFER //Enable this define along with
michael@0 59 //DEBUG_SSL_VERBOSE to dump SSL
michael@0 60 //read/write buffer to a log.
michael@0 61 //Uses PR_LOG except on Mac where
michael@0 62 //we always write out to our own
michael@0 63 //file.
michael@0 64
michael@0 65 namespace {
michael@0 66
michael@0 67 NSSCleanupAutoPtrClass(void, PR_FREEIF)
michael@0 68
michael@0 69 void
michael@0 70 getSiteKey(const nsACString& hostName, uint16_t port,
michael@0 71 /*out*/ nsCSubstring& key)
michael@0 72 {
michael@0 73 key = hostName;
michael@0 74 key.AppendASCII(":");
michael@0 75 key.AppendInt(port);
michael@0 76 }
michael@0 77
michael@0 78 // SSM_UserCertChoice: enum for cert choice info
michael@0 79 typedef enum {ASK, AUTO} SSM_UserCertChoice;
michael@0 80
michael@0 81 // Forward secrecy provides us with a proof of posession of the private key
michael@0 82 // from the server. Without of proof of posession of the private key of the
michael@0 83 // server, any MitM can force us to false start in a connection that the real
michael@0 84 // server never participates in, since with RSA key exchange a MitM can
michael@0 85 // complete the server's first round of the handshake without knowing the
michael@0 86 // server's public key This would be used, for example, to greatly accelerate
michael@0 87 // the attacks on RC4 or other attacks that allow a MitM to decrypt encrypted
michael@0 88 // data without having the server's private key. Without false start, such
michael@0 89 // attacks are naturally rate limited by network latency and may also be rate
michael@0 90 // limited explicitly by the server's DoS or other security mechanisms.
michael@0 91 // Further, because the server that has the private key must participate in the
michael@0 92 // handshake, the server could detect these kinds of attacks if they they are
michael@0 93 // repeated rapidly and/or frequently, by noticing lots of invalid or
michael@0 94 // incomplete handshakes.
michael@0 95 //
michael@0 96 // With this in mind, when we choose not to require forward secrecy (when the
michael@0 97 // pref's value is false), then we will still only false start for RSA key
michael@0 98 // exchange only if the most recent handshake we've previously done used RSA
michael@0 99 // key exchange. This way, we prevent any (EC)DHE-to-RSA downgrade attacks for
michael@0 100 // servers that consistently choose (EC)DHE key exchange. In order to prevent
michael@0 101 // downgrade from ECDHE_*_GCM cipher suites, we need to also consider downgrade
michael@0 102 // from TLS 1.2 to earlier versions (bug 861310).
michael@0 103 static const bool FALSE_START_REQUIRE_FORWARD_SECRECY_DEFAULT = true;
michael@0 104
michael@0 105 // XXX(perf bug 940787): We currently require NPN because there is a very
michael@0 106 // high (perfect so far) correlation between servers that are false-start-
michael@0 107 // tolerant and servers that support NPN, according to Google. Without this, we
michael@0 108 // will run into interop issues with a small percentage of servers that stop
michael@0 109 // responding when we attempt to false start.
michael@0 110 static const bool FALSE_START_REQUIRE_NPN_DEFAULT = true;
michael@0 111
michael@0 112 } // unnamed namespace
michael@0 113
michael@0 114 #ifdef PR_LOGGING
michael@0 115 extern PRLogModuleInfo* gPIPNSSLog;
michael@0 116 #endif
michael@0 117
michael@0 118 nsNSSSocketInfo::nsNSSSocketInfo(SharedSSLState& aState, uint32_t providerFlags)
michael@0 119 : mFd(nullptr),
michael@0 120 mCertVerificationState(before_cert_verification),
michael@0 121 mSharedState(aState),
michael@0 122 mForSTARTTLS(false),
michael@0 123 mHandshakePending(true),
michael@0 124 mRememberClientAuthCertificate(false),
michael@0 125 mPreliminaryHandshakeDone(false),
michael@0 126 mNPNCompleted(false),
michael@0 127 mFalseStartCallbackCalled(false),
michael@0 128 mFalseStarted(false),
michael@0 129 mIsFullHandshake(false),
michael@0 130 mHandshakeCompleted(false),
michael@0 131 mJoined(false),
michael@0 132 mSentClientCert(false),
michael@0 133 mNotedTimeUntilReady(false),
michael@0 134 mKEAUsed(nsISSLSocketControl::KEY_EXCHANGE_UNKNOWN),
michael@0 135 mKEAExpected(nsISSLSocketControl::KEY_EXCHANGE_UNKNOWN),
michael@0 136 mSSLVersionUsed(nsISSLSocketControl::SSL_VERSION_UNKNOWN),
michael@0 137 mProviderFlags(providerFlags),
michael@0 138 mSocketCreationTimestamp(TimeStamp::Now()),
michael@0 139 mPlaintextBytesRead(0)
michael@0 140 {
michael@0 141 mTLSVersionRange.min = 0;
michael@0 142 mTLSVersionRange.max = 0;
michael@0 143 }
michael@0 144
michael@0 145 NS_IMPL_ISUPPORTS_INHERITED(nsNSSSocketInfo, TransportSecurityInfo,
michael@0 146 nsISSLSocketControl,
michael@0 147 nsIClientAuthUserDecision)
michael@0 148
michael@0 149 NS_IMETHODIMP
michael@0 150 nsNSSSocketInfo::GetProviderFlags(uint32_t* aProviderFlags)
michael@0 151 {
michael@0 152 *aProviderFlags = mProviderFlags;
michael@0 153 return NS_OK;
michael@0 154 }
michael@0 155
michael@0 156 NS_IMETHODIMP
michael@0 157 nsNSSSocketInfo::GetKEAUsed(int16_t* aKea)
michael@0 158 {
michael@0 159 *aKea = mKEAUsed;
michael@0 160 return NS_OK;
michael@0 161 }
michael@0 162
michael@0 163 NS_IMETHODIMP
michael@0 164 nsNSSSocketInfo::GetKEAExpected(int16_t* aKea)
michael@0 165 {
michael@0 166 *aKea = mKEAExpected;
michael@0 167 return NS_OK;
michael@0 168 }
michael@0 169
michael@0 170 NS_IMETHODIMP
michael@0 171 nsNSSSocketInfo::SetKEAExpected(int16_t aKea)
michael@0 172 {
michael@0 173 mKEAExpected = aKea;
michael@0 174 return NS_OK;
michael@0 175 }
michael@0 176
michael@0 177 NS_IMETHODIMP
michael@0 178 nsNSSSocketInfo::GetSSLVersionUsed(int16_t* aSSLVersionUsed)
michael@0 179 {
michael@0 180 *aSSLVersionUsed = mSSLVersionUsed;
michael@0 181 return NS_OK;
michael@0 182 }
michael@0 183
michael@0 184 NS_IMETHODIMP
michael@0 185 nsNSSSocketInfo::GetRememberClientAuthCertificate(bool* aRemember)
michael@0 186 {
michael@0 187 NS_ENSURE_ARG_POINTER(aRemember);
michael@0 188 *aRemember = mRememberClientAuthCertificate;
michael@0 189 return NS_OK;
michael@0 190 }
michael@0 191
michael@0 192 NS_IMETHODIMP
michael@0 193 nsNSSSocketInfo::SetRememberClientAuthCertificate(bool aRemember)
michael@0 194 {
michael@0 195 mRememberClientAuthCertificate = aRemember;
michael@0 196 return NS_OK;
michael@0 197 }
michael@0 198
michael@0 199 NS_IMETHODIMP
michael@0 200 nsNSSSocketInfo::GetNotificationCallbacks(nsIInterfaceRequestor** aCallbacks)
michael@0 201 {
michael@0 202 *aCallbacks = mCallbacks;
michael@0 203 NS_IF_ADDREF(*aCallbacks);
michael@0 204 return NS_OK;
michael@0 205 }
michael@0 206
michael@0 207 NS_IMETHODIMP
michael@0 208 nsNSSSocketInfo::SetNotificationCallbacks(nsIInterfaceRequestor* aCallbacks)
michael@0 209 {
michael@0 210 if (!aCallbacks) {
michael@0 211 mCallbacks = nullptr;
michael@0 212 return NS_OK;
michael@0 213 }
michael@0 214
michael@0 215 mCallbacks = aCallbacks;
michael@0 216
michael@0 217 return NS_OK;
michael@0 218 }
michael@0 219
michael@0 220 #ifndef MOZ_NO_EV_CERTS
michael@0 221 static void
michael@0 222 getSecureBrowserUI(nsIInterfaceRequestor* callbacks,
michael@0 223 nsISecureBrowserUI** result)
michael@0 224 {
michael@0 225 NS_ASSERTION(result, "result parameter to getSecureBrowserUI is null");
michael@0 226 *result = nullptr;
michael@0 227
michael@0 228 NS_ASSERTION(NS_IsMainThread(),
michael@0 229 "getSecureBrowserUI called off the main thread");
michael@0 230
michael@0 231 if (!callbacks)
michael@0 232 return;
michael@0 233
michael@0 234 nsCOMPtr<nsISecureBrowserUI> secureUI = do_GetInterface(callbacks);
michael@0 235 if (secureUI) {
michael@0 236 secureUI.forget(result);
michael@0 237 return;
michael@0 238 }
michael@0 239
michael@0 240 nsCOMPtr<nsIDocShellTreeItem> item = do_GetInterface(callbacks);
michael@0 241 if (item) {
michael@0 242 nsCOMPtr<nsIDocShellTreeItem> rootItem;
michael@0 243 (void) item->GetSameTypeRootTreeItem(getter_AddRefs(rootItem));
michael@0 244
michael@0 245 nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(rootItem);
michael@0 246 if (docShell) {
michael@0 247 (void) docShell->GetSecurityUI(result);
michael@0 248 }
michael@0 249 }
michael@0 250 }
michael@0 251 #endif
michael@0 252
michael@0 253 void
michael@0 254 nsNSSSocketInfo::NoteTimeUntilReady()
michael@0 255 {
michael@0 256 if (mNotedTimeUntilReady)
michael@0 257 return;
michael@0 258
michael@0 259 mNotedTimeUntilReady = true;
michael@0 260
michael@0 261 // This will include TCP and proxy tunnel wait time
michael@0 262 Telemetry::AccumulateTimeDelta(Telemetry::SSL_TIME_UNTIL_READY,
michael@0 263 mSocketCreationTimestamp, TimeStamp::Now());
michael@0 264 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
michael@0 265 ("[%p] nsNSSSocketInfo::NoteTimeUntilReady\n", mFd));
michael@0 266 }
michael@0 267
michael@0 268 void
michael@0 269 nsNSSSocketInfo::SetHandshakeCompleted()
michael@0 270 {
michael@0 271 if (!mHandshakeCompleted) {
michael@0 272 enum HandshakeType {
michael@0 273 Resumption = 1,
michael@0 274 FalseStarted = 2,
michael@0 275 ChoseNotToFalseStart = 3,
michael@0 276 NotAllowedToFalseStart = 4,
michael@0 277 };
michael@0 278
michael@0 279 HandshakeType handshakeType = !IsFullHandshake() ? Resumption
michael@0 280 : mFalseStarted ? FalseStarted
michael@0 281 : mFalseStartCallbackCalled ? ChoseNotToFalseStart
michael@0 282 : NotAllowedToFalseStart;
michael@0 283
michael@0 284 // This will include TCP and proxy tunnel wait time
michael@0 285 Telemetry::AccumulateTimeDelta(Telemetry::SSL_TIME_UNTIL_HANDSHAKE_FINISHED,
michael@0 286 mSocketCreationTimestamp, TimeStamp::Now());
michael@0 287
michael@0 288 // If the handshake is completed for the first time from just 1 callback
michael@0 289 // that means that TLS session resumption must have been used.
michael@0 290 Telemetry::Accumulate(Telemetry::SSL_RESUMED_SESSION,
michael@0 291 handshakeType == Resumption);
michael@0 292 Telemetry::Accumulate(Telemetry::SSL_HANDSHAKE_TYPE, handshakeType);
michael@0 293 }
michael@0 294
michael@0 295
michael@0 296 // Remove the plain text layer as it is not needed anymore.
michael@0 297 // The plain text layer is not always present - so its not a fatal error
michael@0 298 // if it cannot be removed
michael@0 299 PRFileDesc* poppedPlaintext =
michael@0 300 PR_GetIdentitiesLayer(mFd, nsSSLIOLayerHelpers::nsSSLPlaintextLayerIdentity);
michael@0 301 if (poppedPlaintext) {
michael@0 302 PR_PopIOLayer(mFd, nsSSLIOLayerHelpers::nsSSLPlaintextLayerIdentity);
michael@0 303 poppedPlaintext->dtor(poppedPlaintext);
michael@0 304 }
michael@0 305
michael@0 306 mHandshakeCompleted = true;
michael@0 307
michael@0 308 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
michael@0 309 ("[%p] nsNSSSocketInfo::SetHandshakeCompleted\n", (void*) mFd));
michael@0 310
michael@0 311 mIsFullHandshake = false; // reset for next handshake on this connection
michael@0 312 }
michael@0 313
michael@0 314 void
michael@0 315 nsNSSSocketInfo::SetNegotiatedNPN(const char* value, uint32_t length)
michael@0 316 {
michael@0 317 if (!value) {
michael@0 318 mNegotiatedNPN.Truncate();
michael@0 319 } else {
michael@0 320 mNegotiatedNPN.Assign(value, length);
michael@0 321 }
michael@0 322 mNPNCompleted = true;
michael@0 323 }
michael@0 324
michael@0 325 NS_IMETHODIMP
michael@0 326 nsNSSSocketInfo::GetNegotiatedNPN(nsACString& aNegotiatedNPN)
michael@0 327 {
michael@0 328 if (!mNPNCompleted)
michael@0 329 return NS_ERROR_NOT_CONNECTED;
michael@0 330
michael@0 331 aNegotiatedNPN = mNegotiatedNPN;
michael@0 332 return NS_OK;
michael@0 333 }
michael@0 334
michael@0 335 NS_IMETHODIMP
michael@0 336 nsNSSSocketInfo::JoinConnection(const nsACString& npnProtocol,
michael@0 337 const nsACString& hostname,
michael@0 338 int32_t port,
michael@0 339 bool* _retval)
michael@0 340 {
michael@0 341 *_retval = false;
michael@0 342
michael@0 343 // Different ports may not be joined together
michael@0 344 if (port != GetPort())
michael@0 345 return NS_OK;
michael@0 346
michael@0 347 // Make sure NPN has been completed and matches requested npnProtocol
michael@0 348 if (!mNPNCompleted || !mNegotiatedNPN.Equals(npnProtocol))
michael@0 349 return NS_OK;
michael@0 350
michael@0 351 // If this is the same hostname then the certicate status does not
michael@0 352 // need to be considered. They are joinable.
michael@0 353 if (hostname.Equals(GetHostName())) {
michael@0 354 *_retval = true;
michael@0 355 return NS_OK;
michael@0 356 }
michael@0 357
michael@0 358 // Before checking the server certificate we need to make sure the
michael@0 359 // handshake has completed.
michael@0 360 if (!mHandshakeCompleted || !SSLStatus() || !SSLStatus()->mServerCert)
michael@0 361 return NS_OK;
michael@0 362
michael@0 363 // If the cert has error bits (e.g. it is untrusted) then do not join.
michael@0 364 // The value of mHaveCertErrorBits is only reliable because we know that
michael@0 365 // the handshake completed.
michael@0 366 if (SSLStatus()->mHaveCertErrorBits)
michael@0 367 return NS_OK;
michael@0 368
michael@0 369 // If the connection is using client certificates then do not join
michael@0 370 // because the user decides on whether to send client certs to hosts on a
michael@0 371 // per-domain basis.
michael@0 372 if (mSentClientCert)
michael@0 373 return NS_OK;
michael@0 374
michael@0 375 // Ensure that the server certificate covers the hostname that would
michael@0 376 // like to join this connection
michael@0 377
michael@0 378 ScopedCERTCertificate nssCert;
michael@0 379
michael@0 380 nsCOMPtr<nsIX509Cert2> cert2 = do_QueryInterface(SSLStatus()->mServerCert);
michael@0 381 if (cert2)
michael@0 382 nssCert = cert2->GetCert();
michael@0 383
michael@0 384 if (!nssCert)
michael@0 385 return NS_OK;
michael@0 386
michael@0 387 if (CERT_VerifyCertName(nssCert, PromiseFlatCString(hostname).get()) !=
michael@0 388 SECSuccess)
michael@0 389 return NS_OK;
michael@0 390
michael@0 391 // All tests pass - this is joinable
michael@0 392 mJoined = true;
michael@0 393 *_retval = true;
michael@0 394 return NS_OK;
michael@0 395 }
michael@0 396
michael@0 397 bool
michael@0 398 nsNSSSocketInfo::GetForSTARTTLS()
michael@0 399 {
michael@0 400 return mForSTARTTLS;
michael@0 401 }
michael@0 402
michael@0 403 void
michael@0 404 nsNSSSocketInfo::SetForSTARTTLS(bool aForSTARTTLS)
michael@0 405 {
michael@0 406 mForSTARTTLS = aForSTARTTLS;
michael@0 407 }
michael@0 408
michael@0 409 NS_IMETHODIMP
michael@0 410 nsNSSSocketInfo::ProxyStartSSL()
michael@0 411 {
michael@0 412 return ActivateSSL();
michael@0 413 }
michael@0 414
michael@0 415 NS_IMETHODIMP
michael@0 416 nsNSSSocketInfo::StartTLS()
michael@0 417 {
michael@0 418 return ActivateSSL();
michael@0 419 }
michael@0 420
michael@0 421 NS_IMETHODIMP
michael@0 422 nsNSSSocketInfo::SetNPNList(nsTArray<nsCString>& protocolArray)
michael@0 423 {
michael@0 424 nsNSSShutDownPreventionLock locker;
michael@0 425 if (isAlreadyShutDown())
michael@0 426 return NS_ERROR_NOT_AVAILABLE;
michael@0 427 if (!mFd)
michael@0 428 return NS_ERROR_FAILURE;
michael@0 429
michael@0 430 // the npn list is a concatenated list of 8 bit byte strings.
michael@0 431 nsCString npnList;
michael@0 432
michael@0 433 for (uint32_t index = 0; index < protocolArray.Length(); ++index) {
michael@0 434 if (protocolArray[index].IsEmpty() ||
michael@0 435 protocolArray[index].Length() > 255)
michael@0 436 return NS_ERROR_ILLEGAL_VALUE;
michael@0 437
michael@0 438 npnList.Append(protocolArray[index].Length());
michael@0 439 npnList.Append(protocolArray[index]);
michael@0 440 }
michael@0 441
michael@0 442 if (SSL_SetNextProtoNego(
michael@0 443 mFd,
michael@0 444 reinterpret_cast<const unsigned char*>(npnList.get()),
michael@0 445 npnList.Length()) != SECSuccess)
michael@0 446 return NS_ERROR_FAILURE;
michael@0 447
michael@0 448 return NS_OK;
michael@0 449 }
michael@0 450
michael@0 451 nsresult
michael@0 452 nsNSSSocketInfo::ActivateSSL()
michael@0 453 {
michael@0 454 nsNSSShutDownPreventionLock locker;
michael@0 455 if (isAlreadyShutDown())
michael@0 456 return NS_ERROR_NOT_AVAILABLE;
michael@0 457
michael@0 458 if (SECSuccess != SSL_OptionSet(mFd, SSL_SECURITY, true))
michael@0 459 return NS_ERROR_FAILURE;
michael@0 460 if (SECSuccess != SSL_ResetHandshake(mFd, false))
michael@0 461 return NS_ERROR_FAILURE;
michael@0 462
michael@0 463 mHandshakePending = true;
michael@0 464
michael@0 465 return NS_OK;
michael@0 466 }
michael@0 467
michael@0 468 nsresult
michael@0 469 nsNSSSocketInfo::GetFileDescPtr(PRFileDesc** aFilePtr)
michael@0 470 {
michael@0 471 *aFilePtr = mFd;
michael@0 472 return NS_OK;
michael@0 473 }
michael@0 474
michael@0 475 nsresult
michael@0 476 nsNSSSocketInfo::SetFileDescPtr(PRFileDesc* aFilePtr)
michael@0 477 {
michael@0 478 mFd = aFilePtr;
michael@0 479 return NS_OK;
michael@0 480 }
michael@0 481
michael@0 482 #ifndef MOZ_NO_EV_CERTS
michael@0 483 class PreviousCertRunnable : public SyncRunnableBase
michael@0 484 {
michael@0 485 public:
michael@0 486 PreviousCertRunnable(nsIInterfaceRequestor* callbacks)
michael@0 487 : mCallbacks(callbacks)
michael@0 488 {
michael@0 489 }
michael@0 490
michael@0 491 virtual void RunOnTargetThread()
michael@0 492 {
michael@0 493 nsCOMPtr<nsISecureBrowserUI> secureUI;
michael@0 494 getSecureBrowserUI(mCallbacks, getter_AddRefs(secureUI));
michael@0 495 nsCOMPtr<nsISSLStatusProvider> statusProvider = do_QueryInterface(secureUI);
michael@0 496 if (statusProvider) {
michael@0 497 nsCOMPtr<nsISSLStatus> status;
michael@0 498 (void) statusProvider->GetSSLStatus(getter_AddRefs(status));
michael@0 499 if (status) {
michael@0 500 (void) status->GetServerCert(getter_AddRefs(mPreviousCert));
michael@0 501 }
michael@0 502 }
michael@0 503 }
michael@0 504
michael@0 505 nsCOMPtr<nsIX509Cert> mPreviousCert; // out
michael@0 506 private:
michael@0 507 nsCOMPtr<nsIInterfaceRequestor> mCallbacks; // in
michael@0 508 };
michael@0 509 #endif
michael@0 510
michael@0 511 void
michael@0 512 nsNSSSocketInfo::GetPreviousCert(nsIX509Cert** _result)
michael@0 513 {
michael@0 514 NS_ASSERTION(_result, "_result parameter to GetPreviousCert is null");
michael@0 515 *_result = nullptr;
michael@0 516
michael@0 517 #ifndef MOZ_NO_EV_CERTS
michael@0 518 RefPtr<PreviousCertRunnable> runnable(new PreviousCertRunnable(mCallbacks));
michael@0 519 DebugOnly<nsresult> rv = runnable->DispatchToMainThreadAndWait();
michael@0 520 NS_ASSERTION(NS_SUCCEEDED(rv), "runnable->DispatchToMainThreadAndWait() failed");
michael@0 521 runnable->mPreviousCert.forget(_result);
michael@0 522 #endif
michael@0 523 }
michael@0 524
michael@0 525 void
michael@0 526 nsNSSSocketInfo::SetCertVerificationWaiting()
michael@0 527 {
michael@0 528 // mCertVerificationState may be before_cert_verification for the first
michael@0 529 // handshake on the connection, or after_cert_verification for subsequent
michael@0 530 // renegotiation handshakes.
michael@0 531 NS_ASSERTION(mCertVerificationState != waiting_for_cert_verification,
michael@0 532 "Invalid state transition to waiting_for_cert_verification");
michael@0 533 mCertVerificationState = waiting_for_cert_verification;
michael@0 534 }
michael@0 535
michael@0 536 // Be careful that SetCertVerificationResult does NOT get called while we are
michael@0 537 // processing a SSL callback function, because SSL_AuthCertificateComplete will
michael@0 538 // attempt to acquire locks that are already held by libssl when it calls
michael@0 539 // callbacks.
michael@0 540 void
michael@0 541 nsNSSSocketInfo::SetCertVerificationResult(PRErrorCode errorCode,
michael@0 542 SSLErrorMessageType errorMessageType)
michael@0 543 {
michael@0 544 NS_ASSERTION(mCertVerificationState == waiting_for_cert_verification,
michael@0 545 "Invalid state transition to cert_verification_finished");
michael@0 546
michael@0 547 if (mFd) {
michael@0 548 SECStatus rv = SSL_AuthCertificateComplete(mFd, errorCode);
michael@0 549 // Only replace errorCode if there was originally no error
michael@0 550 if (rv != SECSuccess && errorCode == 0) {
michael@0 551 errorCode = PR_GetError();
michael@0 552 errorMessageType = PlainErrorMessage;
michael@0 553 if (errorCode == 0) {
michael@0 554 NS_ERROR("SSL_AuthCertificateComplete didn't set error code");
michael@0 555 errorCode = PR_INVALID_STATE_ERROR;
michael@0 556 }
michael@0 557 }
michael@0 558 }
michael@0 559
michael@0 560 if (errorCode) {
michael@0 561 SetCanceled(errorCode, errorMessageType);
michael@0 562 }
michael@0 563
michael@0 564 if (mPlaintextBytesRead && !errorCode) {
michael@0 565 Telemetry::Accumulate(Telemetry::SSL_BYTES_BEFORE_CERT_CALLBACK,
michael@0 566 SafeCast<uint32_t>(mPlaintextBytesRead));
michael@0 567 }
michael@0 568
michael@0 569 mCertVerificationState = after_cert_verification;
michael@0 570 }
michael@0 571
michael@0 572 SharedSSLState&
michael@0 573 nsNSSSocketInfo::SharedState()
michael@0 574 {
michael@0 575 return mSharedState;
michael@0 576 }
michael@0 577
michael@0 578 void nsSSLIOLayerHelpers::Cleanup()
michael@0 579 {
michael@0 580 mTLSIntoleranceInfo.Clear();
michael@0 581
michael@0 582 if (mRenegoUnrestrictedSites) {
michael@0 583 delete mRenegoUnrestrictedSites;
michael@0 584 mRenegoUnrestrictedSites = nullptr;
michael@0 585 }
michael@0 586 }
michael@0 587
michael@0 588 static void
michael@0 589 nsHandleSSLError(nsNSSSocketInfo* socketInfo,
michael@0 590 ::mozilla::psm::SSLErrorMessageType errtype,
michael@0 591 PRErrorCode err)
michael@0 592 {
michael@0 593 if (!NS_IsMainThread()) {
michael@0 594 NS_ERROR("nsHandleSSLError called off the main thread");
michael@0 595 return;
michael@0 596 }
michael@0 597
michael@0 598 // SetCanceled is only called by the main thread or the socket transport
michael@0 599 // thread. Whenever this function is called on the main thread, the SSL
michael@0 600 // thread is blocked on it. So, no mutex is necessary for
michael@0 601 // SetCanceled()/GetError*().
michael@0 602 if (socketInfo->GetErrorCode()) {
michael@0 603 // If the socket has been flagged as canceled,
michael@0 604 // the code who did was responsible for setting the error code.
michael@0 605 return;
michael@0 606 }
michael@0 607
michael@0 608 nsresult rv;
michael@0 609 NS_DEFINE_CID(nssComponentCID, NS_NSSCOMPONENT_CID);
michael@0 610 nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(nssComponentCID, &rv));
michael@0 611 if (NS_FAILED(rv))
michael@0 612 return;
michael@0 613
michael@0 614 // Try to get a nsISSLErrorListener implementation from the socket consumer.
michael@0 615 nsCOMPtr<nsIInterfaceRequestor> cb;
michael@0 616 socketInfo->GetNotificationCallbacks(getter_AddRefs(cb));
michael@0 617 if (cb) {
michael@0 618 nsCOMPtr<nsISSLErrorListener> sel = do_GetInterface(cb);
michael@0 619 if (sel) {
michael@0 620 nsIInterfaceRequestor* csi = static_cast<nsIInterfaceRequestor*>(socketInfo);
michael@0 621
michael@0 622 nsCString hostWithPortString;
michael@0 623 getSiteKey(socketInfo->GetHostName(), socketInfo->GetPort(),
michael@0 624 hostWithPortString);
michael@0 625
michael@0 626 bool suppressMessage = false; // obsolete, ignored
michael@0 627 rv = sel->NotifySSLError(csi, err, hostWithPortString, &suppressMessage);
michael@0 628 }
michael@0 629 }
michael@0 630
michael@0 631 // We must cancel first, which sets the error code.
michael@0 632 socketInfo->SetCanceled(err, PlainErrorMessage);
michael@0 633 nsXPIDLString errorString;
michael@0 634 socketInfo->GetErrorLogMessage(err, errtype, errorString);
michael@0 635
michael@0 636 if (!errorString.IsEmpty()) {
michael@0 637 nsContentUtils::LogSimpleConsoleError(errorString, "SSL");
michael@0 638 }
michael@0 639 }
michael@0 640
michael@0 641 namespace {
michael@0 642
michael@0 643 enum Operation { reading, writing, not_reading_or_writing };
michael@0 644
michael@0 645 int32_t checkHandshake(int32_t bytesTransfered, bool wasReading,
michael@0 646 PRFileDesc* ssl_layer_fd,
michael@0 647 nsNSSSocketInfo* socketInfo);
michael@0 648
michael@0 649 nsNSSSocketInfo*
michael@0 650 getSocketInfoIfRunning(PRFileDesc* fd, Operation op,
michael@0 651 const nsNSSShutDownPreventionLock& /*proofOfLock*/)
michael@0 652 {
michael@0 653 if (!fd || !fd->lower || !fd->secret ||
michael@0 654 fd->identity != nsSSLIOLayerHelpers::nsSSLIOLayerIdentity) {
michael@0 655 NS_ERROR("bad file descriptor passed to getSocketInfoIfRunning");
michael@0 656 PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0);
michael@0 657 return nullptr;
michael@0 658 }
michael@0 659
michael@0 660 nsNSSSocketInfo* socketInfo = (nsNSSSocketInfo*) fd->secret;
michael@0 661
michael@0 662 if (socketInfo->isAlreadyShutDown() || socketInfo->isPK11LoggedOut()) {
michael@0 663 PR_SetError(PR_SOCKET_SHUTDOWN_ERROR, 0);
michael@0 664 return nullptr;
michael@0 665 }
michael@0 666
michael@0 667 if (socketInfo->GetErrorCode()) {
michael@0 668 PRErrorCode err = socketInfo->GetErrorCode();
michael@0 669 PR_SetError(err, 0);
michael@0 670 if (op == reading || op == writing) {
michael@0 671 // We must do TLS intolerance checks for reads and writes, for timeouts
michael@0 672 // in particular.
michael@0 673 (void) checkHandshake(-1, op == reading, fd, socketInfo);
michael@0 674 }
michael@0 675
michael@0 676 // If we get here, it is probably because cert verification failed and this
michael@0 677 // is the first I/O attempt since that failure.
michael@0 678 return nullptr;
michael@0 679 }
michael@0 680
michael@0 681 return socketInfo;
michael@0 682 }
michael@0 683
michael@0 684 } // unnnamed namespace
michael@0 685
michael@0 686 static PRStatus
michael@0 687 nsSSLIOLayerConnect(PRFileDesc* fd, const PRNetAddr* addr,
michael@0 688 PRIntervalTime timeout)
michael@0 689 {
michael@0 690 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p] connecting SSL socket\n",
michael@0 691 (void*) fd));
michael@0 692 nsNSSShutDownPreventionLock locker;
michael@0 693 if (!getSocketInfoIfRunning(fd, not_reading_or_writing, locker))
michael@0 694 return PR_FAILURE;
michael@0 695
michael@0 696 PRStatus status = fd->lower->methods->connect(fd->lower, addr, timeout);
michael@0 697 if (status != PR_SUCCESS) {
michael@0 698 PR_LOG(gPIPNSSLog, PR_LOG_ERROR, ("[%p] Lower layer connect error: %d\n",
michael@0 699 (void*) fd, PR_GetError()));
michael@0 700 return status;
michael@0 701 }
michael@0 702
michael@0 703 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p] Connect\n", (void*) fd));
michael@0 704 return status;
michael@0 705 }
michael@0 706
michael@0 707 void
michael@0 708 nsSSLIOLayerHelpers::rememberTolerantAtVersion(const nsACString& hostName,
michael@0 709 int16_t port, uint16_t tolerant)
michael@0 710 {
michael@0 711 nsCString key;
michael@0 712 getSiteKey(hostName, port, key);
michael@0 713
michael@0 714 MutexAutoLock lock(mutex);
michael@0 715
michael@0 716 IntoleranceEntry entry;
michael@0 717 if (mTLSIntoleranceInfo.Get(key, &entry)) {
michael@0 718 entry.AssertInvariant();
michael@0 719 entry.tolerant = std::max(entry.tolerant, tolerant);
michael@0 720 if (entry.intolerant != 0 && entry.intolerant <= entry.tolerant) {
michael@0 721 entry.intolerant = entry.tolerant + 1;
michael@0 722 }
michael@0 723 } else {
michael@0 724 entry.tolerant = tolerant;
michael@0 725 entry.intolerant = 0;
michael@0 726 }
michael@0 727
michael@0 728 entry.AssertInvariant();
michael@0 729
michael@0 730 mTLSIntoleranceInfo.Put(key, entry);
michael@0 731 }
michael@0 732
michael@0 733 // returns true if we should retry the handshake
michael@0 734 bool
michael@0 735 nsSSLIOLayerHelpers::rememberIntolerantAtVersion(const nsACString& hostName,
michael@0 736 int16_t port,
michael@0 737 uint16_t minVersion,
michael@0 738 uint16_t intolerant)
michael@0 739 {
michael@0 740 nsCString key;
michael@0 741 getSiteKey(hostName, port, key);
michael@0 742
michael@0 743 MutexAutoLock lock(mutex);
michael@0 744
michael@0 745 if (intolerant <= minVersion) {
michael@0 746 // We can't fall back any further. Assume that intolerance isn't the issue.
michael@0 747 IntoleranceEntry entry;
michael@0 748 if (mTLSIntoleranceInfo.Get(key, &entry)) {
michael@0 749 entry.AssertInvariant();
michael@0 750 entry.intolerant = 0;
michael@0 751 entry.AssertInvariant();
michael@0 752 mTLSIntoleranceInfo.Put(key, entry);
michael@0 753 }
michael@0 754
michael@0 755 return false;
michael@0 756 }
michael@0 757
michael@0 758 IntoleranceEntry entry;
michael@0 759 if (mTLSIntoleranceInfo.Get(key, &entry)) {
michael@0 760 entry.AssertInvariant();
michael@0 761 if (intolerant <= entry.tolerant) {
michael@0 762 // We already know the server is tolerant at an equal or higher version.
michael@0 763 return false;
michael@0 764 }
michael@0 765 if ((entry.intolerant != 0 && intolerant >= entry.intolerant)) {
michael@0 766 // We already know that the server is intolerant at a lower version.
michael@0 767 return true;
michael@0 768 }
michael@0 769 } else {
michael@0 770 entry.tolerant = 0;
michael@0 771 }
michael@0 772
michael@0 773 entry.intolerant = intolerant;
michael@0 774 entry.AssertInvariant();
michael@0 775 mTLSIntoleranceInfo.Put(key, entry);
michael@0 776
michael@0 777 return true;
michael@0 778 }
michael@0 779
michael@0 780 void
michael@0 781 nsSSLIOLayerHelpers::adjustForTLSIntolerance(const nsACString& hostName,
michael@0 782 int16_t port,
michael@0 783 /*in/out*/ SSLVersionRange& range)
michael@0 784 {
michael@0 785 IntoleranceEntry entry;
michael@0 786
michael@0 787 {
michael@0 788 nsCString key;
michael@0 789 getSiteKey(hostName, port, key);
michael@0 790
michael@0 791 MutexAutoLock lock(mutex);
michael@0 792 if (!mTLSIntoleranceInfo.Get(key, &entry)) {
michael@0 793 return;
michael@0 794 }
michael@0 795 }
michael@0 796
michael@0 797 entry.AssertInvariant();
michael@0 798
michael@0 799 if (entry.intolerant != 0) {
michael@0 800 // We've tried connecting at a higher range but failed, so try at the
michael@0 801 // version we haven't tried yet, unless we have reached the minimum.
michael@0 802 if (range.min < entry.intolerant) {
michael@0 803 range.max = entry.intolerant - 1;
michael@0 804 }
michael@0 805 }
michael@0 806 }
michael@0 807
michael@0 808 bool nsSSLIOLayerHelpers::nsSSLIOLayerInitialized = false;
michael@0 809 PRDescIdentity nsSSLIOLayerHelpers::nsSSLIOLayerIdentity;
michael@0 810 PRDescIdentity nsSSLIOLayerHelpers::nsSSLPlaintextLayerIdentity;
michael@0 811 PRIOMethods nsSSLIOLayerHelpers::nsSSLIOLayerMethods;
michael@0 812 PRIOMethods nsSSLIOLayerHelpers::nsSSLPlaintextLayerMethods;
michael@0 813
michael@0 814 static PRStatus
michael@0 815 nsSSLIOLayerClose(PRFileDesc* fd)
michael@0 816 {
michael@0 817 nsNSSShutDownPreventionLock locker;
michael@0 818 if (!fd)
michael@0 819 return PR_FAILURE;
michael@0 820
michael@0 821 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p] Shutting down socket\n",
michael@0 822 (void*) fd));
michael@0 823
michael@0 824 nsNSSSocketInfo* socketInfo = (nsNSSSocketInfo*) fd->secret;
michael@0 825 NS_ASSERTION(socketInfo,"nsNSSSocketInfo was null for an fd");
michael@0 826
michael@0 827 return socketInfo->CloseSocketAndDestroy(locker);
michael@0 828 }
michael@0 829
michael@0 830 PRStatus
michael@0 831 nsNSSSocketInfo::CloseSocketAndDestroy(
michael@0 832 const nsNSSShutDownPreventionLock& /*proofOfLock*/)
michael@0 833 {
michael@0 834 nsNSSShutDownList::trackSSLSocketClose();
michael@0 835
michael@0 836 PRFileDesc* popped = PR_PopIOLayer(mFd, PR_TOP_IO_LAYER);
michael@0 837 NS_ASSERTION(popped &&
michael@0 838 popped->identity == nsSSLIOLayerHelpers::nsSSLIOLayerIdentity,
michael@0 839 "SSL Layer not on top of stack");
michael@0 840
michael@0 841 // The plain text layer is not always present - so its not a fatal error
michael@0 842 // if it cannot be removed
michael@0 843 PRFileDesc* poppedPlaintext =
michael@0 844 PR_GetIdentitiesLayer(mFd, nsSSLIOLayerHelpers::nsSSLPlaintextLayerIdentity);
michael@0 845 if (poppedPlaintext) {
michael@0 846 PR_PopIOLayer(mFd, nsSSLIOLayerHelpers::nsSSLPlaintextLayerIdentity);
michael@0 847 poppedPlaintext->dtor(poppedPlaintext);
michael@0 848 }
michael@0 849
michael@0 850 PRStatus status = mFd->methods->close(mFd);
michael@0 851
michael@0 852 // the nsNSSSocketInfo instance can out-live the connection, so we need some
michael@0 853 // indication that the connection has been closed. mFd == nullptr is that
michael@0 854 // indication. This is needed, for example, when the connection is closed
michael@0 855 // before we have finished validating the server's certificate.
michael@0 856 mFd = nullptr;
michael@0 857
michael@0 858 if (status != PR_SUCCESS) return status;
michael@0 859
michael@0 860 popped->identity = PR_INVALID_IO_LAYER;
michael@0 861 NS_RELEASE_THIS();
michael@0 862 popped->dtor(popped);
michael@0 863
michael@0 864 return PR_SUCCESS;
michael@0 865 }
michael@0 866
michael@0 867 #if defined(DEBUG_SSL_VERBOSE) && defined(DUMP_BUFFER)
michael@0 868 // Dumps a (potentially binary) buffer using SSM_DEBUG. (We could have used
michael@0 869 // the version in ssltrace.c, but that's specifically tailored to SSLTRACE.)
michael@0 870 #define DUMPBUF_LINESIZE 24
michael@0 871 static void
michael@0 872 nsDumpBuffer(unsigned char* buf, int len)
michael@0 873 {
michael@0 874 char hexbuf[DUMPBUF_LINESIZE*3+1];
michael@0 875 char chrbuf[DUMPBUF_LINESIZE+1];
michael@0 876 static const char* hex = "0123456789abcdef";
michael@0 877 int i = 0;
michael@0 878 int l = 0;
michael@0 879 char ch;
michael@0 880 char* c;
michael@0 881 char* h;
michael@0 882 if (len == 0)
michael@0 883 return;
michael@0 884 hexbuf[DUMPBUF_LINESIZE*3] = '\0';
michael@0 885 chrbuf[DUMPBUF_LINESIZE] = '\0';
michael@0 886 (void) memset(hexbuf, 0x20, DUMPBUF_LINESIZE*3);
michael@0 887 (void) memset(chrbuf, 0x20, DUMPBUF_LINESIZE);
michael@0 888 h = hexbuf;
michael@0 889 c = chrbuf;
michael@0 890
michael@0 891 while (i < len) {
michael@0 892 ch = buf[i];
michael@0 893
michael@0 894 if (l == DUMPBUF_LINESIZE) {
michael@0 895 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("%s%s\n", hexbuf, chrbuf));
michael@0 896 (void) memset(hexbuf, 0x20, DUMPBUF_LINESIZE*3);
michael@0 897 (void) memset(chrbuf, 0x20, DUMPBUF_LINESIZE);
michael@0 898 h = hexbuf;
michael@0 899 c = chrbuf;
michael@0 900 l = 0;
michael@0 901 }
michael@0 902
michael@0 903 // Convert a character to hex.
michael@0 904 *h++ = hex[(ch >> 4) & 0xf];
michael@0 905 *h++ = hex[ch & 0xf];
michael@0 906 h++;
michael@0 907
michael@0 908 // Put the character (if it's printable) into the character buffer.
michael@0 909 if ((ch >= 0x20) && (ch <= 0x7e)) {
michael@0 910 *c++ = ch;
michael@0 911 } else {
michael@0 912 *c++ = '.';
michael@0 913 }
michael@0 914 i++; l++;
michael@0 915 }
michael@0 916 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("%s%s\n", hexbuf, chrbuf));
michael@0 917 }
michael@0 918
michael@0 919 #define DEBUG_DUMP_BUFFER(buf,len) nsDumpBuffer(buf,len)
michael@0 920 #else
michael@0 921 #define DEBUG_DUMP_BUFFER(buf,len)
michael@0 922 #endif
michael@0 923
michael@0 924 class SSLErrorRunnable : public SyncRunnableBase
michael@0 925 {
michael@0 926 public:
michael@0 927 SSLErrorRunnable(nsNSSSocketInfo* infoObject,
michael@0 928 ::mozilla::psm::SSLErrorMessageType errtype,
michael@0 929 PRErrorCode errorCode)
michael@0 930 : mInfoObject(infoObject)
michael@0 931 , mErrType(errtype)
michael@0 932 , mErrorCode(errorCode)
michael@0 933 {
michael@0 934 }
michael@0 935
michael@0 936 virtual void RunOnTargetThread()
michael@0 937 {
michael@0 938 nsHandleSSLError(mInfoObject, mErrType, mErrorCode);
michael@0 939 }
michael@0 940
michael@0 941 RefPtr<nsNSSSocketInfo> mInfoObject;
michael@0 942 ::mozilla::psm::SSLErrorMessageType mErrType;
michael@0 943 const PRErrorCode mErrorCode;
michael@0 944 };
michael@0 945
michael@0 946 namespace {
michael@0 947
michael@0 948 bool
michael@0 949 retryDueToTLSIntolerance(PRErrorCode err, nsNSSSocketInfo* socketInfo)
michael@0 950 {
michael@0 951 // This function is supposed to decide which error codes should
michael@0 952 // be used to conclude server is TLS intolerant.
michael@0 953 // Note this only happens during the initial SSL handshake.
michael@0 954
michael@0 955 SSLVersionRange range = socketInfo->GetTLSVersionRange();
michael@0 956
michael@0 957 uint32_t reason;
michael@0 958 switch (err) {
michael@0 959 case SSL_ERROR_BAD_MAC_ALERT: reason = 1; break;
michael@0 960 case SSL_ERROR_BAD_MAC_READ: reason = 2; break;
michael@0 961 case SSL_ERROR_HANDSHAKE_FAILURE_ALERT: reason = 3; break;
michael@0 962 case SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT: reason = 4; break;
michael@0 963 case SSL_ERROR_CLIENT_KEY_EXCHANGE_FAILURE: reason = 5; break;
michael@0 964 case SSL_ERROR_ILLEGAL_PARAMETER_ALERT: reason = 6; break;
michael@0 965 case SSL_ERROR_NO_CYPHER_OVERLAP: reason = 7; break;
michael@0 966 case SSL_ERROR_BAD_SERVER: reason = 8; break;
michael@0 967 case SSL_ERROR_BAD_BLOCK_PADDING: reason = 9; break;
michael@0 968 case SSL_ERROR_UNSUPPORTED_VERSION: reason = 10; break;
michael@0 969 case SSL_ERROR_PROTOCOL_VERSION_ALERT: reason = 11; break;
michael@0 970 case SSL_ERROR_RX_MALFORMED_FINISHED: reason = 12; break;
michael@0 971 case SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE: reason = 13; break;
michael@0 972 case SSL_ERROR_DECODE_ERROR_ALERT: reason = 14; break;
michael@0 973 case SSL_ERROR_RX_UNKNOWN_ALERT: reason = 15; break;
michael@0 974
michael@0 975 case PR_CONNECT_RESET_ERROR: reason = 16; goto conditional;
michael@0 976 case PR_END_OF_FILE_ERROR: reason = 17; goto conditional;
michael@0 977
michael@0 978 // When not using a proxy we'll see a connection reset error.
michael@0 979 // When using a proxy, we'll see an end of file error.
michael@0 980 // In addition check for some error codes where it is reasonable
michael@0 981 // to retry without TLS.
michael@0 982
michael@0 983 // Don't allow STARTTLS connections to fall back on connection resets or
michael@0 984 // EOF. Also, don't fall back from TLS 1.0 to SSL 3.0 for connection
michael@0 985 // resets, because connection resets have too many false positives,
michael@0 986 // and we want to maximize how often we send TLS 1.0+ with extensions
michael@0 987 // if at all reasonable. Unfortunately, it appears we have to allow
michael@0 988 // fallback from TLS 1.2 and TLS 1.1 for connection resets due to bad
michael@0 989 // servers and possibly bad intermediaries.
michael@0 990 conditional:
michael@0 991 if ((err == PR_CONNECT_RESET_ERROR &&
michael@0 992 range.max <= SSL_LIBRARY_VERSION_TLS_1_0) ||
michael@0 993 socketInfo->GetForSTARTTLS()) {
michael@0 994 return false;
michael@0 995 }
michael@0 996 break;
michael@0 997
michael@0 998 default:
michael@0 999 return false;
michael@0 1000 }
michael@0 1001
michael@0 1002 Telemetry::ID pre;
michael@0 1003 Telemetry::ID post;
michael@0 1004 switch (range.max) {
michael@0 1005 case SSL_LIBRARY_VERSION_TLS_1_2:
michael@0 1006 pre = Telemetry::SSL_TLS12_INTOLERANCE_REASON_PRE;
michael@0 1007 post = Telemetry::SSL_TLS12_INTOLERANCE_REASON_POST;
michael@0 1008 break;
michael@0 1009 case SSL_LIBRARY_VERSION_TLS_1_1:
michael@0 1010 pre = Telemetry::SSL_TLS11_INTOLERANCE_REASON_PRE;
michael@0 1011 post = Telemetry::SSL_TLS11_INTOLERANCE_REASON_POST;
michael@0 1012 break;
michael@0 1013 case SSL_LIBRARY_VERSION_TLS_1_0:
michael@0 1014 pre = Telemetry::SSL_TLS10_INTOLERANCE_REASON_PRE;
michael@0 1015 post = Telemetry::SSL_TLS10_INTOLERANCE_REASON_POST;
michael@0 1016 break;
michael@0 1017 case SSL_LIBRARY_VERSION_3_0:
michael@0 1018 pre = Telemetry::SSL_SSL30_INTOLERANCE_REASON_PRE;
michael@0 1019 post = Telemetry::SSL_SSL30_INTOLERANCE_REASON_POST;
michael@0 1020 break;
michael@0 1021 default:
michael@0 1022 MOZ_CRASH("impossible TLS version");
michael@0 1023 return false;
michael@0 1024 }
michael@0 1025
michael@0 1026 // The difference between _PRE and _POST represents how often we avoided
michael@0 1027 // TLS intolerance fallback due to remembered tolerance.
michael@0 1028 Telemetry::Accumulate(pre, reason);
michael@0 1029
michael@0 1030 if (!socketInfo->SharedState().IOLayerHelpers()
michael@0 1031 .rememberIntolerantAtVersion(socketInfo->GetHostName(),
michael@0 1032 socketInfo->GetPort(),
michael@0 1033 range.min, range.max)) {
michael@0 1034 return false;
michael@0 1035 }
michael@0 1036
michael@0 1037 Telemetry::Accumulate(post, reason);
michael@0 1038
michael@0 1039 return true;
michael@0 1040 }
michael@0 1041
michael@0 1042 int32_t
michael@0 1043 checkHandshake(int32_t bytesTransfered, bool wasReading,
michael@0 1044 PRFileDesc* ssl_layer_fd, nsNSSSocketInfo* socketInfo)
michael@0 1045 {
michael@0 1046 const PRErrorCode originalError = PR_GetError();
michael@0 1047 PRErrorCode err = originalError;
michael@0 1048
michael@0 1049 // This is where we work around all of those SSL servers that don't
michael@0 1050 // conform to the SSL spec and shutdown a connection when we request
michael@0 1051 // SSL v3.1 (aka TLS). The spec says the client says what version
michael@0 1052 // of the protocol we're willing to perform, in our case SSL v3.1
michael@0 1053 // In its response, the server says which version it wants to perform.
michael@0 1054 // Many servers out there only know how to do v3.0. Next, we're supposed
michael@0 1055 // to send back the version of the protocol we requested (ie v3.1). At
michael@0 1056 // this point many servers's implementations are broken and they shut
michael@0 1057 // down the connection when they don't see the version they sent back.
michael@0 1058 // This is supposed to prevent a man in the middle from forcing one
michael@0 1059 // side to dumb down to a lower level of the protocol. Unfortunately,
michael@0 1060 // there are enough broken servers out there that such a gross work-around
michael@0 1061 // is necessary. :(
michael@0 1062
michael@0 1063 // Do NOT assume TLS intolerance on a closed connection after bad cert ui was shown.
michael@0 1064 // Simply retry.
michael@0 1065 // This depends on the fact that Cert UI will not be shown again,
michael@0 1066 // should the user override the bad cert.
michael@0 1067
michael@0 1068 bool handleHandshakeResultNow = socketInfo->IsHandshakePending();
michael@0 1069
michael@0 1070 bool wantRetry = false;
michael@0 1071
michael@0 1072 if (0 > bytesTransfered) {
michael@0 1073 if (handleHandshakeResultNow) {
michael@0 1074 if (PR_WOULD_BLOCK_ERROR == err) {
michael@0 1075 PR_SetError(err, 0);
michael@0 1076 return bytesTransfered;
michael@0 1077 }
michael@0 1078
michael@0 1079 wantRetry = retryDueToTLSIntolerance(err, socketInfo);
michael@0 1080 }
michael@0 1081
michael@0 1082 // This is the common place where we trigger non-cert-errors on a SSL
michael@0 1083 // socket. This might be reached at any time of the connection.
michael@0 1084 //
michael@0 1085 // The socketInfo->GetErrorCode() check is here to ensure we don't try to
michael@0 1086 // do the synchronous dispatch to the main thread unnecessarily after we've
michael@0 1087 // already handled a certificate error. (SSLErrorRunnable calls
michael@0 1088 // nsHandleSSLError, which has logic to avoid replacing the error message,
michael@0 1089 // so without the !socketInfo->GetErrorCode(), it would just be an
michael@0 1090 // expensive no-op.)
michael@0 1091 if (!wantRetry && (IS_SSL_ERROR(err) || IS_SEC_ERROR(err)) &&
michael@0 1092 !socketInfo->GetErrorCode()) {
michael@0 1093 RefPtr<SyncRunnableBase> runnable(new SSLErrorRunnable(socketInfo,
michael@0 1094 PlainErrorMessage,
michael@0 1095 err));
michael@0 1096 (void) runnable->DispatchToMainThreadAndWait();
michael@0 1097 }
michael@0 1098 } else if (wasReading && 0 == bytesTransfered) {
michael@0 1099 // zero bytes on reading, socket closed
michael@0 1100 if (handleHandshakeResultNow) {
michael@0 1101 wantRetry = retryDueToTLSIntolerance(PR_END_OF_FILE_ERROR, socketInfo);
michael@0 1102 }
michael@0 1103 }
michael@0 1104
michael@0 1105 if (wantRetry) {
michael@0 1106 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
michael@0 1107 ("[%p] checkHandshake: will retry with lower max TLS version\n",
michael@0 1108 ssl_layer_fd));
michael@0 1109 // We want to cause the network layer to retry the connection.
michael@0 1110 err = PR_CONNECT_RESET_ERROR;
michael@0 1111 if (wasReading)
michael@0 1112 bytesTransfered = -1;
michael@0 1113 }
michael@0 1114
michael@0 1115 // TLS intolerant servers only cause the first transfer to fail, so let's
michael@0 1116 // set the HandshakePending attribute to false so that we don't try the logic
michael@0 1117 // above again in a subsequent transfer.
michael@0 1118 if (handleHandshakeResultNow) {
michael@0 1119 socketInfo->SetHandshakeNotPending();
michael@0 1120 }
michael@0 1121
michael@0 1122 if (bytesTransfered < 0) {
michael@0 1123 // Remember that we encountered an error so that getSocketInfoIfRunning
michael@0 1124 // will correctly cause us to fail if another part of Gecko
michael@0 1125 // (erroneously) calls an I/O function (PR_Send/PR_Recv/etc.) again on
michael@0 1126 // this socket. Note that we use the original error because if we use
michael@0 1127 // PR_CONNECT_RESET_ERROR, we'll repeated try to reconnect.
michael@0 1128 if (originalError != PR_WOULD_BLOCK_ERROR && !socketInfo->GetErrorCode()) {
michael@0 1129 socketInfo->SetCanceled(originalError, PlainErrorMessage);
michael@0 1130 }
michael@0 1131 PR_SetError(err, 0);
michael@0 1132 }
michael@0 1133
michael@0 1134 return bytesTransfered;
michael@0 1135 }
michael@0 1136
michael@0 1137 }
michael@0 1138
michael@0 1139 static int16_t
michael@0 1140 nsSSLIOLayerPoll(PRFileDesc* fd, int16_t in_flags, int16_t* out_flags)
michael@0 1141 {
michael@0 1142 nsNSSShutDownPreventionLock locker;
michael@0 1143
michael@0 1144 if (!out_flags) {
michael@0 1145 NS_WARNING("nsSSLIOLayerPoll called with null out_flags");
michael@0 1146 return 0;
michael@0 1147 }
michael@0 1148
michael@0 1149 *out_flags = 0;
michael@0 1150
michael@0 1151 nsNSSSocketInfo* socketInfo =
michael@0 1152 getSocketInfoIfRunning(fd, not_reading_or_writing, locker);
michael@0 1153
michael@0 1154 if (!socketInfo) {
michael@0 1155 // If we get here, it is probably because certificate validation failed
michael@0 1156 // and this is the first I/O operation after the failure.
michael@0 1157 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
michael@0 1158 ("[%p] polling SSL socket right after certificate verification failed "
michael@0 1159 "or NSS shutdown or SDR logout %d\n",
michael@0 1160 fd, (int) in_flags));
michael@0 1161
michael@0 1162 NS_ASSERTION(in_flags & PR_POLL_EXCEPT,
michael@0 1163 "caller did not poll for EXCEPT (canceled)");
michael@0 1164 // Since this poll method cannot return errors, we want the caller to call
michael@0 1165 // PR_Send/PR_Recv right away to get the error, so we tell that we are
michael@0 1166 // ready for whatever I/O they are asking for. (See getSocketInfoIfRunning).
michael@0 1167 *out_flags = in_flags | PR_POLL_EXCEPT; // see also bug 480619
michael@0 1168 return in_flags;
michael@0 1169 }
michael@0 1170
michael@0 1171 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
michael@0 1172 (socketInfo->IsWaitingForCertVerification()
michael@0 1173 ? "[%p] polling SSL socket during certificate verification using lower %d\n"
michael@0 1174 : "[%p] poll SSL socket using lower %d\n",
michael@0 1175 fd, (int) in_flags));
michael@0 1176
michael@0 1177 // We want the handshake to continue during certificate validation, so we
michael@0 1178 // don't need to do anything special here. libssl automatically blocks when
michael@0 1179 // it reaches any point that would be unsafe to send/receive something before
michael@0 1180 // cert validation is complete.
michael@0 1181 int16_t result = fd->lower->methods->poll(fd->lower, in_flags, out_flags);
michael@0 1182 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p] poll SSL socket returned %d\n",
michael@0 1183 (void*) fd, (int) result));
michael@0 1184 return result;
michael@0 1185 }
michael@0 1186
michael@0 1187 nsSSLIOLayerHelpers::nsSSLIOLayerHelpers()
michael@0 1188 : mRenegoUnrestrictedSites(nullptr)
michael@0 1189 , mTreatUnsafeNegotiationAsBroken(false)
michael@0 1190 , mWarnLevelMissingRFC5746(1)
michael@0 1191 , mTLSIntoleranceInfo(16)
michael@0 1192 , mFalseStartRequireNPN(true)
michael@0 1193 , mFalseStartRequireForwardSecrecy(false)
michael@0 1194 , mutex("nsSSLIOLayerHelpers.mutex")
michael@0 1195 {
michael@0 1196 }
michael@0 1197
michael@0 1198 static int
michael@0 1199 _PSM_InvalidInt(void)
michael@0 1200 {
michael@0 1201 PR_ASSERT(!"I/O method is invalid");
michael@0 1202 PR_SetError(PR_INVALID_METHOD_ERROR, 0);
michael@0 1203 return -1;
michael@0 1204 }
michael@0 1205
michael@0 1206 static int64_t
michael@0 1207 _PSM_InvalidInt64(void)
michael@0 1208 {
michael@0 1209 PR_ASSERT(!"I/O method is invalid");
michael@0 1210 PR_SetError(PR_INVALID_METHOD_ERROR, 0);
michael@0 1211 return -1;
michael@0 1212 }
michael@0 1213
michael@0 1214 static PRStatus
michael@0 1215 _PSM_InvalidStatus(void)
michael@0 1216 {
michael@0 1217 PR_ASSERT(!"I/O method is invalid");
michael@0 1218 PR_SetError(PR_INVALID_METHOD_ERROR, 0);
michael@0 1219 return PR_FAILURE;
michael@0 1220 }
michael@0 1221
michael@0 1222 static PRFileDesc*
michael@0 1223 _PSM_InvalidDesc(void)
michael@0 1224 {
michael@0 1225 PR_ASSERT(!"I/O method is invalid");
michael@0 1226 PR_SetError(PR_INVALID_METHOD_ERROR, 0);
michael@0 1227 return nullptr;
michael@0 1228 }
michael@0 1229
michael@0 1230 static PRStatus
michael@0 1231 PSMGetsockname(PRFileDesc* fd, PRNetAddr* addr)
michael@0 1232 {
michael@0 1233 nsNSSShutDownPreventionLock locker;
michael@0 1234 if (!getSocketInfoIfRunning(fd, not_reading_or_writing, locker))
michael@0 1235 return PR_FAILURE;
michael@0 1236
michael@0 1237 return fd->lower->methods->getsockname(fd->lower, addr);
michael@0 1238 }
michael@0 1239
michael@0 1240 static PRStatus
michael@0 1241 PSMGetpeername(PRFileDesc* fd, PRNetAddr* addr)
michael@0 1242 {
michael@0 1243 nsNSSShutDownPreventionLock locker;
michael@0 1244 if (!getSocketInfoIfRunning(fd, not_reading_or_writing, locker))
michael@0 1245 return PR_FAILURE;
michael@0 1246
michael@0 1247 return fd->lower->methods->getpeername(fd->lower, addr);
michael@0 1248 }
michael@0 1249
michael@0 1250 static PRStatus
michael@0 1251 PSMGetsocketoption(PRFileDesc* fd, PRSocketOptionData* data)
michael@0 1252 {
michael@0 1253 nsNSSShutDownPreventionLock locker;
michael@0 1254 if (!getSocketInfoIfRunning(fd, not_reading_or_writing, locker))
michael@0 1255 return PR_FAILURE;
michael@0 1256
michael@0 1257 return fd->lower->methods->getsocketoption(fd, data);
michael@0 1258 }
michael@0 1259
michael@0 1260 static PRStatus
michael@0 1261 PSMSetsocketoption(PRFileDesc* fd, const PRSocketOptionData* data)
michael@0 1262 {
michael@0 1263 nsNSSShutDownPreventionLock locker;
michael@0 1264 if (!getSocketInfoIfRunning(fd, not_reading_or_writing, locker))
michael@0 1265 return PR_FAILURE;
michael@0 1266
michael@0 1267 return fd->lower->methods->setsocketoption(fd, data);
michael@0 1268 }
michael@0 1269
michael@0 1270 static int32_t
michael@0 1271 PSMRecv(PRFileDesc* fd, void* buf, int32_t amount, int flags,
michael@0 1272 PRIntervalTime timeout)
michael@0 1273 {
michael@0 1274 nsNSSShutDownPreventionLock locker;
michael@0 1275 nsNSSSocketInfo* socketInfo = getSocketInfoIfRunning(fd, reading, locker);
michael@0 1276 if (!socketInfo)
michael@0 1277 return -1;
michael@0 1278
michael@0 1279 if (flags != PR_MSG_PEEK && flags != 0) {
michael@0 1280 PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
michael@0 1281 return -1;
michael@0 1282 }
michael@0 1283
michael@0 1284 int32_t bytesRead = fd->lower->methods->recv(fd->lower, buf, amount, flags,
michael@0 1285 timeout);
michael@0 1286
michael@0 1287 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p] read %d bytes\n", (void*) fd,
michael@0 1288 bytesRead));
michael@0 1289
michael@0 1290 #ifdef DEBUG_SSL_VERBOSE
michael@0 1291 DEBUG_DUMP_BUFFER((unsigned char*) buf, bytesRead);
michael@0 1292 #endif
michael@0 1293
michael@0 1294 return checkHandshake(bytesRead, true, fd, socketInfo);
michael@0 1295 }
michael@0 1296
michael@0 1297 static int32_t
michael@0 1298 PSMSend(PRFileDesc* fd, const void* buf, int32_t amount, int flags,
michael@0 1299 PRIntervalTime timeout)
michael@0 1300 {
michael@0 1301 nsNSSShutDownPreventionLock locker;
michael@0 1302 nsNSSSocketInfo* socketInfo = getSocketInfoIfRunning(fd, writing, locker);
michael@0 1303 if (!socketInfo)
michael@0 1304 return -1;
michael@0 1305
michael@0 1306 if (flags != 0) {
michael@0 1307 PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
michael@0 1308 return -1;
michael@0 1309 }
michael@0 1310
michael@0 1311 #ifdef DEBUG_SSL_VERBOSE
michael@0 1312 DEBUG_DUMP_BUFFER((unsigned char*) buf, amount);
michael@0 1313 #endif
michael@0 1314
michael@0 1315 int32_t bytesWritten = fd->lower->methods->send(fd->lower, buf, amount,
michael@0 1316 flags, timeout);
michael@0 1317
michael@0 1318 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p] wrote %d bytes\n",
michael@0 1319 fd, bytesWritten));
michael@0 1320
michael@0 1321 return checkHandshake(bytesWritten, false, fd, socketInfo);
michael@0 1322 }
michael@0 1323
michael@0 1324 static int32_t
michael@0 1325 nsSSLIOLayerRead(PRFileDesc* fd, void* buf, int32_t amount)
michael@0 1326 {
michael@0 1327 return PSMRecv(fd, buf, amount, 0, PR_INTERVAL_NO_TIMEOUT);
michael@0 1328 }
michael@0 1329
michael@0 1330 static int32_t
michael@0 1331 nsSSLIOLayerWrite(PRFileDesc* fd, const void* buf, int32_t amount)
michael@0 1332 {
michael@0 1333 return PSMSend(fd, buf, amount, 0, PR_INTERVAL_NO_TIMEOUT);
michael@0 1334 }
michael@0 1335
michael@0 1336 static PRStatus
michael@0 1337 PSMConnectcontinue(PRFileDesc* fd, int16_t out_flags)
michael@0 1338 {
michael@0 1339 nsNSSShutDownPreventionLock locker;
michael@0 1340 if (!getSocketInfoIfRunning(fd, not_reading_or_writing, locker)) {
michael@0 1341 return PR_FAILURE;
michael@0 1342 }
michael@0 1343
michael@0 1344 return fd->lower->methods->connectcontinue(fd, out_flags);
michael@0 1345 }
michael@0 1346
michael@0 1347 static int
michael@0 1348 PSMAvailable(void)
michael@0 1349 {
michael@0 1350 // This is called through PR_Available(), but is not implemented in PSM
michael@0 1351 PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
michael@0 1352 return -1;
michael@0 1353 }
michael@0 1354
michael@0 1355 static int64_t
michael@0 1356 PSMAvailable64(void)
michael@0 1357 {
michael@0 1358 // This is called through PR_Available(), but is not implemented in PSM
michael@0 1359 PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
michael@0 1360 return -1;
michael@0 1361 }
michael@0 1362
michael@0 1363 namespace {
michael@0 1364
michael@0 1365 class PrefObserver : public nsIObserver {
michael@0 1366 public:
michael@0 1367 NS_DECL_THREADSAFE_ISUPPORTS
michael@0 1368 NS_DECL_NSIOBSERVER
michael@0 1369 PrefObserver(nsSSLIOLayerHelpers* aOwner) : mOwner(aOwner) {}
michael@0 1370 virtual ~PrefObserver() {}
michael@0 1371 private:
michael@0 1372 nsSSLIOLayerHelpers* mOwner;
michael@0 1373 };
michael@0 1374
michael@0 1375 } // unnamed namespace
michael@0 1376
michael@0 1377 NS_IMPL_ISUPPORTS(PrefObserver, nsIObserver)
michael@0 1378
michael@0 1379 NS_IMETHODIMP
michael@0 1380 PrefObserver::Observe(nsISupports* aSubject, const char* aTopic,
michael@0 1381 const char16_t* someData)
michael@0 1382 {
michael@0 1383 if (nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0) {
michael@0 1384 NS_ConvertUTF16toUTF8 prefName(someData);
michael@0 1385
michael@0 1386 if (prefName.Equals("security.ssl.renego_unrestricted_hosts")) {
michael@0 1387 nsCString unrestricted_hosts;
michael@0 1388 Preferences::GetCString("security.ssl.renego_unrestricted_hosts", &unrestricted_hosts);
michael@0 1389 if (!unrestricted_hosts.IsEmpty()) {
michael@0 1390 mOwner->setRenegoUnrestrictedSites(unrestricted_hosts);
michael@0 1391 }
michael@0 1392 } else if (prefName.Equals("security.ssl.treat_unsafe_negotiation_as_broken")) {
michael@0 1393 bool enabled;
michael@0 1394 Preferences::GetBool("security.ssl.treat_unsafe_negotiation_as_broken", &enabled);
michael@0 1395 mOwner->setTreatUnsafeNegotiationAsBroken(enabled);
michael@0 1396 } else if (prefName.Equals("security.ssl.warn_missing_rfc5746")) {
michael@0 1397 int32_t warnLevel = 1;
michael@0 1398 Preferences::GetInt("security.ssl.warn_missing_rfc5746", &warnLevel);
michael@0 1399 mOwner->setWarnLevelMissingRFC5746(warnLevel);
michael@0 1400 } else if (prefName.Equals("security.ssl.false_start.require-npn")) {
michael@0 1401 mOwner->mFalseStartRequireNPN =
michael@0 1402 Preferences::GetBool("security.ssl.false_start.require-npn",
michael@0 1403 FALSE_START_REQUIRE_NPN_DEFAULT);
michael@0 1404 } else if (prefName.Equals("security.ssl.false_start.require-forward-secrecy")) {
michael@0 1405 mOwner->mFalseStartRequireForwardSecrecy =
michael@0 1406 Preferences::GetBool("security.ssl.false_start.require-forward-secrecy",
michael@0 1407 FALSE_START_REQUIRE_FORWARD_SECRECY_DEFAULT);
michael@0 1408 }
michael@0 1409 }
michael@0 1410 return NS_OK;
michael@0 1411 }
michael@0 1412
michael@0 1413 static int32_t
michael@0 1414 PlaintextRecv(PRFileDesc* fd, void* buf, int32_t amount, int flags,
michael@0 1415 PRIntervalTime timeout)
michael@0 1416 {
michael@0 1417 // The shutdownlocker is not needed here because it will already be
michael@0 1418 // held higher in the stack
michael@0 1419 nsNSSSocketInfo* socketInfo = nullptr;
michael@0 1420
michael@0 1421 int32_t bytesRead = fd->lower->methods->recv(fd->lower, buf, amount, flags,
michael@0 1422 timeout);
michael@0 1423 if (fd->identity == nsSSLIOLayerHelpers::nsSSLPlaintextLayerIdentity)
michael@0 1424 socketInfo = (nsNSSSocketInfo*) fd->secret;
michael@0 1425
michael@0 1426 if ((bytesRead > 0) && socketInfo)
michael@0 1427 socketInfo->AddPlaintextBytesRead(bytesRead);
michael@0 1428 return bytesRead;
michael@0 1429 }
michael@0 1430
michael@0 1431 nsSSLIOLayerHelpers::~nsSSLIOLayerHelpers()
michael@0 1432 {
michael@0 1433 // mPrefObserver will only be set if this->Init was called. The GTest tests
michael@0 1434 // do not call Init.
michael@0 1435 if (mPrefObserver) {
michael@0 1436 Preferences::RemoveObserver(mPrefObserver,
michael@0 1437 "security.ssl.renego_unrestricted_hosts");
michael@0 1438 Preferences::RemoveObserver(mPrefObserver,
michael@0 1439 "security.ssl.treat_unsafe_negotiation_as_broken");
michael@0 1440 Preferences::RemoveObserver(mPrefObserver,
michael@0 1441 "security.ssl.warn_missing_rfc5746");
michael@0 1442 Preferences::RemoveObserver(mPrefObserver,
michael@0 1443 "security.ssl.false_start.require-npn");
michael@0 1444 Preferences::RemoveObserver(mPrefObserver,
michael@0 1445 "security.ssl.false_start.require-forward-secrecy");
michael@0 1446 }
michael@0 1447 }
michael@0 1448
michael@0 1449 nsresult
michael@0 1450 nsSSLIOLayerHelpers::Init()
michael@0 1451 {
michael@0 1452 if (!nsSSLIOLayerInitialized) {
michael@0 1453 nsSSLIOLayerInitialized = true;
michael@0 1454 nsSSLIOLayerIdentity = PR_GetUniqueIdentity("NSS layer");
michael@0 1455 nsSSLIOLayerMethods = *PR_GetDefaultIOMethods();
michael@0 1456
michael@0 1457 nsSSLIOLayerMethods.available = (PRAvailableFN) PSMAvailable;
michael@0 1458 nsSSLIOLayerMethods.available64 = (PRAvailable64FN) PSMAvailable64;
michael@0 1459 nsSSLIOLayerMethods.fsync = (PRFsyncFN) _PSM_InvalidStatus;
michael@0 1460 nsSSLIOLayerMethods.seek = (PRSeekFN) _PSM_InvalidInt;
michael@0 1461 nsSSLIOLayerMethods.seek64 = (PRSeek64FN) _PSM_InvalidInt64;
michael@0 1462 nsSSLIOLayerMethods.fileInfo = (PRFileInfoFN) _PSM_InvalidStatus;
michael@0 1463 nsSSLIOLayerMethods.fileInfo64 = (PRFileInfo64FN) _PSM_InvalidStatus;
michael@0 1464 nsSSLIOLayerMethods.writev = (PRWritevFN) _PSM_InvalidInt;
michael@0 1465 nsSSLIOLayerMethods.accept = (PRAcceptFN) _PSM_InvalidDesc;
michael@0 1466 nsSSLIOLayerMethods.bind = (PRBindFN) _PSM_InvalidStatus;
michael@0 1467 nsSSLIOLayerMethods.listen = (PRListenFN) _PSM_InvalidStatus;
michael@0 1468 nsSSLIOLayerMethods.shutdown = (PRShutdownFN) _PSM_InvalidStatus;
michael@0 1469 nsSSLIOLayerMethods.recvfrom = (PRRecvfromFN) _PSM_InvalidInt;
michael@0 1470 nsSSLIOLayerMethods.sendto = (PRSendtoFN) _PSM_InvalidInt;
michael@0 1471 nsSSLIOLayerMethods.acceptread = (PRAcceptreadFN) _PSM_InvalidInt;
michael@0 1472 nsSSLIOLayerMethods.transmitfile = (PRTransmitfileFN) _PSM_InvalidInt;
michael@0 1473 nsSSLIOLayerMethods.sendfile = (PRSendfileFN) _PSM_InvalidInt;
michael@0 1474
michael@0 1475 nsSSLIOLayerMethods.getsockname = PSMGetsockname;
michael@0 1476 nsSSLIOLayerMethods.getpeername = PSMGetpeername;
michael@0 1477 nsSSLIOLayerMethods.getsocketoption = PSMGetsocketoption;
michael@0 1478 nsSSLIOLayerMethods.setsocketoption = PSMSetsocketoption;
michael@0 1479 nsSSLIOLayerMethods.recv = PSMRecv;
michael@0 1480 nsSSLIOLayerMethods.send = PSMSend;
michael@0 1481 nsSSLIOLayerMethods.connectcontinue = PSMConnectcontinue;
michael@0 1482
michael@0 1483 nsSSLIOLayerMethods.connect = nsSSLIOLayerConnect;
michael@0 1484 nsSSLIOLayerMethods.close = nsSSLIOLayerClose;
michael@0 1485 nsSSLIOLayerMethods.write = nsSSLIOLayerWrite;
michael@0 1486 nsSSLIOLayerMethods.read = nsSSLIOLayerRead;
michael@0 1487 nsSSLIOLayerMethods.poll = nsSSLIOLayerPoll;
michael@0 1488
michael@0 1489 nsSSLPlaintextLayerIdentity = PR_GetUniqueIdentity("Plaintxext PSM layer");
michael@0 1490 nsSSLPlaintextLayerMethods = *PR_GetDefaultIOMethods();
michael@0 1491 nsSSLPlaintextLayerMethods.recv = PlaintextRecv;
michael@0 1492 }
michael@0 1493
michael@0 1494 mRenegoUnrestrictedSites = new nsTHashtable<nsCStringHashKey>(16);
michael@0 1495
michael@0 1496 nsCString unrestricted_hosts;
michael@0 1497 Preferences::GetCString("security.ssl.renego_unrestricted_hosts", &unrestricted_hosts);
michael@0 1498 if (!unrestricted_hosts.IsEmpty()) {
michael@0 1499 setRenegoUnrestrictedSites(unrestricted_hosts);
michael@0 1500 }
michael@0 1501
michael@0 1502 bool enabled = false;
michael@0 1503 Preferences::GetBool("security.ssl.treat_unsafe_negotiation_as_broken", &enabled);
michael@0 1504 setTreatUnsafeNegotiationAsBroken(enabled);
michael@0 1505
michael@0 1506 int32_t warnLevel = 1;
michael@0 1507 Preferences::GetInt("security.ssl.warn_missing_rfc5746", &warnLevel);
michael@0 1508 setWarnLevelMissingRFC5746(warnLevel);
michael@0 1509
michael@0 1510 mFalseStartRequireNPN =
michael@0 1511 Preferences::GetBool("security.ssl.false_start.require-npn",
michael@0 1512 FALSE_START_REQUIRE_NPN_DEFAULT);
michael@0 1513 mFalseStartRequireForwardSecrecy =
michael@0 1514 Preferences::GetBool("security.ssl.false_start.require-forward-secrecy",
michael@0 1515 FALSE_START_REQUIRE_FORWARD_SECRECY_DEFAULT);
michael@0 1516
michael@0 1517 mPrefObserver = new PrefObserver(this);
michael@0 1518 Preferences::AddStrongObserver(mPrefObserver,
michael@0 1519 "security.ssl.renego_unrestricted_hosts");
michael@0 1520 Preferences::AddStrongObserver(mPrefObserver,
michael@0 1521 "security.ssl.treat_unsafe_negotiation_as_broken");
michael@0 1522 Preferences::AddStrongObserver(mPrefObserver,
michael@0 1523 "security.ssl.warn_missing_rfc5746");
michael@0 1524 Preferences::AddStrongObserver(mPrefObserver,
michael@0 1525 "security.ssl.false_start.require-npn");
michael@0 1526 Preferences::AddStrongObserver(mPrefObserver,
michael@0 1527 "security.ssl.false_start.require-forward-secrecy");
michael@0 1528 return NS_OK;
michael@0 1529 }
michael@0 1530
michael@0 1531 void
michael@0 1532 nsSSLIOLayerHelpers::clearStoredData()
michael@0 1533 {
michael@0 1534 mRenegoUnrestrictedSites->Clear();
michael@0 1535 mTLSIntoleranceInfo.Clear();
michael@0 1536 }
michael@0 1537
michael@0 1538 void
michael@0 1539 nsSSLIOLayerHelpers::setRenegoUnrestrictedSites(const nsCString& str)
michael@0 1540 {
michael@0 1541 MutexAutoLock lock(mutex);
michael@0 1542
michael@0 1543 if (mRenegoUnrestrictedSites) {
michael@0 1544 delete mRenegoUnrestrictedSites;
michael@0 1545 mRenegoUnrestrictedSites = nullptr;
michael@0 1546 }
michael@0 1547
michael@0 1548 mRenegoUnrestrictedSites = new nsTHashtable<nsCStringHashKey>();
michael@0 1549 if (!mRenegoUnrestrictedSites)
michael@0 1550 return;
michael@0 1551
michael@0 1552 nsCCharSeparatedTokenizer toker(str, ',');
michael@0 1553
michael@0 1554 while (toker.hasMoreTokens()) {
michael@0 1555 const nsCSubstring& host = toker.nextToken();
michael@0 1556 if (!host.IsEmpty()) {
michael@0 1557 mRenegoUnrestrictedSites->PutEntry(host);
michael@0 1558 }
michael@0 1559 }
michael@0 1560 }
michael@0 1561
michael@0 1562 bool
michael@0 1563 nsSSLIOLayerHelpers::isRenegoUnrestrictedSite(const nsCString& str)
michael@0 1564 {
michael@0 1565 MutexAutoLock lock(mutex);
michael@0 1566 return mRenegoUnrestrictedSites->Contains(str);
michael@0 1567 }
michael@0 1568
michael@0 1569 void
michael@0 1570 nsSSLIOLayerHelpers::setTreatUnsafeNegotiationAsBroken(bool broken)
michael@0 1571 {
michael@0 1572 MutexAutoLock lock(mutex);
michael@0 1573 mTreatUnsafeNegotiationAsBroken = broken;
michael@0 1574 }
michael@0 1575
michael@0 1576 bool
michael@0 1577 nsSSLIOLayerHelpers::treatUnsafeNegotiationAsBroken()
michael@0 1578 {
michael@0 1579 MutexAutoLock lock(mutex);
michael@0 1580 return mTreatUnsafeNegotiationAsBroken;
michael@0 1581 }
michael@0 1582
michael@0 1583 void
michael@0 1584 nsSSLIOLayerHelpers::setWarnLevelMissingRFC5746(int32_t level)
michael@0 1585 {
michael@0 1586 MutexAutoLock lock(mutex);
michael@0 1587 mWarnLevelMissingRFC5746 = level;
michael@0 1588 }
michael@0 1589
michael@0 1590 int32_t
michael@0 1591 nsSSLIOLayerHelpers::getWarnLevelMissingRFC5746()
michael@0 1592 {
michael@0 1593 MutexAutoLock lock(mutex);
michael@0 1594 return mWarnLevelMissingRFC5746;
michael@0 1595 }
michael@0 1596
michael@0 1597 nsresult
michael@0 1598 nsSSLIOLayerNewSocket(int32_t family,
michael@0 1599 const char* host,
michael@0 1600 int32_t port,
michael@0 1601 nsIProxyInfo *proxy,
michael@0 1602 PRFileDesc** fd,
michael@0 1603 nsISupports** info,
michael@0 1604 bool forSTARTTLS,
michael@0 1605 uint32_t flags)
michael@0 1606 {
michael@0 1607
michael@0 1608 PRFileDesc* sock = PR_OpenTCPSocket(family);
michael@0 1609 if (!sock) return NS_ERROR_OUT_OF_MEMORY;
michael@0 1610
michael@0 1611 nsresult rv = nsSSLIOLayerAddToSocket(family, host, port, proxy,
michael@0 1612 sock, info, forSTARTTLS, flags);
michael@0 1613 if (NS_FAILED(rv)) {
michael@0 1614 PR_Close(sock);
michael@0 1615 return rv;
michael@0 1616 }
michael@0 1617
michael@0 1618 *fd = sock;
michael@0 1619 return NS_OK;
michael@0 1620 }
michael@0 1621
michael@0 1622 // Creates CA names strings from (CERTDistNames* caNames)
michael@0 1623 //
michael@0 1624 // - arena: arena to allocate strings on
michael@0 1625 // - caNameStrings: filled with CA names strings on return
michael@0 1626 // - caNames: CERTDistNames to extract strings from
michael@0 1627 // - return: SECSuccess if successful; error code otherwise
michael@0 1628 //
michael@0 1629 // Note: copied in its entirety from Nova code
michael@0 1630 static SECStatus
michael@0 1631 nsConvertCANamesToStrings(PLArenaPool* arena, char** caNameStrings,
michael@0 1632 CERTDistNames* caNames)
michael@0 1633 {
michael@0 1634 SECItem* dername;
michael@0 1635 SECStatus rv;
michael@0 1636 int headerlen;
michael@0 1637 uint32_t contentlen;
michael@0 1638 SECItem newitem;
michael@0 1639 int n;
michael@0 1640 char* namestring;
michael@0 1641
michael@0 1642 for (n = 0; n < caNames->nnames; n++) {
michael@0 1643 newitem.data = nullptr;
michael@0 1644 dername = &caNames->names[n];
michael@0 1645
michael@0 1646 rv = DER_Lengths(dername, &headerlen, &contentlen);
michael@0 1647
michael@0 1648 if (rv != SECSuccess) {
michael@0 1649 goto loser;
michael@0 1650 }
michael@0 1651
michael@0 1652 if (headerlen + contentlen != dername->len) {
michael@0 1653 // This must be from an enterprise 2.x server, which sent
michael@0 1654 // incorrectly formatted der without the outer wrapper of type and
michael@0 1655 // length. Fix it up by adding the top level header.
michael@0 1656 if (dername->len <= 127) {
michael@0 1657 newitem.data = (unsigned char*) PR_Malloc(dername->len + 2);
michael@0 1658 if (!newitem.data) {
michael@0 1659 goto loser;
michael@0 1660 }
michael@0 1661 newitem.data[0] = (unsigned char) 0x30;
michael@0 1662 newitem.data[1] = (unsigned char) dername->len;
michael@0 1663 (void) memcpy(&newitem.data[2], dername->data, dername->len);
michael@0 1664 } else if (dername->len <= 255) {
michael@0 1665 newitem.data = (unsigned char*) PR_Malloc(dername->len + 3);
michael@0 1666 if (!newitem.data) {
michael@0 1667 goto loser;
michael@0 1668 }
michael@0 1669 newitem.data[0] = (unsigned char) 0x30;
michael@0 1670 newitem.data[1] = (unsigned char) 0x81;
michael@0 1671 newitem.data[2] = (unsigned char) dername->len;
michael@0 1672 (void) memcpy(&newitem.data[3], dername->data, dername->len);
michael@0 1673 } else {
michael@0 1674 // greater than 256, better be less than 64k
michael@0 1675 newitem.data = (unsigned char*) PR_Malloc(dername->len + 4);
michael@0 1676 if (!newitem.data) {
michael@0 1677 goto loser;
michael@0 1678 }
michael@0 1679 newitem.data[0] = (unsigned char) 0x30;
michael@0 1680 newitem.data[1] = (unsigned char) 0x82;
michael@0 1681 newitem.data[2] = (unsigned char) ((dername->len >> 8) & 0xff);
michael@0 1682 newitem.data[3] = (unsigned char) (dername->len & 0xff);
michael@0 1683 memcpy(&newitem.data[4], dername->data, dername->len);
michael@0 1684 }
michael@0 1685 dername = &newitem;
michael@0 1686 }
michael@0 1687
michael@0 1688 namestring = CERT_DerNameToAscii(dername);
michael@0 1689 if (!namestring) {
michael@0 1690 // XXX - keep going until we fail to convert the name
michael@0 1691 caNameStrings[n] = const_cast<char*>("");
michael@0 1692 } else {
michael@0 1693 caNameStrings[n] = PORT_ArenaStrdup(arena, namestring);
michael@0 1694 PR_Free(namestring);
michael@0 1695 if (!caNameStrings[n]) {
michael@0 1696 goto loser;
michael@0 1697 }
michael@0 1698 }
michael@0 1699
michael@0 1700 if (newitem.data) {
michael@0 1701 PR_Free(newitem.data);
michael@0 1702 }
michael@0 1703 }
michael@0 1704
michael@0 1705 return SECSuccess;
michael@0 1706 loser:
michael@0 1707 if (newitem.data) {
michael@0 1708 PR_Free(newitem.data);
michael@0 1709 }
michael@0 1710 return SECFailure;
michael@0 1711 }
michael@0 1712
michael@0 1713 // Sets certChoice by reading the preference
michael@0 1714 //
michael@0 1715 // If done properly, this function will read the identifier strings for ASK and
michael@0 1716 // AUTO modes read the selected strings from the preference, compare the
michael@0 1717 // strings, and determine in which mode it is in. We currently use ASK mode for
michael@0 1718 // UI apps and AUTO mode for UI-less apps without really asking for
michael@0 1719 // preferences.
michael@0 1720 nsresult
michael@0 1721 nsGetUserCertChoice(SSM_UserCertChoice* certChoice)
michael@0 1722 {
michael@0 1723 char* mode = nullptr;
michael@0 1724 nsresult ret;
michael@0 1725
michael@0 1726 NS_ENSURE_ARG_POINTER(certChoice);
michael@0 1727
michael@0 1728 nsCOMPtr<nsIPrefBranch> pref = do_GetService(NS_PREFSERVICE_CONTRACTID);
michael@0 1729
michael@0 1730 ret = pref->GetCharPref("security.default_personal_cert", &mode);
michael@0 1731 if (NS_FAILED(ret)) {
michael@0 1732 goto loser;
michael@0 1733 }
michael@0 1734
michael@0 1735 if (PL_strcmp(mode, "Select Automatically") == 0) {
michael@0 1736 *certChoice = AUTO;
michael@0 1737 } else if (PL_strcmp(mode, "Ask Every Time") == 0) {
michael@0 1738 *certChoice = ASK;
michael@0 1739 } else {
michael@0 1740 // Most likely we see a nickname from a migrated cert.
michael@0 1741 // We do not currently support that, ask the user which cert to use.
michael@0 1742 *certChoice = ASK;
michael@0 1743 }
michael@0 1744
michael@0 1745 loser:
michael@0 1746 if (mode) {
michael@0 1747 nsMemory::Free(mode);
michael@0 1748 }
michael@0 1749 return ret;
michael@0 1750 }
michael@0 1751
michael@0 1752 static bool
michael@0 1753 hasExplicitKeyUsageNonRepudiation(CERTCertificate* cert)
michael@0 1754 {
michael@0 1755 // There is no extension, v1 or v2 certificate
michael@0 1756 if (!cert->extensions)
michael@0 1757 return false;
michael@0 1758
michael@0 1759 SECStatus srv;
michael@0 1760 SECItem keyUsageItem;
michael@0 1761 keyUsageItem.data = nullptr;
michael@0 1762
michael@0 1763 srv = CERT_FindKeyUsageExtension(cert, &keyUsageItem);
michael@0 1764 if (srv == SECFailure)
michael@0 1765 return false;
michael@0 1766
michael@0 1767 unsigned char keyUsage = keyUsageItem.data[0];
michael@0 1768 PORT_Free (keyUsageItem.data);
michael@0 1769
michael@0 1770 return !!(keyUsage & KU_NON_REPUDIATION);
michael@0 1771 }
michael@0 1772
michael@0 1773 class ClientAuthDataRunnable : public SyncRunnableBase
michael@0 1774 {
michael@0 1775 public:
michael@0 1776 ClientAuthDataRunnable(CERTDistNames* caNames,
michael@0 1777 CERTCertificate** pRetCert,
michael@0 1778 SECKEYPrivateKey** pRetKey,
michael@0 1779 nsNSSSocketInfo* info,
michael@0 1780 CERTCertificate* serverCert)
michael@0 1781 : mRV(SECFailure)
michael@0 1782 , mErrorCodeToReport(SEC_ERROR_NO_MEMORY)
michael@0 1783 , mPRetCert(pRetCert)
michael@0 1784 , mPRetKey(pRetKey)
michael@0 1785 , mCANames(caNames)
michael@0 1786 , mSocketInfo(info)
michael@0 1787 , mServerCert(serverCert)
michael@0 1788 {
michael@0 1789 }
michael@0 1790
michael@0 1791 SECStatus mRV; // out
michael@0 1792 PRErrorCode mErrorCodeToReport; // out
michael@0 1793 CERTCertificate** const mPRetCert; // in/out
michael@0 1794 SECKEYPrivateKey** const mPRetKey; // in/out
michael@0 1795 protected:
michael@0 1796 virtual void RunOnTargetThread();
michael@0 1797 private:
michael@0 1798 CERTDistNames* const mCANames; // in
michael@0 1799 nsNSSSocketInfo* const mSocketInfo; // in
michael@0 1800 CERTCertificate* const mServerCert; // in
michael@0 1801 };
michael@0 1802
michael@0 1803 // This callback function is used to pull client certificate
michael@0 1804 // information upon server request
michael@0 1805 //
michael@0 1806 // - arg: SSL data connection
michael@0 1807 // - socket: SSL socket we're dealing with
michael@0 1808 // - caNames: list of CA names
michael@0 1809 // - pRetCert: returns a pointer to a pointer to a valid certificate if
michael@0 1810 // successful; otherwise nullptr
michael@0 1811 // - pRetKey: returns a pointer to a pointer to the corresponding key if
michael@0 1812 // successful; otherwise nullptr
michael@0 1813 SECStatus
michael@0 1814 nsNSS_SSLGetClientAuthData(void* arg, PRFileDesc* socket,
michael@0 1815 CERTDistNames* caNames, CERTCertificate** pRetCert,
michael@0 1816 SECKEYPrivateKey** pRetKey)
michael@0 1817 {
michael@0 1818 nsNSSShutDownPreventionLock locker;
michael@0 1819
michael@0 1820 if (!socket || !caNames || !pRetCert || !pRetKey) {
michael@0 1821 PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
michael@0 1822 return SECFailure;
michael@0 1823 }
michael@0 1824
michael@0 1825 RefPtr<nsNSSSocketInfo> info(
michael@0 1826 reinterpret_cast<nsNSSSocketInfo*>(socket->higher->secret));
michael@0 1827
michael@0 1828 CERTCertificate* serverCert = SSL_PeerCertificate(socket);
michael@0 1829 if (!serverCert) {
michael@0 1830 NS_NOTREACHED("Missing server certificate should have been detected during "
michael@0 1831 "server cert authentication.");
michael@0 1832 PR_SetError(SSL_ERROR_NO_CERTIFICATE, 0);
michael@0 1833 return SECFailure;
michael@0 1834 }
michael@0 1835
michael@0 1836 if (info->GetJoined()) {
michael@0 1837 // We refuse to send a client certificate when there are multiple hostnames
michael@0 1838 // joined on this connection, because we only show the user one hostname
michael@0 1839 // (mHostName) in the client certificate UI.
michael@0 1840
michael@0 1841 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
michael@0 1842 ("[%p] Not returning client cert due to previous join\n", socket));
michael@0 1843 *pRetCert = nullptr;
michael@0 1844 *pRetKey = nullptr;
michael@0 1845 return SECSuccess;
michael@0 1846 }
michael@0 1847
michael@0 1848 // XXX: This should be done asynchronously; see bug 696976
michael@0 1849 RefPtr<ClientAuthDataRunnable> runnable(
michael@0 1850 new ClientAuthDataRunnable(caNames, pRetCert, pRetKey, info, serverCert));
michael@0 1851 nsresult rv = runnable->DispatchToMainThreadAndWait();
michael@0 1852 if (NS_FAILED(rv)) {
michael@0 1853 PR_SetError(SEC_ERROR_NO_MEMORY, 0);
michael@0 1854 return SECFailure;
michael@0 1855 }
michael@0 1856
michael@0 1857 if (runnable->mRV != SECSuccess) {
michael@0 1858 PR_SetError(runnable->mErrorCodeToReport, 0);
michael@0 1859 } else if (*runnable->mPRetCert || *runnable->mPRetKey) {
michael@0 1860 // Make joinConnection prohibit joining after we've sent a client cert
michael@0 1861 info->SetSentClientCert();
michael@0 1862 }
michael@0 1863
michael@0 1864 return runnable->mRV;
michael@0 1865 }
michael@0 1866
michael@0 1867 void
michael@0 1868 ClientAuthDataRunnable::RunOnTargetThread()
michael@0 1869 {
michael@0 1870 PLArenaPool* arena = nullptr;
michael@0 1871 char** caNameStrings;
michael@0 1872 mozilla::pkix::ScopedCERTCertificate cert;
michael@0 1873 ScopedSECKEYPrivateKey privKey;
michael@0 1874 mozilla::pkix::ScopedCERTCertList certList;
michael@0 1875 CERTCertListNode* node;
michael@0 1876 ScopedCERTCertNicknames nicknames;
michael@0 1877 int keyError = 0; // used for private key retrieval error
michael@0 1878 SSM_UserCertChoice certChoice;
michael@0 1879 int32_t NumberOfCerts = 0;
michael@0 1880 void* wincx = mSocketInfo;
michael@0 1881 nsresult rv;
michael@0 1882
michael@0 1883 // create caNameStrings
michael@0 1884 arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
michael@0 1885 if (!arena) {
michael@0 1886 goto loser;
michael@0 1887 }
michael@0 1888
michael@0 1889 caNameStrings = (char**) PORT_ArenaAlloc(arena,
michael@0 1890 sizeof(char*) * (mCANames->nnames));
michael@0 1891 if (!caNameStrings) {
michael@0 1892 goto loser;
michael@0 1893 }
michael@0 1894
michael@0 1895 mRV = nsConvertCANamesToStrings(arena, caNameStrings, mCANames);
michael@0 1896 if (mRV != SECSuccess) {
michael@0 1897 goto loser;
michael@0 1898 }
michael@0 1899
michael@0 1900 // get the preference
michael@0 1901 if (NS_FAILED(nsGetUserCertChoice(&certChoice))) {
michael@0 1902 goto loser;
michael@0 1903 }
michael@0 1904
michael@0 1905 // find valid user cert and key pair
michael@0 1906 if (certChoice == AUTO) {
michael@0 1907 // automatically find the right cert
michael@0 1908
michael@0 1909 // find all user certs that are valid and for SSL
michael@0 1910 certList = CERT_FindUserCertsByUsage(CERT_GetDefaultCertDB(),
michael@0 1911 certUsageSSLClient, false,
michael@0 1912 true, wincx);
michael@0 1913 if (!certList) {
michael@0 1914 goto noCert;
michael@0 1915 }
michael@0 1916
michael@0 1917 // filter the list to those issued by CAs supported by the server
michael@0 1918 mRV = CERT_FilterCertListByCANames(certList.get(), mCANames->nnames,
michael@0 1919 caNameStrings, certUsageSSLClient);
michael@0 1920 if (mRV != SECSuccess) {
michael@0 1921 goto noCert;
michael@0 1922 }
michael@0 1923
michael@0 1924 // make sure the list is not empty
michael@0 1925 node = CERT_LIST_HEAD(certList);
michael@0 1926 if (CERT_LIST_END(node, certList)) {
michael@0 1927 goto noCert;
michael@0 1928 }
michael@0 1929
michael@0 1930 ScopedCERTCertificate low_prio_nonrep_cert;
michael@0 1931
michael@0 1932 // loop through the list until we find a cert with a key
michael@0 1933 while (!CERT_LIST_END(node, certList)) {
michael@0 1934 // if the certificate has restriction and we do not satisfy it we do not
michael@0 1935 // use it
michael@0 1936 privKey = PK11_FindKeyByAnyCert(node->cert, wincx);
michael@0 1937 if (privKey) {
michael@0 1938 if (hasExplicitKeyUsageNonRepudiation(node->cert)) {
michael@0 1939 privKey = nullptr;
michael@0 1940 // Not a prefered cert
michael@0 1941 if (!low_prio_nonrep_cert) { // did not yet find a low prio cert
michael@0 1942 low_prio_nonrep_cert = CERT_DupCertificate(node->cert);
michael@0 1943 }
michael@0 1944 } else {
michael@0 1945 // this is a good cert to present
michael@0 1946 cert = CERT_DupCertificate(node->cert);
michael@0 1947 break;
michael@0 1948 }
michael@0 1949 }
michael@0 1950 keyError = PR_GetError();
michael@0 1951 if (keyError == SEC_ERROR_BAD_PASSWORD) {
michael@0 1952 // problem with password: bail
michael@0 1953 goto loser;
michael@0 1954 }
michael@0 1955
michael@0 1956 node = CERT_LIST_NEXT(node);
michael@0 1957 }
michael@0 1958
michael@0 1959 if (!cert && low_prio_nonrep_cert) {
michael@0 1960 cert = low_prio_nonrep_cert.forget();
michael@0 1961 privKey = PK11_FindKeyByAnyCert(cert.get(), wincx);
michael@0 1962 }
michael@0 1963
michael@0 1964 if (!cert) {
michael@0 1965 goto noCert;
michael@0 1966 }
michael@0 1967 } else { // Not Auto => ask
michael@0 1968 // Get the SSL Certificate
michael@0 1969
michael@0 1970 nsXPIDLCString hostname;
michael@0 1971 mSocketInfo->GetHostName(getter_Copies(hostname));
michael@0 1972
michael@0 1973 RefPtr<nsClientAuthRememberService> cars =
michael@0 1974 mSocketInfo->SharedState().GetClientAuthRememberService();
michael@0 1975
michael@0 1976 bool hasRemembered = false;
michael@0 1977 nsCString rememberedDBKey;
michael@0 1978 if (cars) {
michael@0 1979 bool found;
michael@0 1980 rv = cars->HasRememberedDecision(hostname, mServerCert,
michael@0 1981 rememberedDBKey, &found);
michael@0 1982 if (NS_SUCCEEDED(rv) && found) {
michael@0 1983 hasRemembered = true;
michael@0 1984 }
michael@0 1985 }
michael@0 1986
michael@0 1987 bool canceled = false;
michael@0 1988
michael@0 1989 if (hasRemembered) {
michael@0 1990 if (rememberedDBKey.IsEmpty()) {
michael@0 1991 canceled = true;
michael@0 1992 } else {
michael@0 1993 nsCOMPtr<nsIX509CertDB> certdb;
michael@0 1994 certdb = do_GetService(NS_X509CERTDB_CONTRACTID);
michael@0 1995 if (certdb) {
michael@0 1996 nsCOMPtr<nsIX509Cert> found_cert;
michael@0 1997 nsresult find_rv =
michael@0 1998 certdb->FindCertByDBKey(rememberedDBKey.get(), nullptr,
michael@0 1999 getter_AddRefs(found_cert));
michael@0 2000 if (NS_SUCCEEDED(find_rv) && found_cert) {
michael@0 2001 nsNSSCertificate* obj_cert =
michael@0 2002 reinterpret_cast<nsNSSCertificate*>(found_cert.get());
michael@0 2003 if (obj_cert) {
michael@0 2004 cert = obj_cert->GetCert();
michael@0 2005 }
michael@0 2006 }
michael@0 2007
michael@0 2008 if (!cert) {
michael@0 2009 hasRemembered = false;
michael@0 2010 }
michael@0 2011 }
michael@0 2012 }
michael@0 2013 }
michael@0 2014
michael@0 2015 if (!hasRemembered) {
michael@0 2016 // user selects a cert to present
michael@0 2017 nsIClientAuthDialogs* dialogs = nullptr;
michael@0 2018 int32_t selectedIndex = -1;
michael@0 2019 char16_t** certNicknameList = nullptr;
michael@0 2020 char16_t** certDetailsList = nullptr;
michael@0 2021
michael@0 2022 // find all user certs that are for SSL
michael@0 2023 // note that we are allowing expired certs in this list
michael@0 2024 certList = CERT_FindUserCertsByUsage(CERT_GetDefaultCertDB(),
michael@0 2025 certUsageSSLClient, false,
michael@0 2026 false, wincx);
michael@0 2027 if (!certList) {
michael@0 2028 goto noCert;
michael@0 2029 }
michael@0 2030
michael@0 2031 if (mCANames->nnames != 0) {
michael@0 2032 // filter the list to those issued by CAs supported by the server
michael@0 2033 mRV = CERT_FilterCertListByCANames(certList.get(),
michael@0 2034 mCANames->nnames,
michael@0 2035 caNameStrings,
michael@0 2036 certUsageSSLClient);
michael@0 2037 if (mRV != SECSuccess) {
michael@0 2038 goto loser;
michael@0 2039 }
michael@0 2040 }
michael@0 2041
michael@0 2042 if (CERT_LIST_END(CERT_LIST_HEAD(certList), certList)) {
michael@0 2043 // list is empty - no matching certs
michael@0 2044 goto noCert;
michael@0 2045 }
michael@0 2046
michael@0 2047 // filter it further for hostname restriction
michael@0 2048 node = CERT_LIST_HEAD(certList.get());
michael@0 2049 while (!CERT_LIST_END(node, certList.get())) {
michael@0 2050 ++NumberOfCerts;
michael@0 2051 node = CERT_LIST_NEXT(node);
michael@0 2052 }
michael@0 2053 if (CERT_LIST_END(CERT_LIST_HEAD(certList.get()), certList.get())) {
michael@0 2054 goto noCert;
michael@0 2055 }
michael@0 2056
michael@0 2057 nicknames = getNSSCertNicknamesFromCertList(certList.get());
michael@0 2058
michael@0 2059 if (!nicknames) {
michael@0 2060 goto loser;
michael@0 2061 }
michael@0 2062
michael@0 2063 NS_ASSERTION(nicknames->numnicknames == NumberOfCerts, "nicknames->numnicknames != NumberOfCerts");
michael@0 2064
michael@0 2065 // Get CN and O of the subject and O of the issuer
michael@0 2066 char* ccn = CERT_GetCommonName(&mServerCert->subject);
michael@0 2067 void* v = ccn;
michael@0 2068 voidCleaner ccnCleaner(v);
michael@0 2069 NS_ConvertUTF8toUTF16 cn(ccn);
michael@0 2070
michael@0 2071 int32_t port;
michael@0 2072 mSocketInfo->GetPort(&port);
michael@0 2073
michael@0 2074 nsString cn_host_port;
michael@0 2075 if (ccn && strcmp(ccn, hostname) == 0) {
michael@0 2076 cn_host_port.Append(cn);
michael@0 2077 cn_host_port.AppendLiteral(":");
michael@0 2078 cn_host_port.AppendInt(port);
michael@0 2079 } else {
michael@0 2080 cn_host_port.Append(cn);
michael@0 2081 cn_host_port.AppendLiteral(" (");
michael@0 2082 cn_host_port.AppendLiteral(":");
michael@0 2083 cn_host_port.AppendInt(port);
michael@0 2084 cn_host_port.AppendLiteral(")");
michael@0 2085 }
michael@0 2086
michael@0 2087 char* corg = CERT_GetOrgName(&mServerCert->subject);
michael@0 2088 NS_ConvertUTF8toUTF16 org(corg);
michael@0 2089 if (corg) PORT_Free(corg);
michael@0 2090
michael@0 2091 char* cissuer = CERT_GetOrgName(&mServerCert->issuer);
michael@0 2092 NS_ConvertUTF8toUTF16 issuer(cissuer);
michael@0 2093 if (cissuer) PORT_Free(cissuer);
michael@0 2094
michael@0 2095 certNicknameList =
michael@0 2096 (char16_t**)nsMemory::Alloc(sizeof(char16_t*)* nicknames->numnicknames);
michael@0 2097 if (!certNicknameList)
michael@0 2098 goto loser;
michael@0 2099 certDetailsList =
michael@0 2100 (char16_t**)nsMemory::Alloc(sizeof(char16_t*)* nicknames->numnicknames);
michael@0 2101 if (!certDetailsList) {
michael@0 2102 nsMemory::Free(certNicknameList);
michael@0 2103 goto loser;
michael@0 2104 }
michael@0 2105
michael@0 2106 int32_t CertsToUse;
michael@0 2107 for (CertsToUse = 0, node = CERT_LIST_HEAD(certList);
michael@0 2108 !CERT_LIST_END(node, certList) && CertsToUse < nicknames->numnicknames;
michael@0 2109 node = CERT_LIST_NEXT(node)
michael@0 2110 ) {
michael@0 2111 RefPtr<nsNSSCertificate> tempCert(nsNSSCertificate::Create(node->cert));
michael@0 2112
michael@0 2113 if (!tempCert)
michael@0 2114 continue;
michael@0 2115
michael@0 2116 NS_ConvertUTF8toUTF16 i_nickname(nicknames->nicknames[CertsToUse]);
michael@0 2117 nsAutoString nickWithSerial, details;
michael@0 2118
michael@0 2119 if (NS_FAILED(tempCert->FormatUIStrings(i_nickname, nickWithSerial, details)))
michael@0 2120 continue;
michael@0 2121
michael@0 2122 certNicknameList[CertsToUse] = ToNewUnicode(nickWithSerial);
michael@0 2123 if (!certNicknameList[CertsToUse])
michael@0 2124 continue;
michael@0 2125 certDetailsList[CertsToUse] = ToNewUnicode(details);
michael@0 2126 if (!certDetailsList[CertsToUse]) {
michael@0 2127 nsMemory::Free(certNicknameList[CertsToUse]);
michael@0 2128 continue;
michael@0 2129 }
michael@0 2130
michael@0 2131 ++CertsToUse;
michael@0 2132 }
michael@0 2133
michael@0 2134 // Throw up the client auth dialog and get back the index of the selected cert
michael@0 2135 nsresult rv = getNSSDialogs((void**)&dialogs,
michael@0 2136 NS_GET_IID(nsIClientAuthDialogs),
michael@0 2137 NS_CLIENTAUTHDIALOGS_CONTRACTID);
michael@0 2138
michael@0 2139 if (NS_FAILED(rv)) {
michael@0 2140 NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(CertsToUse, certNicknameList);
michael@0 2141 NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(CertsToUse, certDetailsList);
michael@0 2142 goto loser;
michael@0 2143 }
michael@0 2144
michael@0 2145 {
michael@0 2146 nsPSMUITracker tracker;
michael@0 2147 if (tracker.isUIForbidden()) {
michael@0 2148 rv = NS_ERROR_NOT_AVAILABLE;
michael@0 2149 } else {
michael@0 2150 rv = dialogs->ChooseCertificate(mSocketInfo, cn_host_port.get(),
michael@0 2151 org.get(), issuer.get(),
michael@0 2152 (const char16_t**)certNicknameList,
michael@0 2153 (const char16_t**)certDetailsList,
michael@0 2154 CertsToUse, &selectedIndex, &canceled);
michael@0 2155 }
michael@0 2156 }
michael@0 2157
michael@0 2158 NS_RELEASE(dialogs);
michael@0 2159 NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(CertsToUse, certNicknameList);
michael@0 2160 NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(CertsToUse, certDetailsList);
michael@0 2161
michael@0 2162 if (NS_FAILED(rv)) goto loser;
michael@0 2163
michael@0 2164 // even if the user has canceled, we want to remember that, to avoid repeating prompts
michael@0 2165 bool wantRemember = false;
michael@0 2166 mSocketInfo->GetRememberClientAuthCertificate(&wantRemember);
michael@0 2167
michael@0 2168 int i;
michael@0 2169 if (!canceled)
michael@0 2170 for (i = 0, node = CERT_LIST_HEAD(certList);
michael@0 2171 !CERT_LIST_END(node, certList);
michael@0 2172 ++i, node = CERT_LIST_NEXT(node)) {
michael@0 2173
michael@0 2174 if (i == selectedIndex) {
michael@0 2175 cert = CERT_DupCertificate(node->cert);
michael@0 2176 break;
michael@0 2177 }
michael@0 2178 }
michael@0 2179
michael@0 2180 if (cars && wantRemember) {
michael@0 2181 cars->RememberDecision(hostname, mServerCert,
michael@0 2182 canceled ? nullptr : cert.get());
michael@0 2183 }
michael@0 2184 }
michael@0 2185
michael@0 2186 if (canceled) { rv = NS_ERROR_NOT_AVAILABLE; goto loser; }
michael@0 2187
michael@0 2188 if (!cert) {
michael@0 2189 goto loser;
michael@0 2190 }
michael@0 2191
michael@0 2192 // go get the private key
michael@0 2193 privKey = PK11_FindKeyByAnyCert(cert.get(), wincx);
michael@0 2194 if (!privKey) {
michael@0 2195 keyError = PR_GetError();
michael@0 2196 if (keyError == SEC_ERROR_BAD_PASSWORD) {
michael@0 2197 // problem with password: bail
michael@0 2198 goto loser;
michael@0 2199 } else {
michael@0 2200 goto noCert;
michael@0 2201 }
michael@0 2202 }
michael@0 2203 }
michael@0 2204 goto done;
michael@0 2205
michael@0 2206 noCert:
michael@0 2207 loser:
michael@0 2208 if (mRV == SECSuccess) {
michael@0 2209 mRV = SECFailure;
michael@0 2210 }
michael@0 2211 done:
michael@0 2212 int error = PR_GetError();
michael@0 2213
michael@0 2214 if (arena) {
michael@0 2215 PORT_FreeArena(arena, false);
michael@0 2216 }
michael@0 2217
michael@0 2218 *mPRetCert = cert.release();
michael@0 2219 *mPRetKey = privKey.forget();
michael@0 2220
michael@0 2221 if (mRV == SECFailure) {
michael@0 2222 mErrorCodeToReport = error;
michael@0 2223 }
michael@0 2224 }
michael@0 2225
michael@0 2226 static PRFileDesc*
michael@0 2227 nsSSLIOLayerImportFD(PRFileDesc* fd,
michael@0 2228 nsNSSSocketInfo* infoObject,
michael@0 2229 const char* host)
michael@0 2230 {
michael@0 2231 nsNSSShutDownPreventionLock locker;
michael@0 2232 PRFileDesc* sslSock = SSL_ImportFD(nullptr, fd);
michael@0 2233 if (!sslSock) {
michael@0 2234 NS_ASSERTION(false, "NSS: Error importing socket");
michael@0 2235 return nullptr;
michael@0 2236 }
michael@0 2237 SSL_SetPKCS11PinArg(sslSock, (nsIInterfaceRequestor*) infoObject);
michael@0 2238 SSL_HandshakeCallback(sslSock, HandshakeCallback, infoObject);
michael@0 2239 SSL_SetCanFalseStartCallback(sslSock, CanFalseStartCallback, infoObject);
michael@0 2240
michael@0 2241 // Disable this hook if we connect anonymously. See bug 466080.
michael@0 2242 uint32_t flags = 0;
michael@0 2243 infoObject->GetProviderFlags(&flags);
michael@0 2244 if (flags & nsISocketProvider::ANONYMOUS_CONNECT) {
michael@0 2245 SSL_GetClientAuthDataHook(sslSock, nullptr, infoObject);
michael@0 2246 } else {
michael@0 2247 SSL_GetClientAuthDataHook(sslSock,
michael@0 2248 (SSLGetClientAuthData) nsNSS_SSLGetClientAuthData,
michael@0 2249 infoObject);
michael@0 2250 }
michael@0 2251 if (SECSuccess != SSL_AuthCertificateHook(sslSock, AuthCertificateHook,
michael@0 2252 infoObject)) {
michael@0 2253 NS_NOTREACHED("failed to configure AuthCertificateHook");
michael@0 2254 goto loser;
michael@0 2255 }
michael@0 2256
michael@0 2257 if (SECSuccess != SSL_SetURL(sslSock, host)) {
michael@0 2258 NS_NOTREACHED("SSL_SetURL failed");
michael@0 2259 goto loser;
michael@0 2260 }
michael@0 2261
michael@0 2262 // This is an optimization to make sure the identity info dataset is parsed
michael@0 2263 // and loaded on a separate thread and can be overlapped with network latency.
michael@0 2264 EnsureServerVerificationInitialized();
michael@0 2265
michael@0 2266 return sslSock;
michael@0 2267 loser:
michael@0 2268 if (sslSock) {
michael@0 2269 PR_Close(sslSock);
michael@0 2270 }
michael@0 2271 return nullptr;
michael@0 2272 }
michael@0 2273
michael@0 2274 static nsresult
michael@0 2275 nsSSLIOLayerSetOptions(PRFileDesc* fd, bool forSTARTTLS,
michael@0 2276 bool haveProxy, const char* host, int32_t port,
michael@0 2277 nsNSSSocketInfo* infoObject)
michael@0 2278 {
michael@0 2279 nsNSSShutDownPreventionLock locker;
michael@0 2280 if (forSTARTTLS || haveProxy) {
michael@0 2281 if (SECSuccess != SSL_OptionSet(fd, SSL_SECURITY, false)) {
michael@0 2282 return NS_ERROR_FAILURE;
michael@0 2283 }
michael@0 2284 }
michael@0 2285
michael@0 2286 // Let's see if we're trying to connect to a site we know is
michael@0 2287 // TLS intolerant.
michael@0 2288 nsAutoCString key;
michael@0 2289 key = nsDependentCString(host) + NS_LITERAL_CSTRING(":") + nsPrintfCString("%d", port);
michael@0 2290
michael@0 2291 SSLVersionRange range;
michael@0 2292 if (SSL_VersionRangeGet(fd, &range) != SECSuccess) {
michael@0 2293 return NS_ERROR_FAILURE;
michael@0 2294 }
michael@0 2295
michael@0 2296 uint16_t maxEnabledVersion = range.max;
michael@0 2297
michael@0 2298 infoObject->SharedState().IOLayerHelpers()
michael@0 2299 .adjustForTLSIntolerance(infoObject->GetHostName(), infoObject->GetPort(),
michael@0 2300 range);
michael@0 2301 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
michael@0 2302 ("[%p] nsSSLIOLayerSetOptions: using TLS version range (0x%04x,0x%04x)\n",
michael@0 2303 fd, static_cast<unsigned int>(range.min),
michael@0 2304 static_cast<unsigned int>(range.max)));
michael@0 2305
michael@0 2306 if (SSL_VersionRangeSet(fd, &range) != SECSuccess) {
michael@0 2307 return NS_ERROR_FAILURE;
michael@0 2308 }
michael@0 2309 infoObject->SetTLSVersionRange(range);
michael@0 2310
michael@0 2311 // when adjustForTLSIntolerance tweaks the maximum version downward,
michael@0 2312 // we tell the server using this SCSV so they can detect a downgrade attack
michael@0 2313 if (range.max < maxEnabledVersion) {
michael@0 2314 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
michael@0 2315 ("[%p] nsSSLIOLayerSetOptions: enabling TLS_FALLBACK_SCSV\n", fd));
michael@0 2316 if (SECSuccess != SSL_OptionSet(fd, SSL_ENABLE_FALLBACK_SCSV, true)) {
michael@0 2317 return NS_ERROR_FAILURE;
michael@0 2318 }
michael@0 2319 }
michael@0 2320
michael@0 2321 bool enabled = infoObject->SharedState().IsOCSPStaplingEnabled();
michael@0 2322 if (SECSuccess != SSL_OptionSet(fd, SSL_ENABLE_OCSP_STAPLING, enabled)) {
michael@0 2323 return NS_ERROR_FAILURE;
michael@0 2324 }
michael@0 2325
michael@0 2326 if (SECSuccess != SSL_OptionSet(fd, SSL_HANDSHAKE_AS_CLIENT, true)) {
michael@0 2327 return NS_ERROR_FAILURE;
michael@0 2328 }
michael@0 2329
michael@0 2330 nsSSLIOLayerHelpers& ioHelpers = infoObject->SharedState().IOLayerHelpers();
michael@0 2331 if (ioHelpers.isRenegoUnrestrictedSite(nsDependentCString(host))) {
michael@0 2332 if (SECSuccess != SSL_OptionSet(fd, SSL_REQUIRE_SAFE_NEGOTIATION, false)) {
michael@0 2333 return NS_ERROR_FAILURE;
michael@0 2334 }
michael@0 2335 if (SECSuccess != SSL_OptionSet(fd, SSL_ENABLE_RENEGOTIATION, SSL_RENEGOTIATE_UNRESTRICTED)) {
michael@0 2336 return NS_ERROR_FAILURE;
michael@0 2337 }
michael@0 2338 }
michael@0 2339
michael@0 2340 // Set the Peer ID so that SSL proxy connections work properly and to
michael@0 2341 // separate anonymous and/or private browsing connections.
michael@0 2342 uint32_t flags = infoObject->GetProviderFlags();
michael@0 2343 nsAutoCString peerId;
michael@0 2344 if (flags & nsISocketProvider::ANONYMOUS_CONNECT) { // See bug 466080
michael@0 2345 peerId.Append("anon:");
michael@0 2346 }
michael@0 2347 if (flags & nsISocketProvider::NO_PERMANENT_STORAGE) {
michael@0 2348 peerId.Append("private:");
michael@0 2349 }
michael@0 2350 peerId.Append(host);
michael@0 2351 peerId.Append(':');
michael@0 2352 peerId.AppendInt(port);
michael@0 2353 if (SECSuccess != SSL_SetSockPeerID(fd, peerId.get())) {
michael@0 2354 return NS_ERROR_FAILURE;
michael@0 2355 }
michael@0 2356
michael@0 2357 return NS_OK;
michael@0 2358 }
michael@0 2359
michael@0 2360 nsresult
michael@0 2361 nsSSLIOLayerAddToSocket(int32_t family,
michael@0 2362 const char* host,
michael@0 2363 int32_t port,
michael@0 2364 nsIProxyInfo* proxy,
michael@0 2365 PRFileDesc* fd,
michael@0 2366 nsISupports** info,
michael@0 2367 bool forSTARTTLS,
michael@0 2368 uint32_t providerFlags)
michael@0 2369 {
michael@0 2370 nsNSSShutDownPreventionLock locker;
michael@0 2371 PRFileDesc* layer = nullptr;
michael@0 2372 PRFileDesc* plaintextLayer = nullptr;
michael@0 2373 nsresult rv;
michael@0 2374 PRStatus stat;
michael@0 2375
michael@0 2376 SharedSSLState* sharedState =
michael@0 2377 providerFlags & nsISocketProvider::NO_PERMANENT_STORAGE ? PrivateSSLState() : PublicSSLState();
michael@0 2378 nsNSSSocketInfo* infoObject = new nsNSSSocketInfo(*sharedState, providerFlags);
michael@0 2379 if (!infoObject) return NS_ERROR_FAILURE;
michael@0 2380
michael@0 2381 NS_ADDREF(infoObject);
michael@0 2382 infoObject->SetForSTARTTLS(forSTARTTLS);
michael@0 2383 infoObject->SetHostName(host);
michael@0 2384 infoObject->SetPort(port);
michael@0 2385
michael@0 2386 bool haveProxy = false;
michael@0 2387 if (proxy) {
michael@0 2388 nsCString proxyHost;
michael@0 2389 proxy->GetHost(proxyHost);
michael@0 2390 haveProxy = !proxyHost.IsEmpty();
michael@0 2391 }
michael@0 2392
michael@0 2393 // A plaintext observer shim is inserted so we can observe some protocol
michael@0 2394 // details without modifying nss
michael@0 2395 plaintextLayer = PR_CreateIOLayerStub(nsSSLIOLayerHelpers::nsSSLPlaintextLayerIdentity,
michael@0 2396 &nsSSLIOLayerHelpers::nsSSLPlaintextLayerMethods);
michael@0 2397 if (plaintextLayer) {
michael@0 2398 plaintextLayer->secret = (PRFilePrivate*) infoObject;
michael@0 2399 stat = PR_PushIOLayer(fd, PR_TOP_IO_LAYER, plaintextLayer);
michael@0 2400 if (stat == PR_FAILURE) {
michael@0 2401 plaintextLayer->dtor(plaintextLayer);
michael@0 2402 plaintextLayer = nullptr;
michael@0 2403 }
michael@0 2404 }
michael@0 2405
michael@0 2406 PRFileDesc* sslSock = nsSSLIOLayerImportFD(fd, infoObject, host);
michael@0 2407 if (!sslSock) {
michael@0 2408 NS_ASSERTION(false, "NSS: Error importing socket");
michael@0 2409 goto loser;
michael@0 2410 }
michael@0 2411
michael@0 2412 infoObject->SetFileDescPtr(sslSock);
michael@0 2413
michael@0 2414 rv = nsSSLIOLayerSetOptions(sslSock, forSTARTTLS, haveProxy, host, port,
michael@0 2415 infoObject);
michael@0 2416
michael@0 2417 if (NS_FAILED(rv))
michael@0 2418 goto loser;
michael@0 2419
michael@0 2420 // Now, layer ourselves on top of the SSL socket...
michael@0 2421 layer = PR_CreateIOLayerStub(nsSSLIOLayerHelpers::nsSSLIOLayerIdentity,
michael@0 2422 &nsSSLIOLayerHelpers::nsSSLIOLayerMethods);
michael@0 2423 if (!layer)
michael@0 2424 goto loser;
michael@0 2425
michael@0 2426 layer->secret = (PRFilePrivate*) infoObject;
michael@0 2427 stat = PR_PushIOLayer(sslSock, PR_GetLayersIdentity(sslSock), layer);
michael@0 2428
michael@0 2429 if (stat == PR_FAILURE) {
michael@0 2430 goto loser;
michael@0 2431 }
michael@0 2432
michael@0 2433 nsNSSShutDownList::trackSSLSocketCreate();
michael@0 2434
michael@0 2435 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p] Socket set up\n", (void*) sslSock));
michael@0 2436 infoObject->QueryInterface(NS_GET_IID(nsISupports), (void**) (info));
michael@0 2437
michael@0 2438 // We are going use a clear connection first //
michael@0 2439 if (forSTARTTLS || haveProxy) {
michael@0 2440 infoObject->SetHandshakeNotPending();
michael@0 2441 }
michael@0 2442
michael@0 2443 infoObject->SharedState().NoteSocketCreated();
michael@0 2444
michael@0 2445 return NS_OK;
michael@0 2446 loser:
michael@0 2447 NS_IF_RELEASE(infoObject);
michael@0 2448 if (layer) {
michael@0 2449 layer->dtor(layer);
michael@0 2450 }
michael@0 2451 if (plaintextLayer) {
michael@0 2452 PR_PopIOLayer(fd, nsSSLIOLayerHelpers::nsSSLPlaintextLayerIdentity);
michael@0 2453 plaintextLayer->dtor(plaintextLayer);
michael@0 2454 }
michael@0 2455 return NS_ERROR_FAILURE;
michael@0 2456 }

mercurial