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