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