Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
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 | } |