/src/mozilla-central/security/manager/ssl/nsNSSIOLayer.cpp
Line | Count | Source (jump to first uncovered line) |
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 <algorithm> |
10 | | |
11 | | #include "NSSCertDBTrustDomain.h" |
12 | | #include "NSSErrorsService.h" |
13 | | #include "PSMRunnable.h" |
14 | | #include "SSLServerCertVerification.h" |
15 | | #include "ScopedNSSTypes.h" |
16 | | #include "SharedSSLState.h" |
17 | | #include "keyhi.h" |
18 | | #include "mozilla/Casting.h" |
19 | | #include "mozilla/DebugOnly.h" |
20 | | #include "mozilla/Logging.h" |
21 | | #include "mozilla/Move.h" |
22 | | #include "mozilla/Preferences.h" |
23 | | #include "mozilla/Telemetry.h" |
24 | | #include "nsArray.h" |
25 | | #include "nsArrayUtils.h" |
26 | | #include "nsCRT.h" |
27 | | #include "nsCharSeparatedTokenizer.h" |
28 | | #include "nsClientAuthRemember.h" |
29 | | #include "nsContentUtils.h" |
30 | | #include "nsIClientAuthDialogs.h" |
31 | | #include "nsIConsoleService.h" |
32 | | #include "nsIPrefService.h" |
33 | | #include "nsISocketProvider.h" |
34 | | #include "nsIWebProgressListener.h" |
35 | | #include "nsNSSCertHelper.h" |
36 | | #include "nsNSSComponent.h" |
37 | | #include "nsNSSHelper.h" |
38 | | #include "nsPrintfCString.h" |
39 | | #include "nsServiceManagerUtils.h" |
40 | | #include "pkix/pkixnss.h" |
41 | | #include "pkix/pkixtypes.h" |
42 | | #include "prmem.h" |
43 | | #include "prnetdb.h" |
44 | | #include "secder.h" |
45 | | #include "secerr.h" |
46 | | #include "ssl.h" |
47 | | #include "sslerr.h" |
48 | | #include "sslproto.h" |
49 | | #include "sslexp.h" |
50 | | |
51 | | using namespace mozilla; |
52 | | using namespace mozilla::psm; |
53 | | |
54 | | //#define DEBUG_SSL_VERBOSE //Enable this define to get minimal |
55 | | //reports when doing SSL read/write |
56 | | |
57 | | //#define DUMP_BUFFER //Enable this define along with |
58 | | //DEBUG_SSL_VERBOSE to dump SSL |
59 | | //read/write buffer to a log. |
60 | | //Uses PR_LOG except on Mac where |
61 | | //we always write out to our own |
62 | | //file. |
63 | | |
64 | | namespace { |
65 | | |
66 | | // The NSSSocketInfo tls flags are meant to be opaque to most calling applications |
67 | | // but provide a mechanism for direct TLS manipulation when experimenting with new |
68 | | // features in the scope of a single socket. They do not create a persistent ABI. |
69 | | // |
70 | | // Use of these flags creates a new 'sharedSSLState' so existing states for intolerance |
71 | | // are not carried to sockets that use these flags (and intolerance they discover |
72 | | // does not impact other normal sockets not using the flags.) |
73 | | // |
74 | | // Their current definitions are: |
75 | | // |
76 | | // bits 0-2 (mask 0x07) specify the max tls version |
77 | | // 0 means no override 1->4 are 1.0, 1.1, 1.2, 1.3, 4->7 unused |
78 | | // bits 3-5 (mask 0x38) specify the tls fallback limit |
79 | | // 0 means no override, values 1->4 match prefs |
80 | | // bit 6 (mask 0x40) was used to specify compat mode. Temporarily reserved. |
81 | | |
82 | | enum { |
83 | | kTLSProviderFlagMaxVersion10 = 0x01, |
84 | | kTLSProviderFlagMaxVersion11 = 0x02, |
85 | | kTLSProviderFlagMaxVersion12 = 0x03, |
86 | | kTLSProviderFlagMaxVersion13 = 0x04, |
87 | | }; |
88 | | |
89 | | static uint32_t getTLSProviderFlagMaxVersion(uint32_t flags) |
90 | 0 | { |
91 | 0 | return (flags & 0x07); |
92 | 0 | } |
93 | | |
94 | | static uint32_t getTLSProviderFlagFallbackLimit(uint32_t flags) |
95 | 0 | { |
96 | 0 | return (flags & 0x38) >> 3; |
97 | 0 | } |
98 | | |
99 | | #define MAX_ALPN_LENGTH 255 |
100 | | |
101 | | void |
102 | | getSiteKey(const nsACString& hostName, uint16_t port, |
103 | | /*out*/ nsACString& key) |
104 | 0 | { |
105 | 0 | key = hostName; |
106 | 0 | key.AppendLiteral(":"); |
107 | 0 | key.AppendInt(port); |
108 | 0 | } |
109 | | |
110 | | } // unnamed namespace |
111 | | |
112 | | extern LazyLogModule gPIPNSSLog; |
113 | | |
114 | | nsNSSSocketInfo::nsNSSSocketInfo(SharedSSLState& aState, uint32_t providerFlags, |
115 | | uint32_t providerTlsFlags) |
116 | | : mFd(nullptr), |
117 | | mCertVerificationState(before_cert_verification), |
118 | | mSharedState(aState), |
119 | | mForSTARTTLS(false), |
120 | | mHandshakePending(true), |
121 | | mRememberClientAuthCertificate(false), |
122 | | mPreliminaryHandshakeDone(false), |
123 | | mNPNCompleted(false), |
124 | | mEarlyDataAccepted(false), |
125 | | mDenyClientCert(false), |
126 | | mFalseStartCallbackCalled(false), |
127 | | mFalseStarted(false), |
128 | | mIsFullHandshake(false), |
129 | | mHandshakeCompleted(false), |
130 | | mJoined(false), |
131 | | mSentClientCert(false), |
132 | | mNotedTimeUntilReady(false), |
133 | | mFailedVerification(false), |
134 | | mIsShortWritePending(false), |
135 | | mShortWritePendingByte(0), |
136 | | mShortWriteOriginalAmount(-1), |
137 | | mKEAUsed(nsISSLSocketControl::KEY_EXCHANGE_UNKNOWN), |
138 | | mKEAKeyBits(0), |
139 | | mSSLVersionUsed(nsISSLSocketControl::SSL_VERSION_UNKNOWN), |
140 | | mMACAlgorithmUsed(nsISSLSocketControl::SSL_MAC_UNKNOWN), |
141 | | mBypassAuthentication(false), |
142 | | mProviderFlags(providerFlags), |
143 | | mProviderTlsFlags(providerTlsFlags), |
144 | | mSocketCreationTimestamp(TimeStamp::Now()), |
145 | | mPlaintextBytesRead(0), |
146 | | mClientCert(nullptr) |
147 | 0 | { |
148 | 0 | mTLSVersionRange.min = 0; |
149 | 0 | mTLSVersionRange.max = 0; |
150 | 0 | } |
151 | | |
152 | | nsNSSSocketInfo::~nsNSSSocketInfo() |
153 | 0 | { |
154 | 0 | } |
155 | | |
156 | | NS_IMPL_ISUPPORTS_INHERITED(nsNSSSocketInfo, TransportSecurityInfo, |
157 | | nsISSLSocketControl, |
158 | | nsIClientAuthUserDecision) |
159 | | |
160 | | NS_IMETHODIMP |
161 | | nsNSSSocketInfo::GetProviderFlags(uint32_t* aProviderFlags) |
162 | 0 | { |
163 | 0 | *aProviderFlags = mProviderFlags; |
164 | 0 | return NS_OK; |
165 | 0 | } |
166 | | |
167 | | NS_IMETHODIMP |
168 | | nsNSSSocketInfo::GetProviderTlsFlags(uint32_t* aProviderTlsFlags) |
169 | 0 | { |
170 | 0 | *aProviderTlsFlags = mProviderTlsFlags; |
171 | 0 | return NS_OK; |
172 | 0 | } |
173 | | |
174 | | NS_IMETHODIMP |
175 | | nsNSSSocketInfo::GetKEAUsed(int16_t* aKea) |
176 | 0 | { |
177 | 0 | *aKea = mKEAUsed; |
178 | 0 | return NS_OK; |
179 | 0 | } |
180 | | |
181 | | NS_IMETHODIMP |
182 | | nsNSSSocketInfo::GetKEAKeyBits(uint32_t* aKeyBits) |
183 | 0 | { |
184 | 0 | *aKeyBits = mKEAKeyBits; |
185 | 0 | return NS_OK; |
186 | 0 | } |
187 | | |
188 | | NS_IMETHODIMP |
189 | | nsNSSSocketInfo::GetSSLVersionUsed(int16_t* aSSLVersionUsed) |
190 | 0 | { |
191 | 0 | *aSSLVersionUsed = mSSLVersionUsed; |
192 | 0 | return NS_OK; |
193 | 0 | } |
194 | | |
195 | | NS_IMETHODIMP |
196 | | nsNSSSocketInfo::GetSSLVersionOffered(int16_t* aSSLVersionOffered) |
197 | 0 | { |
198 | 0 | *aSSLVersionOffered = mTLSVersionRange.max; |
199 | 0 | return NS_OK; |
200 | 0 | } |
201 | | |
202 | | NS_IMETHODIMP |
203 | | nsNSSSocketInfo::GetMACAlgorithmUsed(int16_t* aMac) |
204 | 0 | { |
205 | 0 | *aMac = mMACAlgorithmUsed; |
206 | 0 | return NS_OK; |
207 | 0 | } |
208 | | |
209 | | NS_IMETHODIMP |
210 | | nsNSSSocketInfo::GetClientCert(nsIX509Cert** aClientCert) |
211 | 0 | { |
212 | 0 | NS_ENSURE_ARG_POINTER(aClientCert); |
213 | 0 | *aClientCert = mClientCert; |
214 | 0 | NS_IF_ADDREF(*aClientCert); |
215 | 0 | return NS_OK; |
216 | 0 | } |
217 | | |
218 | | NS_IMETHODIMP |
219 | | nsNSSSocketInfo::SetClientCert(nsIX509Cert* aClientCert) |
220 | 0 | { |
221 | 0 | mClientCert = aClientCert; |
222 | 0 | return NS_OK; |
223 | 0 | } |
224 | | |
225 | | NS_IMETHODIMP |
226 | | nsNSSSocketInfo::GetClientCertSent(bool* arg) |
227 | 0 | { |
228 | 0 | *arg = mSentClientCert; |
229 | 0 | return NS_OK; |
230 | 0 | } |
231 | | |
232 | | NS_IMETHODIMP |
233 | | nsNSSSocketInfo::GetBypassAuthentication(bool* arg) |
234 | 0 | { |
235 | 0 | *arg = mBypassAuthentication; |
236 | 0 | return NS_OK; |
237 | 0 | } |
238 | | |
239 | | NS_IMETHODIMP |
240 | | nsNSSSocketInfo::GetFailedVerification(bool* arg) |
241 | 0 | { |
242 | 0 | *arg = mFailedVerification; |
243 | 0 | return NS_OK; |
244 | 0 | } |
245 | | |
246 | | NS_IMETHODIMP |
247 | | nsNSSSocketInfo::GetRememberClientAuthCertificate(bool* aRemember) |
248 | 0 | { |
249 | 0 | NS_ENSURE_ARG_POINTER(aRemember); |
250 | 0 | *aRemember = mRememberClientAuthCertificate; |
251 | 0 | return NS_OK; |
252 | 0 | } |
253 | | |
254 | | NS_IMETHODIMP |
255 | | nsNSSSocketInfo::SetRememberClientAuthCertificate(bool aRemember) |
256 | 0 | { |
257 | 0 | mRememberClientAuthCertificate = aRemember; |
258 | 0 | return NS_OK; |
259 | 0 | } |
260 | | |
261 | | NS_IMETHODIMP |
262 | | nsNSSSocketInfo::GetNotificationCallbacks(nsIInterfaceRequestor** aCallbacks) |
263 | 0 | { |
264 | 0 | *aCallbacks = mCallbacks; |
265 | 0 | NS_IF_ADDREF(*aCallbacks); |
266 | 0 | return NS_OK; |
267 | 0 | } |
268 | | |
269 | | NS_IMETHODIMP |
270 | | nsNSSSocketInfo::SetNotificationCallbacks(nsIInterfaceRequestor* aCallbacks) |
271 | 0 | { |
272 | 0 | if (!aCallbacks) { |
273 | 0 | mCallbacks = nullptr; |
274 | 0 | return NS_OK; |
275 | 0 | } |
276 | 0 | |
277 | 0 | mCallbacks = aCallbacks; |
278 | 0 |
|
279 | 0 | return NS_OK; |
280 | 0 | } |
281 | | |
282 | | void |
283 | | nsNSSSocketInfo::NoteTimeUntilReady() |
284 | 0 | { |
285 | 0 | if (mNotedTimeUntilReady) |
286 | 0 | return; |
287 | 0 | |
288 | 0 | mNotedTimeUntilReady = true; |
289 | 0 |
|
290 | 0 | // This will include TCP and proxy tunnel wait time |
291 | 0 | Telemetry::AccumulateTimeDelta(Telemetry::SSL_TIME_UNTIL_READY, |
292 | 0 | mSocketCreationTimestamp, TimeStamp::Now()); |
293 | 0 | MOZ_LOG(gPIPNSSLog, LogLevel::Debug, |
294 | 0 | ("[%p] nsNSSSocketInfo::NoteTimeUntilReady\n", mFd)); |
295 | 0 | } |
296 | | |
297 | | void |
298 | | nsNSSSocketInfo::SetHandshakeCompleted() |
299 | 0 | { |
300 | 0 | if (!mHandshakeCompleted) { |
301 | 0 | enum HandshakeType { |
302 | 0 | Resumption = 1, |
303 | 0 | FalseStarted = 2, |
304 | 0 | ChoseNotToFalseStart = 3, |
305 | 0 | NotAllowedToFalseStart = 4, |
306 | 0 | }; |
307 | 0 |
|
308 | 0 | HandshakeType handshakeType = !IsFullHandshake() ? Resumption |
309 | 0 | : mFalseStarted ? FalseStarted |
310 | 0 | : mFalseStartCallbackCalled ? ChoseNotToFalseStart |
311 | 0 | : NotAllowedToFalseStart; |
312 | 0 |
|
313 | 0 | // This will include TCP and proxy tunnel wait time |
314 | 0 | Telemetry::AccumulateTimeDelta(Telemetry::SSL_TIME_UNTIL_HANDSHAKE_FINISHED, |
315 | 0 | mSocketCreationTimestamp, TimeStamp::Now()); |
316 | 0 |
|
317 | 0 | // If the handshake is completed for the first time from just 1 callback |
318 | 0 | // that means that TLS session resumption must have been used. |
319 | 0 | Telemetry::Accumulate(Telemetry::SSL_RESUMED_SESSION, |
320 | 0 | handshakeType == Resumption); |
321 | 0 | Telemetry::Accumulate(Telemetry::SSL_HANDSHAKE_TYPE, handshakeType); |
322 | 0 | } |
323 | 0 |
|
324 | 0 | // Remove the plaintext layer as it is not needed anymore. |
325 | 0 | // The plaintext layer is not always present - so it's not a fatal error if it |
326 | 0 | // cannot be removed. |
327 | 0 | // Note that PR_PopIOLayer may modify its stack, so a pointer returned by |
328 | 0 | // PR_GetIdentitiesLayer may not point to what we think it points to after |
329 | 0 | // calling PR_PopIOLayer. We must operate on the pointer returned by |
330 | 0 | // PR_PopIOLayer. |
331 | 0 | if (PR_GetIdentitiesLayer(mFd, nsSSLIOLayerHelpers::nsSSLPlaintextLayerIdentity)) { |
332 | 0 | PRFileDesc* poppedPlaintext = |
333 | 0 | PR_PopIOLayer(mFd, nsSSLIOLayerHelpers::nsSSLPlaintextLayerIdentity); |
334 | 0 | poppedPlaintext->dtor(poppedPlaintext); |
335 | 0 | } |
336 | 0 |
|
337 | 0 | mHandshakeCompleted = true; |
338 | 0 |
|
339 | 0 | MOZ_LOG(gPIPNSSLog, LogLevel::Debug, |
340 | 0 | ("[%p] nsNSSSocketInfo::SetHandshakeCompleted\n", (void*) mFd)); |
341 | 0 |
|
342 | 0 | mIsFullHandshake = false; // reset for next handshake on this connection |
343 | 0 | } |
344 | | |
345 | | void |
346 | | nsNSSSocketInfo::SetNegotiatedNPN(const char* value, uint32_t length) |
347 | 0 | { |
348 | 0 | if (!value) { |
349 | 0 | mNegotiatedNPN.Truncate(); |
350 | 0 | } else { |
351 | 0 | mNegotiatedNPN.Assign(value, length); |
352 | 0 | } |
353 | 0 | mNPNCompleted = true; |
354 | 0 | } |
355 | | |
356 | | NS_IMETHODIMP |
357 | | nsNSSSocketInfo::GetNegotiatedNPN(nsACString& aNegotiatedNPN) |
358 | 0 | { |
359 | 0 | if (!mNPNCompleted) |
360 | 0 | return NS_ERROR_NOT_CONNECTED; |
361 | 0 | |
362 | 0 | aNegotiatedNPN = mNegotiatedNPN; |
363 | 0 | return NS_OK; |
364 | 0 | } |
365 | | |
366 | | NS_IMETHODIMP |
367 | | nsNSSSocketInfo::GetAlpnEarlySelection(nsACString& aAlpnSelected) |
368 | 0 | { |
369 | 0 | aAlpnSelected.Truncate(); |
370 | 0 |
|
371 | 0 | SSLPreliminaryChannelInfo info; |
372 | 0 | SECStatus rv = SSL_GetPreliminaryChannelInfo(mFd, &info, sizeof(info)); |
373 | 0 | if (rv != SECSuccess || !info.canSendEarlyData) { |
374 | 0 | return NS_ERROR_NOT_AVAILABLE; |
375 | 0 | } |
376 | 0 | |
377 | 0 | SSLNextProtoState alpnState; |
378 | 0 | unsigned char chosenAlpn[MAX_ALPN_LENGTH]; |
379 | 0 | unsigned int chosenAlpnLen; |
380 | 0 | rv = SSL_GetNextProto(mFd, &alpnState, chosenAlpn, &chosenAlpnLen, |
381 | 0 | AssertedCast<unsigned int>(ArrayLength(chosenAlpn))); |
382 | 0 |
|
383 | 0 | if (rv != SECSuccess) { |
384 | 0 | return NS_ERROR_NOT_AVAILABLE; |
385 | 0 | } |
386 | 0 | |
387 | 0 | if (alpnState == SSL_NEXT_PROTO_EARLY_VALUE) { |
388 | 0 | aAlpnSelected.Assign(BitwiseCast<char*, unsigned char*>(chosenAlpn), |
389 | 0 | chosenAlpnLen); |
390 | 0 | } |
391 | 0 |
|
392 | 0 | return NS_OK; |
393 | 0 | } |
394 | | |
395 | | NS_IMETHODIMP |
396 | | nsNSSSocketInfo::GetEarlyDataAccepted(bool* aAccepted) |
397 | 0 | { |
398 | 0 | *aAccepted = mEarlyDataAccepted; |
399 | 0 | return NS_OK; |
400 | 0 | } |
401 | | |
402 | | void |
403 | | nsNSSSocketInfo::SetEarlyDataAccepted(bool aAccepted) |
404 | 0 | { |
405 | 0 | mEarlyDataAccepted = aAccepted; |
406 | 0 | } |
407 | | |
408 | | NS_IMETHODIMP |
409 | | nsNSSSocketInfo::GetDenyClientCert(bool* aDenyClientCert) |
410 | 0 | { |
411 | 0 | *aDenyClientCert = mDenyClientCert; |
412 | 0 | return NS_OK; |
413 | 0 | } |
414 | | |
415 | | NS_IMETHODIMP |
416 | | nsNSSSocketInfo::SetDenyClientCert(bool aDenyClientCert) |
417 | 0 | { |
418 | 0 | mDenyClientCert = aDenyClientCert; |
419 | 0 | return NS_OK; |
420 | 0 | } |
421 | | |
422 | | NS_IMETHODIMP |
423 | | nsNSSSocketInfo::DriveHandshake() |
424 | 0 | { |
425 | 0 | if (!mFd) { |
426 | 0 | return NS_ERROR_FAILURE; |
427 | 0 | } |
428 | 0 | PRErrorCode errorCode = GetErrorCode(); |
429 | 0 | if (errorCode) { |
430 | 0 | return GetXPCOMFromNSSError(errorCode); |
431 | 0 | } |
432 | 0 | |
433 | 0 | SECStatus rv = SSL_ForceHandshake(mFd); |
434 | 0 |
|
435 | 0 | if (rv != SECSuccess) { |
436 | 0 | errorCode = PR_GetError(); |
437 | 0 | if (errorCode == PR_WOULD_BLOCK_ERROR) { |
438 | 0 | return NS_BASE_STREAM_WOULD_BLOCK; |
439 | 0 | } |
440 | 0 | |
441 | 0 | SetCanceled(errorCode); |
442 | 0 | return GetXPCOMFromNSSError(errorCode); |
443 | 0 | } |
444 | 0 | return NS_OK; |
445 | 0 | } |
446 | | |
447 | | NS_IMETHODIMP |
448 | | nsNSSSocketInfo::IsAcceptableForHost(const nsACString& hostname, bool* _retval) |
449 | 0 | { |
450 | 0 | NS_ENSURE_ARG(_retval); |
451 | 0 |
|
452 | 0 | *_retval = false; |
453 | 0 |
|
454 | 0 | // If this is the same hostname then the certicate status does not |
455 | 0 | // need to be considered. They are joinable. |
456 | 0 | if (hostname.Equals(GetHostName())) { |
457 | 0 | *_retval = true; |
458 | 0 | return NS_OK; |
459 | 0 | } |
460 | 0 | |
461 | 0 | // Before checking the server certificate we need to make sure the |
462 | 0 | // handshake has completed. |
463 | 0 | if (!mHandshakeCompleted || !HasServerCert()) { |
464 | 0 | return NS_OK; |
465 | 0 | } |
466 | 0 | |
467 | 0 | // If the cert has error bits (e.g. it is untrusted) then do not join. |
468 | 0 | // The value of mHaveCertErrorBits is only reliable because we know that |
469 | 0 | // the handshake completed. |
470 | 0 | if (mHaveCertErrorBits) { |
471 | 0 | return NS_OK; |
472 | 0 | } |
473 | 0 | |
474 | 0 | // If the connection is using client certificates then do not join |
475 | 0 | // because the user decides on whether to send client certs to hosts on a |
476 | 0 | // per-domain basis. |
477 | 0 | if (mSentClientCert) |
478 | 0 | return NS_OK; |
479 | 0 | |
480 | 0 | // Ensure that the server certificate covers the hostname that would |
481 | 0 | // like to join this connection |
482 | 0 | |
483 | 0 | UniqueCERTCertificate nssCert; |
484 | 0 |
|
485 | 0 | nsCOMPtr<nsIX509Cert> cert; |
486 | 0 | if (NS_FAILED(GetServerCert(getter_AddRefs(cert)))) { |
487 | 0 | return NS_OK; |
488 | 0 | } |
489 | 0 | if (cert) { |
490 | 0 | nssCert.reset(cert->GetCert()); |
491 | 0 | } |
492 | 0 |
|
493 | 0 | if (!nssCert) { |
494 | 0 | return NS_OK; |
495 | 0 | } |
496 | 0 | |
497 | 0 | // Attempt to verify the joinee's certificate using the joining hostname. |
498 | 0 | // This ensures that any hostname-specific verification logic (e.g. key |
499 | 0 | // pinning) is satisfied by the joinee's certificate chain. |
500 | 0 | // This verification only uses local information; since we're on the network |
501 | 0 | // thread, we would be blocking on ourselves if we attempted any network i/o. |
502 | 0 | // TODO(bug 1056935): The certificate chain built by this verification may be |
503 | 0 | // different than the certificate chain originally built during the joined |
504 | 0 | // connection's TLS handshake. Consequently, we may report a wrong and/or |
505 | 0 | // misleading certificate chain for HTTP transactions coalesced onto this |
506 | 0 | // connection. This may become problematic in the future. For example, |
507 | 0 | // if/when we begin relying on intermediate certificates being stored in the |
508 | 0 | // securityInfo of a cached HTTPS response, that cached certificate chain may |
509 | 0 | // actually be the wrong chain. We should consider having JoinConnection |
510 | 0 | // return the certificate chain built here, so that the calling Necko code |
511 | 0 | // can associate the correct certificate chain with the HTTP transactions it |
512 | 0 | // is trying to join onto this connection. |
513 | 0 | RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier()); |
514 | 0 | if (!certVerifier) { |
515 | 0 | return NS_OK; |
516 | 0 | } |
517 | 0 | CertVerifier::Flags flags = CertVerifier::FLAG_LOCAL_ONLY; |
518 | 0 | UniqueCERTCertList unusedBuiltChain; |
519 | 0 | mozilla::pkix::Result result = |
520 | 0 | certVerifier->VerifySSLServerCert(nssCert, |
521 | 0 | nullptr, // stapledOCSPResponse |
522 | 0 | nullptr, // sctsFromTLSExtension |
523 | 0 | mozilla::pkix::Now(), |
524 | 0 | nullptr, // pinarg |
525 | 0 | hostname, |
526 | 0 | unusedBuiltChain, |
527 | 0 | false, // save intermediates |
528 | 0 | flags); |
529 | 0 | if (result != mozilla::pkix::Success) { |
530 | 0 | return NS_OK; |
531 | 0 | } |
532 | 0 | |
533 | 0 | // All tests pass |
534 | 0 | *_retval = true; |
535 | 0 | return NS_OK; |
536 | 0 | } |
537 | | |
538 | | NS_IMETHODIMP |
539 | | nsNSSSocketInfo::TestJoinConnection(const nsACString& npnProtocol, |
540 | | const nsACString& hostname, |
541 | | int32_t port, |
542 | | bool* _retval) |
543 | 0 | { |
544 | 0 | *_retval = false; |
545 | 0 |
|
546 | 0 | // Different ports may not be joined together |
547 | 0 | if (port != GetPort()) |
548 | 0 | return NS_OK; |
549 | 0 | |
550 | 0 | // Make sure NPN has been completed and matches requested npnProtocol |
551 | 0 | if (!mNPNCompleted || !mNegotiatedNPN.Equals(npnProtocol)) |
552 | 0 | return NS_OK; |
553 | 0 | |
554 | 0 | if (mBypassAuthentication) { |
555 | 0 | // An unauthenticated connection does not know whether or not it |
556 | 0 | // is acceptable for a particular hostname |
557 | 0 | return NS_OK; |
558 | 0 | } |
559 | 0 | |
560 | 0 | IsAcceptableForHost(hostname, _retval); // sets _retval |
561 | 0 | return NS_OK; |
562 | 0 | } |
563 | | |
564 | | NS_IMETHODIMP |
565 | | nsNSSSocketInfo::JoinConnection(const nsACString& npnProtocol, |
566 | | const nsACString& hostname, |
567 | | int32_t port, |
568 | | bool* _retval) |
569 | 0 | { |
570 | 0 | nsresult rv = TestJoinConnection(npnProtocol, hostname, port, _retval); |
571 | 0 | if (NS_SUCCEEDED(rv) && *_retval) { |
572 | 0 | // All tests pass - this is joinable |
573 | 0 | mJoined = true; |
574 | 0 | } |
575 | 0 | return rv; |
576 | 0 | } |
577 | | |
578 | | bool |
579 | | nsNSSSocketInfo::GetForSTARTTLS() |
580 | 0 | { |
581 | 0 | return mForSTARTTLS; |
582 | 0 | } |
583 | | |
584 | | void |
585 | | nsNSSSocketInfo::SetForSTARTTLS(bool aForSTARTTLS) |
586 | 0 | { |
587 | 0 | mForSTARTTLS = aForSTARTTLS; |
588 | 0 | } |
589 | | |
590 | | NS_IMETHODIMP |
591 | | nsNSSSocketInfo::ProxyStartSSL() |
592 | 0 | { |
593 | 0 | return ActivateSSL(); |
594 | 0 | } |
595 | | |
596 | | NS_IMETHODIMP |
597 | | nsNSSSocketInfo::StartTLS() |
598 | 0 | { |
599 | 0 | return ActivateSSL(); |
600 | 0 | } |
601 | | |
602 | | NS_IMETHODIMP |
603 | | nsNSSSocketInfo::SetNPNList(nsTArray<nsCString>& protocolArray) |
604 | 0 | { |
605 | 0 | if (!mFd) |
606 | 0 | return NS_ERROR_FAILURE; |
607 | 0 | |
608 | 0 | // the npn list is a concatenated list of 8 bit byte strings. |
609 | 0 | nsCString npnList; |
610 | 0 |
|
611 | 0 | for (uint32_t index = 0; index < protocolArray.Length(); ++index) { |
612 | 0 | if (protocolArray[index].IsEmpty() || |
613 | 0 | protocolArray[index].Length() > 255) |
614 | 0 | return NS_ERROR_ILLEGAL_VALUE; |
615 | 0 | |
616 | 0 | npnList.Append(protocolArray[index].Length()); |
617 | 0 | npnList.Append(protocolArray[index]); |
618 | 0 | } |
619 | 0 |
|
620 | 0 | if (SSL_SetNextProtoNego( |
621 | 0 | mFd, |
622 | 0 | BitwiseCast<const unsigned char*, const char*>(npnList.get()), |
623 | 0 | npnList.Length()) != SECSuccess) |
624 | 0 | return NS_ERROR_FAILURE; |
625 | 0 | |
626 | 0 | return NS_OK; |
627 | 0 | } |
628 | | |
629 | | nsresult |
630 | | nsNSSSocketInfo::ActivateSSL() |
631 | 0 | { |
632 | 0 | if (SECSuccess != SSL_OptionSet(mFd, SSL_SECURITY, true)) |
633 | 0 | return NS_ERROR_FAILURE; |
634 | 0 | if (SECSuccess != SSL_ResetHandshake(mFd, false)) |
635 | 0 | return NS_ERROR_FAILURE; |
636 | 0 | |
637 | 0 | mHandshakePending = true; |
638 | 0 |
|
639 | 0 | return NS_OK; |
640 | 0 | } |
641 | | |
642 | | nsresult |
643 | | nsNSSSocketInfo::GetFileDescPtr(PRFileDesc** aFilePtr) |
644 | 0 | { |
645 | 0 | *aFilePtr = mFd; |
646 | 0 | return NS_OK; |
647 | 0 | } |
648 | | |
649 | | nsresult |
650 | | nsNSSSocketInfo::SetFileDescPtr(PRFileDesc* aFilePtr) |
651 | 0 | { |
652 | 0 | mFd = aFilePtr; |
653 | 0 | return NS_OK; |
654 | 0 | } |
655 | | |
656 | | void |
657 | | nsNSSSocketInfo::SetCertVerificationWaiting() |
658 | 0 | { |
659 | 0 | // mCertVerificationState may be before_cert_verification for the first |
660 | 0 | // handshake on the connection, or after_cert_verification for subsequent |
661 | 0 | // renegotiation handshakes. |
662 | 0 | MOZ_ASSERT(mCertVerificationState != waiting_for_cert_verification, |
663 | 0 | "Invalid state transition to waiting_for_cert_verification"); |
664 | 0 | mCertVerificationState = waiting_for_cert_verification; |
665 | 0 | } |
666 | | |
667 | | // Be careful that SetCertVerificationResult does NOT get called while we are |
668 | | // processing a SSL callback function, because SSL_AuthCertificateComplete will |
669 | | // attempt to acquire locks that are already held by libssl when it calls |
670 | | // callbacks. |
671 | | void |
672 | | nsNSSSocketInfo::SetCertVerificationResult(PRErrorCode errorCode) |
673 | 0 | { |
674 | 0 | MOZ_ASSERT(mCertVerificationState == waiting_for_cert_verification, |
675 | 0 | "Invalid state transition to cert_verification_finished"); |
676 | 0 |
|
677 | 0 | if (mFd) { |
678 | 0 | SECStatus rv = SSL_AuthCertificateComplete(mFd, errorCode); |
679 | 0 | // Only replace errorCode if there was originally no error |
680 | 0 | if (rv != SECSuccess && errorCode == 0) { |
681 | 0 | errorCode = PR_GetError(); |
682 | 0 | if (errorCode == 0) { |
683 | 0 | NS_ERROR("SSL_AuthCertificateComplete didn't set error code"); |
684 | 0 | errorCode = PR_INVALID_STATE_ERROR; |
685 | 0 | } |
686 | 0 | } |
687 | 0 | } |
688 | 0 |
|
689 | 0 | if (errorCode) { |
690 | 0 | mFailedVerification = true; |
691 | 0 | SetCanceled(errorCode); |
692 | 0 | } |
693 | 0 |
|
694 | 0 | if (mPlaintextBytesRead && !errorCode) { |
695 | 0 | Telemetry::Accumulate(Telemetry::SSL_BYTES_BEFORE_CERT_CALLBACK, |
696 | 0 | AssertedCast<uint32_t>(mPlaintextBytesRead)); |
697 | 0 | } |
698 | 0 |
|
699 | 0 | mCertVerificationState = after_cert_verification; |
700 | 0 | } |
701 | | |
702 | | SharedSSLState& |
703 | | nsNSSSocketInfo::SharedState() |
704 | 0 | { |
705 | 0 | return mSharedState; |
706 | 0 | } |
707 | | |
708 | | void |
709 | | nsNSSSocketInfo::SetSharedOwningReference(SharedSSLState* aRef) |
710 | 0 | { |
711 | 0 | mOwningSharedRef = aRef; |
712 | 0 | } |
713 | | |
714 | | void nsSSLIOLayerHelpers::Cleanup() |
715 | 0 | { |
716 | 0 | MutexAutoLock lock(mutex); |
717 | 0 | mTLSIntoleranceInfo.Clear(); |
718 | 0 | mInsecureFallbackSites.Clear(); |
719 | 0 | } |
720 | | |
721 | | static void |
722 | | nsHandleSSLError(nsNSSSocketInfo* socketInfo, |
723 | | PRErrorCode err) |
724 | 0 | { |
725 | 0 | if (!NS_IsMainThread()) { |
726 | 0 | NS_ERROR("nsHandleSSLError called off the main thread"); |
727 | 0 | return; |
728 | 0 | } |
729 | 0 |
|
730 | 0 | // SetCanceled is only called by the main thread or the socket transport |
731 | 0 | // thread. Whenever this function is called on the main thread, the SSL |
732 | 0 | // thread is blocked on it. So, no mutex is necessary for |
733 | 0 | // SetCanceled()/GetError*(). |
734 | 0 | if (socketInfo->GetErrorCode()) { |
735 | 0 | // If the socket has been flagged as canceled, |
736 | 0 | // the code who did was responsible for setting the error code. |
737 | 0 | return; |
738 | 0 | } |
739 | 0 | |
740 | 0 | // We must cancel, which sets the error code. |
741 | 0 | socketInfo->SetCanceled(err); |
742 | 0 | } |
743 | | |
744 | | namespace { |
745 | | |
746 | | enum Operation { reading, writing, not_reading_or_writing }; |
747 | | |
748 | | int32_t checkHandshake(int32_t bytesTransfered, bool wasReading, |
749 | | PRFileDesc* ssl_layer_fd, |
750 | | nsNSSSocketInfo* socketInfo); |
751 | | |
752 | | nsNSSSocketInfo* |
753 | | getSocketInfoIfRunning(PRFileDesc* fd, Operation op) |
754 | 0 | { |
755 | 0 | if (!fd || !fd->lower || !fd->secret || |
756 | 0 | fd->identity != nsSSLIOLayerHelpers::nsSSLIOLayerIdentity) { |
757 | 0 | NS_ERROR("bad file descriptor passed to getSocketInfoIfRunning"); |
758 | 0 | PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0); |
759 | 0 | return nullptr; |
760 | 0 | } |
761 | 0 |
|
762 | 0 | nsNSSSocketInfo* socketInfo = (nsNSSSocketInfo*) fd->secret; |
763 | 0 |
|
764 | 0 | if (socketInfo->GetErrorCode()) { |
765 | 0 | PRErrorCode err = socketInfo->GetErrorCode(); |
766 | 0 | PR_SetError(err, 0); |
767 | 0 | if (op == reading || op == writing) { |
768 | 0 | // We must do TLS intolerance checks for reads and writes, for timeouts |
769 | 0 | // in particular. |
770 | 0 | (void) checkHandshake(-1, op == reading, fd, socketInfo); |
771 | 0 | } |
772 | 0 |
|
773 | 0 | // If we get here, it is probably because cert verification failed and this |
774 | 0 | // is the first I/O attempt since that failure. |
775 | 0 | return nullptr; |
776 | 0 | } |
777 | 0 |
|
778 | 0 | return socketInfo; |
779 | 0 | } |
780 | | |
781 | | } // namespace |
782 | | |
783 | | static PRStatus |
784 | | nsSSLIOLayerConnect(PRFileDesc* fd, const PRNetAddr* addr, |
785 | | PRIntervalTime timeout) |
786 | 0 | { |
787 | 0 | MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("[%p] connecting SSL socket\n", |
788 | 0 | (void*) fd)); |
789 | 0 | if (!getSocketInfoIfRunning(fd, not_reading_or_writing)) |
790 | 0 | return PR_FAILURE; |
791 | 0 | |
792 | 0 | PRStatus status = fd->lower->methods->connect(fd->lower, addr, timeout); |
793 | 0 | if (status != PR_SUCCESS) { |
794 | 0 | MOZ_LOG(gPIPNSSLog, LogLevel::Error, ("[%p] Lower layer connect error: %d\n", |
795 | 0 | (void*) fd, PR_GetError())); |
796 | 0 | return status; |
797 | 0 | } |
798 | 0 |
|
799 | 0 | MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("[%p] Connect\n", (void*) fd)); |
800 | 0 | return status; |
801 | 0 | } |
802 | | |
803 | | void |
804 | | nsSSLIOLayerHelpers::rememberTolerantAtVersion(const nsACString& hostName, |
805 | | int16_t port, uint16_t tolerant) |
806 | 0 | { |
807 | 0 | nsCString key; |
808 | 0 | getSiteKey(hostName, port, key); |
809 | 0 |
|
810 | 0 | MutexAutoLock lock(mutex); |
811 | 0 |
|
812 | 0 | IntoleranceEntry entry; |
813 | 0 | if (mTLSIntoleranceInfo.Get(key, &entry)) { |
814 | 0 | entry.AssertInvariant(); |
815 | 0 | entry.tolerant = std::max(entry.tolerant, tolerant); |
816 | 0 | if (entry.intolerant != 0 && entry.intolerant <= entry.tolerant) { |
817 | 0 | entry.intolerant = entry.tolerant + 1; |
818 | 0 | entry.intoleranceReason = 0; // lose the reason |
819 | 0 | } |
820 | 0 | } else { |
821 | 0 | entry.tolerant = tolerant; |
822 | 0 | entry.intolerant = 0; |
823 | 0 | entry.intoleranceReason = 0; |
824 | 0 | } |
825 | 0 |
|
826 | 0 | entry.AssertInvariant(); |
827 | 0 |
|
828 | 0 | mTLSIntoleranceInfo.Put(key, entry); |
829 | 0 | } |
830 | | |
831 | | void |
832 | | nsSSLIOLayerHelpers::forgetIntolerance(const nsACString& hostName, |
833 | | int16_t port) |
834 | 0 | { |
835 | 0 | nsCString key; |
836 | 0 | getSiteKey(hostName, port, key); |
837 | 0 |
|
838 | 0 | MutexAutoLock lock(mutex); |
839 | 0 |
|
840 | 0 | IntoleranceEntry entry; |
841 | 0 | if (mTLSIntoleranceInfo.Get(key, &entry)) { |
842 | 0 | entry.AssertInvariant(); |
843 | 0 |
|
844 | 0 | entry.intolerant = 0; |
845 | 0 | entry.intoleranceReason = 0; |
846 | 0 |
|
847 | 0 | entry.AssertInvariant(); |
848 | 0 | mTLSIntoleranceInfo.Put(key, entry); |
849 | 0 | } |
850 | 0 | } |
851 | | |
852 | | bool |
853 | | nsSSLIOLayerHelpers::fallbackLimitReached(const nsACString& hostName, |
854 | | uint16_t intolerant) |
855 | 0 | { |
856 | 0 | if (isInsecureFallbackSite(hostName)) { |
857 | 0 | return intolerant <= SSL_LIBRARY_VERSION_TLS_1_0; |
858 | 0 | } |
859 | 0 | return intolerant <= mVersionFallbackLimit; |
860 | 0 | } |
861 | | |
862 | | // returns true if we should retry the handshake |
863 | | bool |
864 | | nsSSLIOLayerHelpers::rememberIntolerantAtVersion(const nsACString& hostName, |
865 | | int16_t port, |
866 | | uint16_t minVersion, |
867 | | uint16_t intolerant, |
868 | | PRErrorCode intoleranceReason) |
869 | 0 | { |
870 | 0 | if (intolerant <= minVersion || fallbackLimitReached(hostName, intolerant)) { |
871 | 0 | // We can't fall back any further. Assume that intolerance isn't the issue. |
872 | 0 | forgetIntolerance(hostName, port); |
873 | 0 | return false; |
874 | 0 | } |
875 | 0 | |
876 | 0 | nsCString key; |
877 | 0 | getSiteKey(hostName, port, key); |
878 | 0 |
|
879 | 0 | MutexAutoLock lock(mutex); |
880 | 0 |
|
881 | 0 | IntoleranceEntry entry; |
882 | 0 | if (mTLSIntoleranceInfo.Get(key, &entry)) { |
883 | 0 | entry.AssertInvariant(); |
884 | 0 | if (intolerant <= entry.tolerant) { |
885 | 0 | // We already know the server is tolerant at an equal or higher version. |
886 | 0 | return false; |
887 | 0 | } |
888 | 0 | if ((entry.intolerant != 0 && intolerant >= entry.intolerant)) { |
889 | 0 | // We already know that the server is intolerant at a lower version. |
890 | 0 | return true; |
891 | 0 | } |
892 | 0 | } else { |
893 | 0 | entry.tolerant = 0; |
894 | 0 | } |
895 | 0 |
|
896 | 0 | entry.intolerant = intolerant; |
897 | 0 | entry.intoleranceReason = intoleranceReason; |
898 | 0 | entry.AssertInvariant(); |
899 | 0 | mTLSIntoleranceInfo.Put(key, entry); |
900 | 0 |
|
901 | 0 | return true; |
902 | 0 | } |
903 | | |
904 | | void |
905 | | nsSSLIOLayerHelpers::adjustForTLSIntolerance(const nsACString& hostName, |
906 | | int16_t port, |
907 | | /*in/out*/ SSLVersionRange& range) |
908 | 0 | { |
909 | 0 | IntoleranceEntry entry; |
910 | 0 |
|
911 | 0 | { |
912 | 0 | nsCString key; |
913 | 0 | getSiteKey(hostName, port, key); |
914 | 0 |
|
915 | 0 | MutexAutoLock lock(mutex); |
916 | 0 | if (!mTLSIntoleranceInfo.Get(key, &entry)) { |
917 | 0 | return; |
918 | 0 | } |
919 | 0 | } |
920 | 0 | |
921 | 0 | entry.AssertInvariant(); |
922 | 0 |
|
923 | 0 | if (entry.intolerant != 0) { |
924 | 0 | // We've tried connecting at a higher range but failed, so try at the |
925 | 0 | // version we haven't tried yet, unless we have reached the minimum. |
926 | 0 | if (range.min < entry.intolerant) { |
927 | 0 | range.max = entry.intolerant - 1; |
928 | 0 | } |
929 | 0 | } |
930 | 0 | } |
931 | | |
932 | | PRErrorCode |
933 | | nsSSLIOLayerHelpers::getIntoleranceReason(const nsACString& hostName, |
934 | | int16_t port) |
935 | 0 | { |
936 | 0 | IntoleranceEntry entry; |
937 | 0 |
|
938 | 0 | { |
939 | 0 | nsCString key; |
940 | 0 | getSiteKey(hostName, port, key); |
941 | 0 |
|
942 | 0 | MutexAutoLock lock(mutex); |
943 | 0 | if (!mTLSIntoleranceInfo.Get(key, &entry)) { |
944 | 0 | return 0; |
945 | 0 | } |
946 | 0 | } |
947 | 0 | |
948 | 0 | entry.AssertInvariant(); |
949 | 0 | return entry.intoleranceReason; |
950 | 0 | } |
951 | | |
952 | | bool nsSSLIOLayerHelpers::nsSSLIOLayerInitialized = false; |
953 | | PRDescIdentity nsSSLIOLayerHelpers::nsSSLIOLayerIdentity; |
954 | | PRDescIdentity nsSSLIOLayerHelpers::nsSSLPlaintextLayerIdentity; |
955 | | PRIOMethods nsSSLIOLayerHelpers::nsSSLIOLayerMethods; |
956 | | PRIOMethods nsSSLIOLayerHelpers::nsSSLPlaintextLayerMethods; |
957 | | |
958 | | static PRStatus |
959 | | nsSSLIOLayerClose(PRFileDesc* fd) |
960 | 0 | { |
961 | 0 | if (!fd) |
962 | 0 | return PR_FAILURE; |
963 | 0 | |
964 | 0 | MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("[%p] Shutting down socket\n", |
965 | 0 | (void*) fd)); |
966 | 0 |
|
967 | 0 | nsNSSSocketInfo* socketInfo = (nsNSSSocketInfo*) fd->secret; |
968 | 0 | MOZ_ASSERT(socketInfo, "nsNSSSocketInfo was null for an fd"); |
969 | 0 |
|
970 | 0 | return socketInfo->CloseSocketAndDestroy(); |
971 | 0 | } |
972 | | |
973 | | PRStatus |
974 | | nsNSSSocketInfo::CloseSocketAndDestroy() |
975 | 0 | { |
976 | 0 | PRFileDesc* popped = PR_PopIOLayer(mFd, PR_TOP_IO_LAYER); |
977 | 0 | MOZ_ASSERT(popped && |
978 | 0 | popped->identity == nsSSLIOLayerHelpers::nsSSLIOLayerIdentity, |
979 | 0 | "SSL Layer not on top of stack"); |
980 | 0 |
|
981 | 0 | // The plaintext layer is not always present - so it's not a fatal error if it |
982 | 0 | // cannot be removed. |
983 | 0 | // Note that PR_PopIOLayer may modify its stack, so a pointer returned by |
984 | 0 | // PR_GetIdentitiesLayer may not point to what we think it points to after |
985 | 0 | // calling PR_PopIOLayer. We must operate on the pointer returned by |
986 | 0 | // PR_PopIOLayer. |
987 | 0 | if (PR_GetIdentitiesLayer(mFd, nsSSLIOLayerHelpers::nsSSLPlaintextLayerIdentity)) { |
988 | 0 | PRFileDesc* poppedPlaintext = |
989 | 0 | PR_PopIOLayer(mFd, nsSSLIOLayerHelpers::nsSSLPlaintextLayerIdentity); |
990 | 0 | poppedPlaintext->dtor(poppedPlaintext); |
991 | 0 | } |
992 | 0 |
|
993 | 0 | PRStatus status = mFd->methods->close(mFd); |
994 | 0 |
|
995 | 0 | // the nsNSSSocketInfo instance can out-live the connection, so we need some |
996 | 0 | // indication that the connection has been closed. mFd == nullptr is that |
997 | 0 | // indication. This is needed, for example, when the connection is closed |
998 | 0 | // before we have finished validating the server's certificate. |
999 | 0 | mFd = nullptr; |
1000 | 0 |
|
1001 | 0 | if (status != PR_SUCCESS) return status; |
1002 | 0 | |
1003 | 0 | popped->identity = PR_INVALID_IO_LAYER; |
1004 | 0 | NS_RELEASE_THIS(); |
1005 | 0 | popped->dtor(popped); |
1006 | 0 |
|
1007 | 0 | return PR_SUCCESS; |
1008 | 0 | } |
1009 | | |
1010 | | NS_IMETHODIMP |
1011 | | nsNSSSocketInfo::GetEsniTxt(nsACString & aEsniTxt) |
1012 | 0 | { |
1013 | 0 | aEsniTxt = mEsniTxt; |
1014 | 0 | return NS_OK; |
1015 | 0 | } |
1016 | | |
1017 | | NS_IMETHODIMP |
1018 | | nsNSSSocketInfo::SetEsniTxt(const nsACString & aEsniTxt) |
1019 | 0 | { |
1020 | 0 | mEsniTxt = aEsniTxt; |
1021 | 0 |
|
1022 | 0 | if (mEsniTxt.Length()) { |
1023 | 0 | fprintf(stderr,"\n\nTODO - SSL_EnableSNI() [%s] (%d bytes)\n", |
1024 | 0 | mEsniTxt.get(), mEsniTxt.Length()); |
1025 | 0 |
|
1026 | | #if 0 |
1027 | | if (SECSuccess != SSL_EnableESNI(mFd, |
1028 | | reinterpret_cast<const PRUint8*>(mEsniTxt.get()), |
1029 | | mEsniTxt.Length(), "dummy.invalid")) { |
1030 | | return NS_ERROR_FAILURE; |
1031 | | } |
1032 | | #endif |
1033 | | } |
1034 | 0 |
|
1035 | 0 | return NS_OK; |
1036 | 0 | } |
1037 | | |
1038 | | NS_IMETHODIMP |
1039 | | nsNSSSocketInfo::GetServerRootCertIsBuiltInRoot(bool *aIsBuiltInRoot) |
1040 | 0 | { |
1041 | 0 | *aIsBuiltInRoot = false; |
1042 | 0 |
|
1043 | 0 | if (!HasServerCert()) { |
1044 | 0 | return NS_ERROR_NOT_AVAILABLE; |
1045 | 0 | } |
1046 | 0 | |
1047 | 0 | nsCOMPtr<nsIX509CertList> certList; |
1048 | 0 | nsresult rv = GetSucceededCertChain(getter_AddRefs(certList)); |
1049 | 0 | if (NS_SUCCEEDED(rv)) { |
1050 | 0 | if (!certList) { |
1051 | 0 | return NS_ERROR_NOT_AVAILABLE; |
1052 | 0 | } |
1053 | 0 | RefPtr<nsNSSCertList> nssCertList = certList->GetCertList(); |
1054 | 0 | nsCOMPtr<nsIX509Cert> cert; |
1055 | 0 | rv = nssCertList->GetRootCertificate(cert); |
1056 | 0 | if (NS_SUCCEEDED(rv)) { |
1057 | 0 | if (!cert) { |
1058 | 0 | return NS_ERROR_NOT_AVAILABLE; |
1059 | 0 | } |
1060 | 0 | rv = cert->GetIsBuiltInRoot(aIsBuiltInRoot); |
1061 | 0 | } |
1062 | 0 | } |
1063 | 0 | return rv; |
1064 | 0 | } |
1065 | | |
1066 | | #if defined(DEBUG_SSL_VERBOSE) && defined(DUMP_BUFFER) |
1067 | | // Dumps a (potentially binary) buffer using SSM_DEBUG. (We could have used |
1068 | | // the version in ssltrace.c, but that's specifically tailored to SSLTRACE.) |
1069 | | #define DUMPBUF_LINESIZE 24 |
1070 | | static void |
1071 | | nsDumpBuffer(unsigned char* buf, int len) |
1072 | | { |
1073 | | char hexbuf[DUMPBUF_LINESIZE*3+1]; |
1074 | | char chrbuf[DUMPBUF_LINESIZE+1]; |
1075 | | static const char* hex = "0123456789abcdef"; |
1076 | | int i = 0; |
1077 | | int l = 0; |
1078 | | char ch; |
1079 | | char* c; |
1080 | | char* h; |
1081 | | if (len == 0) |
1082 | | return; |
1083 | | hexbuf[DUMPBUF_LINESIZE*3] = '\0'; |
1084 | | chrbuf[DUMPBUF_LINESIZE] = '\0'; |
1085 | | (void) memset(hexbuf, 0x20, DUMPBUF_LINESIZE*3); |
1086 | | (void) memset(chrbuf, 0x20, DUMPBUF_LINESIZE); |
1087 | | h = hexbuf; |
1088 | | c = chrbuf; |
1089 | | |
1090 | | while (i < len) { |
1091 | | ch = buf[i]; |
1092 | | |
1093 | | if (l == DUMPBUF_LINESIZE) { |
1094 | | MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("%s%s\n", hexbuf, chrbuf)); |
1095 | | (void) memset(hexbuf, 0x20, DUMPBUF_LINESIZE*3); |
1096 | | (void) memset(chrbuf, 0x20, DUMPBUF_LINESIZE); |
1097 | | h = hexbuf; |
1098 | | c = chrbuf; |
1099 | | l = 0; |
1100 | | } |
1101 | | |
1102 | | // Convert a character to hex. |
1103 | | *h++ = hex[(ch >> 4) & 0xf]; |
1104 | | *h++ = hex[ch & 0xf]; |
1105 | | h++; |
1106 | | |
1107 | | // Put the character (if it's printable) into the character buffer. |
1108 | | if ((ch >= 0x20) && (ch <= 0x7e)) { |
1109 | | *c++ = ch; |
1110 | | } else { |
1111 | | *c++ = '.'; |
1112 | | } |
1113 | | i++; l++; |
1114 | | } |
1115 | | MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("%s%s\n", hexbuf, chrbuf)); |
1116 | | } |
1117 | | |
1118 | | #define DEBUG_DUMP_BUFFER(buf,len) nsDumpBuffer(buf,len) |
1119 | | #else |
1120 | | #define DEBUG_DUMP_BUFFER(buf,len) |
1121 | | #endif |
1122 | | |
1123 | | class SSLErrorRunnable : public SyncRunnableBase |
1124 | | { |
1125 | | public: |
1126 | | SSLErrorRunnable(nsNSSSocketInfo* infoObject, |
1127 | | PRErrorCode errorCode) |
1128 | | : mInfoObject(infoObject) |
1129 | | , mErrorCode(errorCode) |
1130 | 0 | { |
1131 | 0 | } |
1132 | | |
1133 | | virtual void RunOnTargetThread() override |
1134 | 0 | { |
1135 | 0 | nsHandleSSLError(mInfoObject, mErrorCode); |
1136 | 0 | } |
1137 | | |
1138 | | RefPtr<nsNSSSocketInfo> mInfoObject; |
1139 | | const PRErrorCode mErrorCode; |
1140 | | }; |
1141 | | |
1142 | | namespace { |
1143 | | |
1144 | | uint32_t tlsIntoleranceTelemetryBucket(PRErrorCode err) |
1145 | | { |
1146 | | // returns a numeric code for where we track various errors in telemetry |
1147 | | // only errors that cause version fallback are tracked, |
1148 | | // so this is also used to determine which errors can cause version fallback |
1149 | | switch (err) { |
1150 | | case SSL_ERROR_BAD_MAC_ALERT: return 1; |
1151 | | case SSL_ERROR_BAD_MAC_READ: return 2; |
1152 | | case SSL_ERROR_HANDSHAKE_FAILURE_ALERT: return 3; |
1153 | | case SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT: return 4; |
1154 | | case SSL_ERROR_ILLEGAL_PARAMETER_ALERT: return 6; |
1155 | | case SSL_ERROR_NO_CYPHER_OVERLAP: return 7; |
1156 | | case SSL_ERROR_UNSUPPORTED_VERSION: return 10; |
1157 | | case SSL_ERROR_PROTOCOL_VERSION_ALERT: return 11; |
1158 | | case SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE: return 13; |
1159 | | case SSL_ERROR_DECODE_ERROR_ALERT: return 14; |
1160 | | case PR_CONNECT_RESET_ERROR: return 16; |
1161 | | case PR_END_OF_FILE_ERROR: return 17; |
1162 | | case SSL_ERROR_INTERNAL_ERROR_ALERT: return 18; |
1163 | | default: return 0; |
1164 | | } |
1165 | | } |
1166 | | |
1167 | | bool |
1168 | | retryDueToTLSIntolerance(PRErrorCode err, nsNSSSocketInfo* socketInfo) |
1169 | 0 | { |
1170 | 0 | // This function is supposed to decide which error codes should |
1171 | 0 | // be used to conclude server is TLS intolerant. |
1172 | 0 | // Note this only happens during the initial SSL handshake. |
1173 | 0 |
|
1174 | 0 | SSLVersionRange range = socketInfo->GetTLSVersionRange(); |
1175 | 0 | nsSSLIOLayerHelpers& helpers = socketInfo->SharedState().IOLayerHelpers(); |
1176 | 0 |
|
1177 | 0 | if (err == SSL_ERROR_UNSUPPORTED_VERSION && |
1178 | 0 | range.min == SSL_LIBRARY_VERSION_TLS_1_0) { |
1179 | 0 | socketInfo->SetSecurityState(nsIWebProgressListener::STATE_IS_INSECURE | |
1180 | 0 | nsIWebProgressListener::STATE_USES_SSL_3); |
1181 | 0 | } |
1182 | 0 |
|
1183 | 0 | // NSS will return SSL_ERROR_RX_MALFORMED_SERVER_HELLO if anti-downgrade |
1184 | 0 | // detected the downgrade. |
1185 | 0 | if (err == SSL_ERROR_INAPPROPRIATE_FALLBACK_ALERT || |
1186 | 0 | err == SSL_ERROR_RX_MALFORMED_SERVER_HELLO) { |
1187 | 0 | // This is a clear signal that we've fallen back too many versions. Treat |
1188 | 0 | // this as a hard failure, but forget any intolerance so that later attempts |
1189 | 0 | // don't use this version (i.e., range.max) and trigger the error again. |
1190 | 0 |
|
1191 | 0 | // First, track the original cause of the version fallback. This uses the |
1192 | 0 | // same buckets as the telemetry below, except that bucket 0 will include |
1193 | 0 | // all cases where there wasn't an original reason. |
1194 | 0 | PRErrorCode originalReason = |
1195 | 0 | helpers.getIntoleranceReason(socketInfo->GetHostName(), |
1196 | 0 | socketInfo->GetPort()); |
1197 | 0 | Telemetry::Accumulate(Telemetry::SSL_VERSION_FALLBACK_INAPPROPRIATE, |
1198 | 0 | tlsIntoleranceTelemetryBucket(originalReason)); |
1199 | 0 |
|
1200 | 0 | helpers.forgetIntolerance(socketInfo->GetHostName(), |
1201 | 0 | socketInfo->GetPort()); |
1202 | 0 |
|
1203 | 0 | return false; |
1204 | 0 | } |
1205 | 0 | |
1206 | 0 | // When not using a proxy we'll see a connection reset error. |
1207 | 0 | // When using a proxy, we'll see an end of file error. |
1208 | 0 | |
1209 | 0 | // Don't allow STARTTLS connections to fall back on connection resets or |
1210 | 0 | // EOF. |
1211 | 0 | if ((err == PR_CONNECT_RESET_ERROR || err == PR_END_OF_FILE_ERROR) |
1212 | 0 | && socketInfo->GetForSTARTTLS()) { |
1213 | 0 | return false; |
1214 | 0 | } |
1215 | 0 | |
1216 | 0 | uint32_t reason = tlsIntoleranceTelemetryBucket(err); |
1217 | 0 | if (reason == 0) { |
1218 | 0 | return false; |
1219 | 0 | } |
1220 | 0 | |
1221 | 0 | Telemetry::HistogramID pre; |
1222 | 0 | Telemetry::HistogramID post; |
1223 | 0 | switch (range.max) { |
1224 | 0 | case SSL_LIBRARY_VERSION_TLS_1_3: |
1225 | 0 | pre = Telemetry::SSL_TLS13_INTOLERANCE_REASON_PRE; |
1226 | 0 | post = Telemetry::SSL_TLS13_INTOLERANCE_REASON_POST; |
1227 | 0 | break; |
1228 | 0 | case SSL_LIBRARY_VERSION_TLS_1_2: |
1229 | 0 | pre = Telemetry::SSL_TLS12_INTOLERANCE_REASON_PRE; |
1230 | 0 | post = Telemetry::SSL_TLS12_INTOLERANCE_REASON_POST; |
1231 | 0 | break; |
1232 | 0 | case SSL_LIBRARY_VERSION_TLS_1_1: |
1233 | 0 | pre = Telemetry::SSL_TLS11_INTOLERANCE_REASON_PRE; |
1234 | 0 | post = Telemetry::SSL_TLS11_INTOLERANCE_REASON_POST; |
1235 | 0 | break; |
1236 | 0 | case SSL_LIBRARY_VERSION_TLS_1_0: |
1237 | 0 | pre = Telemetry::SSL_TLS10_INTOLERANCE_REASON_PRE; |
1238 | 0 | post = Telemetry::SSL_TLS10_INTOLERANCE_REASON_POST; |
1239 | 0 | break; |
1240 | 0 | default: |
1241 | 0 | MOZ_CRASH("impossible TLS version"); |
1242 | 0 | return false; |
1243 | 0 | } |
1244 | 0 | |
1245 | 0 | // The difference between _PRE and _POST represents how often we avoided |
1246 | 0 | // TLS intolerance fallback due to remembered tolerance. |
1247 | 0 | Telemetry::Accumulate(pre, reason); |
1248 | 0 |
|
1249 | 0 | if (!helpers.rememberIntolerantAtVersion(socketInfo->GetHostName(), |
1250 | 0 | socketInfo->GetPort(), |
1251 | 0 | range.min, range.max, err)) { |
1252 | 0 | return false; |
1253 | 0 | } |
1254 | 0 | |
1255 | 0 | Telemetry::Accumulate(post, reason); |
1256 | 0 |
|
1257 | 0 | return true; |
1258 | 0 | } |
1259 | | |
1260 | | // Ensure that we haven't added too many errors to fit. |
1261 | | static_assert((SSL_ERROR_END_OF_LIST - SSL_ERROR_BASE) <= 256, |
1262 | | "too many SSL errors"); |
1263 | | static_assert((SEC_ERROR_END_OF_LIST - SEC_ERROR_BASE) <= 256, |
1264 | | "too many SEC errors"); |
1265 | | static_assert((PR_MAX_ERROR - PR_NSPR_ERROR_BASE) <= 128, |
1266 | | "too many NSPR errors"); |
1267 | | static_assert((mozilla::pkix::ERROR_BASE - mozilla::pkix::END_OF_LIST) < 31, |
1268 | | "too many moz::pkix errors"); |
1269 | | |
1270 | | static void |
1271 | | reportHandshakeResult(int32_t bytesTransferred, bool wasReading, PRErrorCode err) |
1272 | 0 | { |
1273 | 0 | uint32_t bucket; |
1274 | 0 |
|
1275 | 0 | // A negative bytesTransferred or a 0 read are errors. |
1276 | 0 | if (bytesTransferred > 0) { |
1277 | 0 | bucket = 0; |
1278 | 0 | } else if ((bytesTransferred == 0) && !wasReading) { |
1279 | 0 | // PR_Write() is defined to never return 0, but let's make sure. |
1280 | 0 | // https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSPR/Reference/PR_Write. |
1281 | 0 | MOZ_ASSERT(false); |
1282 | 0 | bucket = 671; |
1283 | 0 | } else if (IS_SSL_ERROR(err)) { |
1284 | 0 | bucket = err - SSL_ERROR_BASE; |
1285 | 0 | MOZ_ASSERT(bucket > 0); // SSL_ERROR_EXPORT_ONLY_SERVER isn't used. |
1286 | 0 | } else if (IS_SEC_ERROR(err)) { |
1287 | 0 | bucket = (err - SEC_ERROR_BASE) + 256; |
1288 | 0 | } else if ((err >= PR_NSPR_ERROR_BASE) && (err < PR_MAX_ERROR)) { |
1289 | 0 | bucket = (err - PR_NSPR_ERROR_BASE) + 512; |
1290 | 0 | } else if ((err >= mozilla::pkix::ERROR_BASE) && |
1291 | 0 | (err < mozilla::pkix::ERROR_LIMIT)) { |
1292 | 0 | bucket = (err - mozilla::pkix::ERROR_BASE) + 640; |
1293 | 0 | } else { |
1294 | 0 | bucket = 671; |
1295 | 0 | } |
1296 | 0 |
|
1297 | 0 | Telemetry::Accumulate(Telemetry::SSL_HANDSHAKE_RESULT, bucket); |
1298 | 0 | } |
1299 | | |
1300 | | int32_t |
1301 | | checkHandshake(int32_t bytesTransfered, bool wasReading, |
1302 | | PRFileDesc* ssl_layer_fd, nsNSSSocketInfo* socketInfo) |
1303 | 0 | { |
1304 | 0 | const PRErrorCode originalError = PR_GetError(); |
1305 | 0 | PRErrorCode err = originalError; |
1306 | 0 |
|
1307 | 0 | // This is where we work around all of those SSL servers that don't |
1308 | 0 | // conform to the SSL spec and shutdown a connection when we request |
1309 | 0 | // SSL v3.1 (aka TLS). The spec says the client says what version |
1310 | 0 | // of the protocol we're willing to perform, in our case SSL v3.1 |
1311 | 0 | // In its response, the server says which version it wants to perform. |
1312 | 0 | // Many servers out there only know how to do v3.0. Next, we're supposed |
1313 | 0 | // to send back the version of the protocol we requested (ie v3.1). At |
1314 | 0 | // this point many servers's implementations are broken and they shut |
1315 | 0 | // down the connection when they don't see the version they sent back. |
1316 | 0 | // This is supposed to prevent a man in the middle from forcing one |
1317 | 0 | // side to dumb down to a lower level of the protocol. Unfortunately, |
1318 | 0 | // there are enough broken servers out there that such a gross work-around |
1319 | 0 | // is necessary. :( |
1320 | 0 |
|
1321 | 0 | // Do NOT assume TLS intolerance on a closed connection after bad cert ui was shown. |
1322 | 0 | // Simply retry. |
1323 | 0 | // This depends on the fact that Cert UI will not be shown again, |
1324 | 0 | // should the user override the bad cert. |
1325 | 0 |
|
1326 | 0 | bool handleHandshakeResultNow = socketInfo->IsHandshakePending(); |
1327 | 0 |
|
1328 | 0 | bool wantRetry = false; |
1329 | 0 |
|
1330 | 0 | if (0 > bytesTransfered) { |
1331 | 0 | if (handleHandshakeResultNow) { |
1332 | 0 | if (PR_WOULD_BLOCK_ERROR == err) { |
1333 | 0 | PR_SetError(err, 0); |
1334 | 0 | return bytesTransfered; |
1335 | 0 | } |
1336 | 0 | |
1337 | 0 | wantRetry = retryDueToTLSIntolerance(err, socketInfo); |
1338 | 0 | } |
1339 | 0 |
|
1340 | 0 | // This is the common place where we trigger non-cert-errors on a SSL |
1341 | 0 | // socket. This might be reached at any time of the connection. |
1342 | 0 | // |
1343 | 0 | // The socketInfo->GetErrorCode() check is here to ensure we don't try to |
1344 | 0 | // do the synchronous dispatch to the main thread unnecessarily after we've |
1345 | 0 | // already handled a certificate error. (SSLErrorRunnable calls |
1346 | 0 | // nsHandleSSLError, which has logic to avoid replacing the error message, |
1347 | 0 | // so without the !socketInfo->GetErrorCode(), it would just be an |
1348 | 0 | // expensive no-op.) |
1349 | 0 | if (!wantRetry && mozilla::psm::IsNSSErrorCode(err) && |
1350 | 0 | !socketInfo->GetErrorCode()) { |
1351 | 0 | RefPtr<SyncRunnableBase> runnable( |
1352 | 0 | new SSLErrorRunnable(socketInfo, err)); |
1353 | 0 | (void) runnable->DispatchToMainThreadAndWait(); |
1354 | 0 | } |
1355 | 0 | } else if (wasReading && 0 == bytesTransfered) { |
1356 | 0 | // zero bytes on reading, socket closed |
1357 | 0 | if (handleHandshakeResultNow) { |
1358 | 0 | wantRetry = retryDueToTLSIntolerance(PR_END_OF_FILE_ERROR, socketInfo); |
1359 | 0 | } |
1360 | 0 | } |
1361 | 0 |
|
1362 | 0 | if (wantRetry) { |
1363 | 0 | MOZ_LOG(gPIPNSSLog, LogLevel::Debug, |
1364 | 0 | ("[%p] checkHandshake: will retry with lower max TLS version\n", |
1365 | 0 | ssl_layer_fd)); |
1366 | 0 | // We want to cause the network layer to retry the connection. |
1367 | 0 | err = PR_CONNECT_RESET_ERROR; |
1368 | 0 | if (wasReading) |
1369 | 0 | bytesTransfered = -1; |
1370 | 0 | } |
1371 | 0 |
|
1372 | 0 | // TLS intolerant servers only cause the first transfer to fail, so let's |
1373 | 0 | // set the HandshakePending attribute to false so that we don't try the logic |
1374 | 0 | // above again in a subsequent transfer. |
1375 | 0 | if (handleHandshakeResultNow) { |
1376 | 0 | // Report the result once for each handshake. Note that this does not |
1377 | 0 | // get handshakes which are cancelled before any reads or writes |
1378 | 0 | // happen. |
1379 | 0 | reportHandshakeResult(bytesTransfered, wasReading, originalError); |
1380 | 0 | socketInfo->SetHandshakeNotPending(); |
1381 | 0 | } |
1382 | 0 |
|
1383 | 0 | if (bytesTransfered < 0) { |
1384 | 0 | // Remember that we encountered an error so that getSocketInfoIfRunning |
1385 | 0 | // will correctly cause us to fail if another part of Gecko |
1386 | 0 | // (erroneously) calls an I/O function (PR_Send/PR_Recv/etc.) again on |
1387 | 0 | // this socket. Note that we use the original error because if we use |
1388 | 0 | // PR_CONNECT_RESET_ERROR, we'll repeated try to reconnect. |
1389 | 0 | if (originalError != PR_WOULD_BLOCK_ERROR && !socketInfo->GetErrorCode()) { |
1390 | 0 | socketInfo->SetCanceled(originalError); |
1391 | 0 | } |
1392 | 0 | PR_SetError(err, 0); |
1393 | 0 | } |
1394 | 0 |
|
1395 | 0 | return bytesTransfered; |
1396 | 0 | } |
1397 | | |
1398 | | } // namespace |
1399 | | |
1400 | | static int16_t |
1401 | | nsSSLIOLayerPoll(PRFileDesc* fd, int16_t in_flags, int16_t* out_flags) |
1402 | 0 | { |
1403 | 0 | if (!out_flags) { |
1404 | 0 | NS_WARNING("nsSSLIOLayerPoll called with null out_flags"); |
1405 | 0 | return 0; |
1406 | 0 | } |
1407 | 0 |
|
1408 | 0 | *out_flags = 0; |
1409 | 0 |
|
1410 | 0 | nsNSSSocketInfo* socketInfo = |
1411 | 0 | getSocketInfoIfRunning(fd, not_reading_or_writing); |
1412 | 0 |
|
1413 | 0 | if (!socketInfo) { |
1414 | 0 | // If we get here, it is probably because certificate validation failed |
1415 | 0 | // and this is the first I/O operation after the failure. |
1416 | 0 | MOZ_LOG(gPIPNSSLog, LogLevel::Debug, |
1417 | 0 | ("[%p] polling SSL socket right after certificate verification failed " |
1418 | 0 | "or NSS shutdown or SDR logout %d\n", |
1419 | 0 | fd, (int) in_flags)); |
1420 | 0 |
|
1421 | 0 | MOZ_ASSERT(in_flags & PR_POLL_EXCEPT, |
1422 | 0 | "Caller did not poll for EXCEPT (canceled)"); |
1423 | 0 | // Since this poll method cannot return errors, we want the caller to call |
1424 | 0 | // PR_Send/PR_Recv right away to get the error, so we tell that we are |
1425 | 0 | // ready for whatever I/O they are asking for. (See getSocketInfoIfRunning). |
1426 | 0 | *out_flags = in_flags | PR_POLL_EXCEPT; // see also bug 480619 |
1427 | 0 | return in_flags; |
1428 | 0 | } |
1429 | 0 |
|
1430 | 0 | MOZ_LOG(gPIPNSSLog, LogLevel::Verbose, |
1431 | 0 | (socketInfo->IsWaitingForCertVerification() |
1432 | 0 | ? "[%p] polling SSL socket during certificate verification using lower %d\n" |
1433 | 0 | : "[%p] poll SSL socket using lower %d\n", |
1434 | 0 | fd, (int) in_flags)); |
1435 | 0 |
|
1436 | 0 | // We want the handshake to continue during certificate validation, so we |
1437 | 0 | // don't need to do anything special here. libssl automatically blocks when |
1438 | 0 | // it reaches any point that would be unsafe to send/receive something before |
1439 | 0 | // cert validation is complete. |
1440 | 0 | int16_t result = fd->lower->methods->poll(fd->lower, in_flags, out_flags); |
1441 | 0 | MOZ_LOG(gPIPNSSLog, LogLevel::Verbose, |
1442 | 0 | ("[%p] poll SSL socket returned %d\n", (void*) fd, (int) result)); |
1443 | 0 | return result; |
1444 | 0 | } |
1445 | | |
1446 | | nsSSLIOLayerHelpers::nsSSLIOLayerHelpers(uint32_t aTlsFlags) |
1447 | | : mTreatUnsafeNegotiationAsBroken(false) |
1448 | | , mTLSIntoleranceInfo() |
1449 | | , mVersionFallbackLimit(SSL_LIBRARY_VERSION_TLS_1_0) |
1450 | | , mutex("nsSSLIOLayerHelpers.mutex") |
1451 | | , mTlsFlags(aTlsFlags) |
1452 | 0 | { |
1453 | 0 | } |
1454 | | |
1455 | | // PSMAvailable and PSMAvailable64 are reachable, but they're unimplemented in |
1456 | | // PSM, so we set an error and return -1. |
1457 | | static int32_t |
1458 | | PSMAvailable(PRFileDesc*) |
1459 | 0 | { |
1460 | 0 | PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); |
1461 | 0 | return -1; |
1462 | 0 | } |
1463 | | |
1464 | | static int64_t |
1465 | | PSMAvailable64(PRFileDesc*) |
1466 | 0 | { |
1467 | 0 | PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); |
1468 | 0 | return -1; |
1469 | 0 | } |
1470 | | |
1471 | | static PRStatus |
1472 | | PSMGetsockname(PRFileDesc* fd, PRNetAddr* addr) |
1473 | 0 | { |
1474 | 0 | if (!getSocketInfoIfRunning(fd, not_reading_or_writing)) |
1475 | 0 | return PR_FAILURE; |
1476 | 0 | |
1477 | 0 | return fd->lower->methods->getsockname(fd->lower, addr); |
1478 | 0 | } |
1479 | | |
1480 | | static PRStatus |
1481 | | PSMGetpeername(PRFileDesc* fd, PRNetAddr* addr) |
1482 | 0 | { |
1483 | 0 | if (!getSocketInfoIfRunning(fd, not_reading_or_writing)) |
1484 | 0 | return PR_FAILURE; |
1485 | 0 | |
1486 | 0 | return fd->lower->methods->getpeername(fd->lower, addr); |
1487 | 0 | } |
1488 | | |
1489 | | static PRStatus |
1490 | | PSMGetsocketoption(PRFileDesc* fd, PRSocketOptionData* data) |
1491 | 0 | { |
1492 | 0 | if (!getSocketInfoIfRunning(fd, not_reading_or_writing)) |
1493 | 0 | return PR_FAILURE; |
1494 | 0 | |
1495 | 0 | return fd->lower->methods->getsocketoption(fd, data); |
1496 | 0 | } |
1497 | | |
1498 | | static PRStatus |
1499 | | PSMSetsocketoption(PRFileDesc* fd, const PRSocketOptionData* data) |
1500 | 0 | { |
1501 | 0 | if (!getSocketInfoIfRunning(fd, not_reading_or_writing)) |
1502 | 0 | return PR_FAILURE; |
1503 | 0 | |
1504 | 0 | return fd->lower->methods->setsocketoption(fd, data); |
1505 | 0 | } |
1506 | | |
1507 | | static int32_t |
1508 | | PSMRecv(PRFileDesc* fd, void* buf, int32_t amount, int flags, |
1509 | | PRIntervalTime timeout) |
1510 | 0 | { |
1511 | 0 | nsNSSSocketInfo* socketInfo = getSocketInfoIfRunning(fd, reading); |
1512 | 0 | if (!socketInfo) |
1513 | 0 | return -1; |
1514 | 0 | |
1515 | 0 | if (flags != PR_MSG_PEEK && flags != 0) { |
1516 | 0 | PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); |
1517 | 0 | return -1; |
1518 | 0 | } |
1519 | 0 | |
1520 | 0 | int32_t bytesRead = fd->lower->methods->recv(fd->lower, buf, amount, flags, |
1521 | 0 | timeout); |
1522 | 0 |
|
1523 | 0 | MOZ_LOG(gPIPNSSLog, LogLevel::Verbose, |
1524 | 0 | ("[%p] read %d bytes\n", (void*) fd, bytesRead)); |
1525 | 0 |
|
1526 | | #ifdef DEBUG_SSL_VERBOSE |
1527 | | DEBUG_DUMP_BUFFER((unsigned char*) buf, bytesRead); |
1528 | | #endif |
1529 | |
|
1530 | 0 | return checkHandshake(bytesRead, true, fd, socketInfo); |
1531 | 0 | } |
1532 | | |
1533 | | static int32_t |
1534 | | PSMSend(PRFileDesc* fd, const void* buf, int32_t amount, int flags, |
1535 | | PRIntervalTime timeout) |
1536 | 0 | { |
1537 | 0 | nsNSSSocketInfo* socketInfo = getSocketInfoIfRunning(fd, writing); |
1538 | 0 | if (!socketInfo) |
1539 | 0 | return -1; |
1540 | 0 | |
1541 | 0 | if (flags != 0) { |
1542 | 0 | PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); |
1543 | 0 | return -1; |
1544 | 0 | } |
1545 | 0 | |
1546 | | #ifdef DEBUG_SSL_VERBOSE |
1547 | | DEBUG_DUMP_BUFFER((unsigned char*) buf, amount); |
1548 | | #endif |
1549 | | |
1550 | 0 | if (socketInfo->IsShortWritePending() && amount > 0) { |
1551 | 0 | // We got "SSL short write" last time, try to flush the pending byte. |
1552 | | #ifdef DEBUG |
1553 | | socketInfo->CheckShortWrittenBuffer(static_cast<const unsigned char*>(buf), amount); |
1554 | | #endif |
1555 | |
|
1556 | 0 | buf = socketInfo->GetShortWritePendingByteRef(); |
1557 | 0 | amount = 1; |
1558 | 0 |
|
1559 | 0 | MOZ_LOG(gPIPNSSLog, LogLevel::Verbose, |
1560 | 0 | ("[%p] pushing 1 byte after SSL short write", fd)); |
1561 | 0 | } |
1562 | 0 |
|
1563 | 0 | int32_t bytesWritten = fd->lower->methods->send(fd->lower, buf, amount, |
1564 | 0 | flags, timeout); |
1565 | 0 |
|
1566 | 0 | // NSS indicates that it can't write all requested data (due to network |
1567 | 0 | // congestion, for example) by returning either one less than the amount |
1568 | 0 | // of data requested or 16383, if the requested amount is greater than |
1569 | 0 | // 16384. We refer to this as a "short write". If we simply returned |
1570 | 0 | // the amount that NSS did write, the layer above us would then call |
1571 | 0 | // PSMSend with a very small amount of data (often 1). This is inefficient |
1572 | 0 | // and can lead to alternating between sending large packets and very small |
1573 | 0 | // packets. To prevent this, we alert the layer calling us that the operation |
1574 | 0 | // would block and that it should be retried later, with the same data. |
1575 | 0 | // When it does, we tell NSS to write the remaining byte it didn't write |
1576 | 0 | // in the previous call. We then return the total number of bytes written, |
1577 | 0 | // which is the number that caused the short write plus the additional byte |
1578 | 0 | // we just wrote out. |
1579 | 0 |
|
1580 | 0 | // The 16384 value is based on libssl's maximum buffer size: |
1581 | 0 | // MAX_FRAGMENT_LENGTH - 1 |
1582 | 0 | // |
1583 | 0 | // It's in a private header, though, filed bug 1394822 to expose it. |
1584 | 0 | static const int32_t kShortWrite16k = 16383; |
1585 | 0 |
|
1586 | 0 | if ((amount > 1 && bytesWritten == (amount - 1)) || |
1587 | 0 | (amount > kShortWrite16k && bytesWritten == kShortWrite16k)) { |
1588 | 0 | // This is indication of an "SSL short write", block to force retry. |
1589 | 0 | socketInfo->SetShortWritePending( |
1590 | 0 | bytesWritten + 1, // The amount to return after the flush |
1591 | 0 | *(static_cast<const unsigned char*>(buf) + bytesWritten)); |
1592 | 0 |
|
1593 | 0 | MOZ_LOG(gPIPNSSLog, LogLevel::Verbose, |
1594 | 0 | ("[%p] indicated SSL short write for %d bytes (written just %d bytes)", |
1595 | 0 | fd, amount, bytesWritten)); |
1596 | 0 |
|
1597 | 0 | bytesWritten = -1; |
1598 | 0 | PR_SetError(PR_WOULD_BLOCK_ERROR, 0); |
1599 | 0 |
|
1600 | | #ifdef DEBUG |
1601 | | socketInfo->RememberShortWrittenBuffer(static_cast<const unsigned char*>(buf)); |
1602 | | #endif |
1603 | |
|
1604 | 0 | } else if (socketInfo->IsShortWritePending() && bytesWritten == 1) { |
1605 | 0 | // We have now flushed all pending data in the SSL socket |
1606 | 0 | // after the indicated short write. Tell the upper layer |
1607 | 0 | // it has sent all its data now. |
1608 | 0 | MOZ_LOG(gPIPNSSLog, LogLevel::Verbose, ("[%p] finished SSL short write", fd)); |
1609 | 0 |
|
1610 | 0 | bytesWritten = socketInfo->ResetShortWritePending(); |
1611 | 0 | } |
1612 | 0 |
|
1613 | 0 | MOZ_LOG(gPIPNSSLog, LogLevel::Verbose, |
1614 | 0 | ("[%p] wrote %d bytes\n", fd, bytesWritten)); |
1615 | 0 |
|
1616 | 0 | return checkHandshake(bytesWritten, false, fd, socketInfo); |
1617 | 0 | } |
1618 | | |
1619 | | static PRStatus |
1620 | | PSMBind(PRFileDesc* fd, const PRNetAddr *addr) |
1621 | 0 | { |
1622 | 0 | if (!getSocketInfoIfRunning(fd, not_reading_or_writing)) |
1623 | 0 | return PR_FAILURE; |
1624 | 0 | |
1625 | 0 | return fd->lower->methods->bind(fd->lower, addr); |
1626 | 0 | } |
1627 | | |
1628 | | static int32_t |
1629 | | nsSSLIOLayerRead(PRFileDesc* fd, void* buf, int32_t amount) |
1630 | 0 | { |
1631 | 0 | return PSMRecv(fd, buf, amount, 0, PR_INTERVAL_NO_TIMEOUT); |
1632 | 0 | } |
1633 | | |
1634 | | static int32_t |
1635 | | nsSSLIOLayerWrite(PRFileDesc* fd, const void* buf, int32_t amount) |
1636 | 0 | { |
1637 | 0 | return PSMSend(fd, buf, amount, 0, PR_INTERVAL_NO_TIMEOUT); |
1638 | 0 | } |
1639 | | |
1640 | | static PRStatus |
1641 | | PSMConnectcontinue(PRFileDesc* fd, int16_t out_flags) |
1642 | 0 | { |
1643 | 0 | if (!getSocketInfoIfRunning(fd, not_reading_or_writing)) { |
1644 | 0 | return PR_FAILURE; |
1645 | 0 | } |
1646 | 0 | |
1647 | 0 | return fd->lower->methods->connectcontinue(fd, out_flags); |
1648 | 0 | } |
1649 | | |
1650 | | namespace { |
1651 | | |
1652 | | class PrefObserver : public nsIObserver { |
1653 | | public: |
1654 | | NS_DECL_THREADSAFE_ISUPPORTS |
1655 | | NS_DECL_NSIOBSERVER |
1656 | 0 | explicit PrefObserver(nsSSLIOLayerHelpers* aOwner) : mOwner(aOwner) {} |
1657 | | |
1658 | | protected: |
1659 | 0 | virtual ~PrefObserver() {} |
1660 | | private: |
1661 | | nsSSLIOLayerHelpers* mOwner; |
1662 | | }; |
1663 | | |
1664 | | } // unnamed namespace |
1665 | | |
1666 | | NS_IMPL_ISUPPORTS(PrefObserver, nsIObserver) |
1667 | | |
1668 | | NS_IMETHODIMP |
1669 | | PrefObserver::Observe(nsISupports* aSubject, const char* aTopic, |
1670 | | const char16_t* someData) |
1671 | 0 | { |
1672 | 0 | if (nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0) { |
1673 | 0 | NS_ConvertUTF16toUTF8 prefName(someData); |
1674 | 0 |
|
1675 | 0 | if (prefName.EqualsLiteral("security.ssl.treat_unsafe_negotiation_as_broken")) { |
1676 | 0 | bool enabled; |
1677 | 0 | Preferences::GetBool("security.ssl.treat_unsafe_negotiation_as_broken", &enabled); |
1678 | 0 | mOwner->setTreatUnsafeNegotiationAsBroken(enabled); |
1679 | 0 | } else if (prefName.EqualsLiteral("security.tls.version.fallback-limit")) { |
1680 | 0 | mOwner->loadVersionFallbackLimit(); |
1681 | 0 | } else if (prefName.EqualsLiteral("security.tls.insecure_fallback_hosts")) { |
1682 | 0 | // Changes to the whitelist on the public side will update the pref. |
1683 | 0 | // Don't propagate the changes to the private side. |
1684 | 0 | if (mOwner->isPublic()) { |
1685 | 0 | mOwner->initInsecureFallbackSites(); |
1686 | 0 | } |
1687 | 0 | } |
1688 | 0 | } |
1689 | 0 | return NS_OK; |
1690 | 0 | } |
1691 | | |
1692 | | static int32_t |
1693 | | PlaintextRecv(PRFileDesc* fd, void* buf, int32_t amount, int flags, |
1694 | | PRIntervalTime timeout) |
1695 | 0 | { |
1696 | 0 | // The shutdownlocker is not needed here because it will already be |
1697 | 0 | // held higher in the stack |
1698 | 0 | nsNSSSocketInfo* socketInfo = nullptr; |
1699 | 0 |
|
1700 | 0 | int32_t bytesRead = fd->lower->methods->recv(fd->lower, buf, amount, flags, |
1701 | 0 | timeout); |
1702 | 0 | if (fd->identity == nsSSLIOLayerHelpers::nsSSLPlaintextLayerIdentity) |
1703 | 0 | socketInfo = (nsNSSSocketInfo*) fd->secret; |
1704 | 0 |
|
1705 | 0 | if ((bytesRead > 0) && socketInfo) |
1706 | 0 | socketInfo->AddPlaintextBytesRead(bytesRead); |
1707 | 0 | return bytesRead; |
1708 | 0 | } |
1709 | | |
1710 | | nsSSLIOLayerHelpers::~nsSSLIOLayerHelpers() |
1711 | 0 | { |
1712 | 0 | // mPrefObserver will only be set if this->Init was called. The GTest tests |
1713 | 0 | // do not call Init. |
1714 | 0 | if (mPrefObserver) { |
1715 | 0 | Preferences::RemoveObserver(mPrefObserver, |
1716 | 0 | "security.ssl.treat_unsafe_negotiation_as_broken"); |
1717 | 0 | Preferences::RemoveObserver(mPrefObserver, |
1718 | 0 | "security.tls.version.fallback-limit"); |
1719 | 0 | Preferences::RemoveObserver(mPrefObserver, |
1720 | 0 | "security.tls.insecure_fallback_hosts"); |
1721 | 0 | } |
1722 | 0 | } |
1723 | | |
1724 | | template <typename R, R return_value, typename... Args> |
1725 | | static R |
1726 | | InvalidPRIOMethod(Args...) |
1727 | 0 | { |
1728 | 0 | MOZ_ASSERT_UNREACHABLE("I/O method is invalid"); |
1729 | 0 | PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); |
1730 | 0 | return return_value; |
1731 | 0 | } Unexecuted instantiation: Unified_cpp_security_manager_ssl2.cpp:PRStatus InvalidPRIOMethod<PRStatus, (PRStatus)-1, PRFileDesc*>(PRFileDesc*) Unexecuted instantiation: Unified_cpp_security_manager_ssl2.cpp:int InvalidPRIOMethod<int, -1, PRFileDesc*, int, PRSeekWhence>(PRFileDesc*, int, PRSeekWhence) Unexecuted instantiation: Unified_cpp_security_manager_ssl2.cpp:long InvalidPRIOMethod<long, -1l, PRFileDesc*, long, PRSeekWhence>(PRFileDesc*, long, PRSeekWhence) Unexecuted instantiation: Unified_cpp_security_manager_ssl2.cpp:PRStatus InvalidPRIOMethod<PRStatus, (PRStatus)-1, PRFileDesc*, PRFileInfo*>(PRFileDesc*, PRFileInfo*) Unexecuted instantiation: Unified_cpp_security_manager_ssl2.cpp:PRStatus InvalidPRIOMethod<PRStatus, (PRStatus)-1, PRFileDesc*, PRFileInfo64*>(PRFileDesc*, PRFileInfo64*) Unexecuted instantiation: Unified_cpp_security_manager_ssl2.cpp:int InvalidPRIOMethod<int, -1, PRFileDesc*, PRIOVec const*, int, unsigned int>(PRFileDesc*, PRIOVec const*, int, unsigned int) Unexecuted instantiation: Unified_cpp_security_manager_ssl2.cpp:PRFileDesc* InvalidPRIOMethod<PRFileDesc*, (PRFileDesc*)0, PRFileDesc*, PRNetAddr*, unsigned int>(PRFileDesc*, PRNetAddr*, unsigned int) Unexecuted instantiation: Unified_cpp_security_manager_ssl2.cpp:PRStatus InvalidPRIOMethod<PRStatus, (PRStatus)-1, PRFileDesc*, int>(PRFileDesc*, int) Unexecuted instantiation: Unified_cpp_security_manager_ssl2.cpp:int InvalidPRIOMethod<int, -1, PRFileDesc*, void*, int, int, PRNetAddr*, unsigned int>(PRFileDesc*, void*, int, int, PRNetAddr*, unsigned int) Unexecuted instantiation: Unified_cpp_security_manager_ssl2.cpp:int InvalidPRIOMethod<int, -1, PRFileDesc*, void const*, int, int, PRNetAddr const*, unsigned int>(PRFileDesc*, void const*, int, int, PRNetAddr const*, unsigned int) Unexecuted instantiation: Unified_cpp_security_manager_ssl2.cpp:int InvalidPRIOMethod<int, -1, PRFileDesc*, PRFileDesc**, PRNetAddr**, void*, int, unsigned int>(PRFileDesc*, PRFileDesc**, PRNetAddr**, void*, int, unsigned int) Unexecuted instantiation: Unified_cpp_security_manager_ssl2.cpp:int InvalidPRIOMethod<int, -1, PRFileDesc*, PRFileDesc*, void const*, int, PRTransmitFileFlags, unsigned int>(PRFileDesc*, PRFileDesc*, void const*, int, PRTransmitFileFlags, unsigned int) Unexecuted instantiation: Unified_cpp_security_manager_ssl2.cpp:int InvalidPRIOMethod<int, -1, PRFileDesc*, PRSendFileData*, PRTransmitFileFlags, unsigned int>(PRFileDesc*, PRSendFileData*, PRTransmitFileFlags, unsigned int) |
1732 | | |
1733 | | nsresult |
1734 | | nsSSLIOLayerHelpers::Init() |
1735 | 0 | { |
1736 | 0 | if (!nsSSLIOLayerInitialized) { |
1737 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
1738 | 0 | nsSSLIOLayerInitialized = true; |
1739 | 0 | nsSSLIOLayerIdentity = PR_GetUniqueIdentity("NSS layer"); |
1740 | 0 | nsSSLIOLayerMethods = *PR_GetDefaultIOMethods(); |
1741 | 0 |
|
1742 | 0 | nsSSLIOLayerMethods.fsync = |
1743 | 0 | InvalidPRIOMethod<PRStatus, PR_FAILURE, PRFileDesc*>; |
1744 | 0 | nsSSLIOLayerMethods.seek = |
1745 | 0 | InvalidPRIOMethod<int32_t, -1, PRFileDesc*, int32_t, PRSeekWhence>; |
1746 | 0 | nsSSLIOLayerMethods.seek64 = |
1747 | 0 | InvalidPRIOMethod<int64_t, -1, PRFileDesc*, int64_t, PRSeekWhence>; |
1748 | 0 | nsSSLIOLayerMethods.fileInfo = |
1749 | 0 | InvalidPRIOMethod<PRStatus, PR_FAILURE, PRFileDesc*, PRFileInfo*>; |
1750 | 0 | nsSSLIOLayerMethods.fileInfo64 = |
1751 | 0 | InvalidPRIOMethod<PRStatus, PR_FAILURE, PRFileDesc*, PRFileInfo64*>; |
1752 | 0 | nsSSLIOLayerMethods.writev = |
1753 | 0 | InvalidPRIOMethod<int32_t, -1, PRFileDesc*, const PRIOVec*, int32_t, |
1754 | 0 | PRIntervalTime>; |
1755 | 0 | nsSSLIOLayerMethods.accept = |
1756 | 0 | InvalidPRIOMethod<PRFileDesc*, nullptr, PRFileDesc*, PRNetAddr*, |
1757 | 0 | PRIntervalTime>; |
1758 | 0 | nsSSLIOLayerMethods.listen = |
1759 | 0 | InvalidPRIOMethod<PRStatus, PR_FAILURE, PRFileDesc*, int>; |
1760 | 0 | nsSSLIOLayerMethods.shutdown = |
1761 | 0 | InvalidPRIOMethod<PRStatus, PR_FAILURE, PRFileDesc*, int>; |
1762 | 0 | nsSSLIOLayerMethods.recvfrom = |
1763 | 0 | InvalidPRIOMethod<int32_t, -1, PRFileDesc*, void*, int32_t, int, |
1764 | 0 | PRNetAddr*, PRIntervalTime>; |
1765 | 0 | nsSSLIOLayerMethods.sendto = |
1766 | 0 | InvalidPRIOMethod<int32_t, -1, PRFileDesc*, const void*, int32_t, int, |
1767 | 0 | const PRNetAddr*, PRIntervalTime>; |
1768 | 0 | nsSSLIOLayerMethods.acceptread = |
1769 | 0 | InvalidPRIOMethod<int32_t, -1, PRFileDesc*, PRFileDesc**, PRNetAddr**, |
1770 | 0 | void*, int32_t, PRIntervalTime>; |
1771 | 0 | nsSSLIOLayerMethods.transmitfile = |
1772 | 0 | InvalidPRIOMethod<int32_t, -1, PRFileDesc*, PRFileDesc*, const void*, |
1773 | 0 | int32_t, PRTransmitFileFlags, PRIntervalTime>; |
1774 | 0 | nsSSLIOLayerMethods.sendfile = |
1775 | 0 | InvalidPRIOMethod<int32_t, -1, PRFileDesc*, PRSendFileData*, |
1776 | 0 | PRTransmitFileFlags, PRIntervalTime>; |
1777 | 0 |
|
1778 | 0 | nsSSLIOLayerMethods.available = PSMAvailable; |
1779 | 0 | nsSSLIOLayerMethods.available64 = PSMAvailable64; |
1780 | 0 | nsSSLIOLayerMethods.getsockname = PSMGetsockname; |
1781 | 0 | nsSSLIOLayerMethods.getpeername = PSMGetpeername; |
1782 | 0 | nsSSLIOLayerMethods.getsocketoption = PSMGetsocketoption; |
1783 | 0 | nsSSLIOLayerMethods.setsocketoption = PSMSetsocketoption; |
1784 | 0 | nsSSLIOLayerMethods.recv = PSMRecv; |
1785 | 0 | nsSSLIOLayerMethods.send = PSMSend; |
1786 | 0 | nsSSLIOLayerMethods.connectcontinue = PSMConnectcontinue; |
1787 | 0 | nsSSLIOLayerMethods.bind = PSMBind; |
1788 | 0 |
|
1789 | 0 | nsSSLIOLayerMethods.connect = nsSSLIOLayerConnect; |
1790 | 0 | nsSSLIOLayerMethods.close = nsSSLIOLayerClose; |
1791 | 0 | nsSSLIOLayerMethods.write = nsSSLIOLayerWrite; |
1792 | 0 | nsSSLIOLayerMethods.read = nsSSLIOLayerRead; |
1793 | 0 | nsSSLIOLayerMethods.poll = nsSSLIOLayerPoll; |
1794 | 0 |
|
1795 | 0 | nsSSLPlaintextLayerIdentity = PR_GetUniqueIdentity("Plaintxext PSM layer"); |
1796 | 0 | nsSSLPlaintextLayerMethods = *PR_GetDefaultIOMethods(); |
1797 | 0 | nsSSLPlaintextLayerMethods.recv = PlaintextRecv; |
1798 | 0 | } |
1799 | 0 |
|
1800 | 0 | loadVersionFallbackLimit(); |
1801 | 0 |
|
1802 | 0 | // non main thread helpers will need to use defaults |
1803 | 0 | if (NS_IsMainThread()) { |
1804 | 0 | bool enabled = false; |
1805 | 0 | Preferences::GetBool("security.ssl.treat_unsafe_negotiation_as_broken", &enabled); |
1806 | 0 | setTreatUnsafeNegotiationAsBroken(enabled); |
1807 | 0 |
|
1808 | 0 | initInsecureFallbackSites(); |
1809 | 0 |
|
1810 | 0 | mPrefObserver = new PrefObserver(this); |
1811 | 0 | Preferences::AddStrongObserver(mPrefObserver, |
1812 | 0 | "security.ssl.treat_unsafe_negotiation_as_broken"); |
1813 | 0 | Preferences::AddStrongObserver(mPrefObserver, |
1814 | 0 | "security.tls.version.fallback-limit"); |
1815 | 0 | Preferences::AddStrongObserver(mPrefObserver, |
1816 | 0 | "security.tls.insecure_fallback_hosts"); |
1817 | 0 | } else { |
1818 | 0 | MOZ_ASSERT(mTlsFlags, "Only per socket version can ignore prefs"); |
1819 | 0 | } |
1820 | 0 |
|
1821 | 0 | return NS_OK; |
1822 | 0 | } |
1823 | | |
1824 | | void |
1825 | | nsSSLIOLayerHelpers::loadVersionFallbackLimit() |
1826 | 0 | { |
1827 | 0 | // see nsNSSComponent::setEnabledTLSVersions for pref handling rules |
1828 | 0 | uint32_t limit = 3; // TLS 1.2 |
1829 | 0 |
|
1830 | 0 | if (NS_IsMainThread()) { |
1831 | 0 | limit = Preferences::GetUint("security.tls.version.fallback-limit", |
1832 | 0 | 3); // 3 = TLS 1.2 |
1833 | 0 | } |
1834 | 0 |
|
1835 | 0 | // set fallback limit if it is set in the tls flags |
1836 | 0 | uint32_t tlsFlagsFallbackLimit = getTLSProviderFlagFallbackLimit(mTlsFlags); |
1837 | 0 |
|
1838 | 0 | if (tlsFlagsFallbackLimit) { |
1839 | 0 | limit = tlsFlagsFallbackLimit; |
1840 | 0 | MOZ_LOG(gPIPNSSLog, LogLevel::Debug, |
1841 | 0 | ("loadVersionFallbackLimit overriden by tlsFlags %d\n", limit)); |
1842 | 0 | } |
1843 | 0 |
|
1844 | 0 | SSLVersionRange defaults = { SSL_LIBRARY_VERSION_TLS_1_2, |
1845 | 0 | SSL_LIBRARY_VERSION_TLS_1_2 }; |
1846 | 0 | SSLVersionRange filledInRange; |
1847 | 0 | nsNSSComponent::FillTLSVersionRange(filledInRange, limit, limit, defaults); |
1848 | 0 | if (filledInRange.max < SSL_LIBRARY_VERSION_TLS_1_2) { |
1849 | 0 | filledInRange.max = SSL_LIBRARY_VERSION_TLS_1_2; |
1850 | 0 | } |
1851 | 0 |
|
1852 | 0 | mVersionFallbackLimit = filledInRange.max; |
1853 | 0 | } |
1854 | | |
1855 | | void |
1856 | | nsSSLIOLayerHelpers::clearStoredData() |
1857 | 0 | { |
1858 | 0 | MutexAutoLock lock(mutex); |
1859 | 0 | mInsecureFallbackSites.Clear(); |
1860 | 0 | mTLSIntoleranceInfo.Clear(); |
1861 | 0 | } |
1862 | | |
1863 | | void |
1864 | | nsSSLIOLayerHelpers::setInsecureFallbackSites(const nsCString& str) |
1865 | 0 | { |
1866 | 0 | MutexAutoLock lock(mutex); |
1867 | 0 |
|
1868 | 0 | mInsecureFallbackSites.Clear(); |
1869 | 0 |
|
1870 | 0 | if (str.IsEmpty()) { |
1871 | 0 | return; |
1872 | 0 | } |
1873 | 0 | |
1874 | 0 | nsCCharSeparatedTokenizer toker(str, ','); |
1875 | 0 |
|
1876 | 0 | while (toker.hasMoreTokens()) { |
1877 | 0 | const nsACString& host = toker.nextToken(); |
1878 | 0 | if (!host.IsEmpty()) { |
1879 | 0 | mInsecureFallbackSites.PutEntry(host); |
1880 | 0 | } |
1881 | 0 | } |
1882 | 0 | } |
1883 | | |
1884 | | void |
1885 | | nsSSLIOLayerHelpers::initInsecureFallbackSites() |
1886 | 0 | { |
1887 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
1888 | 0 | nsAutoCString insecureFallbackHosts; |
1889 | 0 | Preferences::GetCString("security.tls.insecure_fallback_hosts", |
1890 | 0 | insecureFallbackHosts); |
1891 | 0 | setInsecureFallbackSites(insecureFallbackHosts); |
1892 | 0 | } |
1893 | | |
1894 | | bool |
1895 | | nsSSLIOLayerHelpers::isPublic() const |
1896 | 0 | { |
1897 | 0 | return this == &PublicSSLState()->IOLayerHelpers(); |
1898 | 0 | } |
1899 | | |
1900 | | class FallbackPrefRemover final : public Runnable |
1901 | | { |
1902 | | public: |
1903 | | explicit FallbackPrefRemover(const nsACString& aHost) |
1904 | | : mozilla::Runnable("FallbackPrefRemover") |
1905 | | , mHost(aHost) |
1906 | 0 | {} |
1907 | | NS_IMETHOD Run() override; |
1908 | | private: |
1909 | | nsCString mHost; |
1910 | | }; |
1911 | | |
1912 | | NS_IMETHODIMP |
1913 | | FallbackPrefRemover::Run() |
1914 | 0 | { |
1915 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
1916 | 0 | nsAutoCString oldValue; |
1917 | 0 | Preferences::GetCString("security.tls.insecure_fallback_hosts", oldValue); |
1918 | 0 | nsCCharSeparatedTokenizer toker(oldValue, ','); |
1919 | 0 | nsCString newValue; |
1920 | 0 | while (toker.hasMoreTokens()) { |
1921 | 0 | const nsACString& host = toker.nextToken(); |
1922 | 0 | if (host.Equals(mHost)) { |
1923 | 0 | continue; |
1924 | 0 | } |
1925 | 0 | if (!newValue.IsEmpty()) { |
1926 | 0 | newValue.Append(','); |
1927 | 0 | } |
1928 | 0 | newValue.Append(host); |
1929 | 0 | } |
1930 | 0 | Preferences::SetCString("security.tls.insecure_fallback_hosts", newValue); |
1931 | 0 | return NS_OK; |
1932 | 0 | } |
1933 | | |
1934 | | void |
1935 | | nsSSLIOLayerHelpers::removeInsecureFallbackSite(const nsACString& hostname, |
1936 | | uint16_t port) |
1937 | 0 | { |
1938 | 0 | forgetIntolerance(hostname, port); |
1939 | 0 | { |
1940 | 0 | MutexAutoLock lock(mutex); |
1941 | 0 | if (!mInsecureFallbackSites.Contains(hostname)) { |
1942 | 0 | return; |
1943 | 0 | } |
1944 | 0 | mInsecureFallbackSites.RemoveEntry(hostname); |
1945 | 0 | } |
1946 | 0 | if (!isPublic()) { |
1947 | 0 | return; |
1948 | 0 | } |
1949 | 0 | RefPtr<Runnable> runnable = new FallbackPrefRemover(hostname); |
1950 | 0 | if (NS_IsMainThread()) { |
1951 | 0 | runnable->Run(); |
1952 | 0 | } else { |
1953 | 0 | NS_DispatchToMainThread(runnable); |
1954 | 0 | } |
1955 | 0 | } |
1956 | | |
1957 | | bool |
1958 | | nsSSLIOLayerHelpers::isInsecureFallbackSite(const nsACString& hostname) |
1959 | 0 | { |
1960 | 0 | MutexAutoLock lock(mutex); |
1961 | 0 | return mInsecureFallbackSites.Contains(hostname); |
1962 | 0 | } |
1963 | | |
1964 | | void |
1965 | | nsSSLIOLayerHelpers::setTreatUnsafeNegotiationAsBroken(bool broken) |
1966 | 0 | { |
1967 | 0 | MutexAutoLock lock(mutex); |
1968 | 0 | mTreatUnsafeNegotiationAsBroken = broken; |
1969 | 0 | } |
1970 | | |
1971 | | bool |
1972 | | nsSSLIOLayerHelpers::treatUnsafeNegotiationAsBroken() |
1973 | 0 | { |
1974 | 0 | MutexAutoLock lock(mutex); |
1975 | 0 | return mTreatUnsafeNegotiationAsBroken; |
1976 | 0 | } |
1977 | | |
1978 | | nsresult |
1979 | | nsSSLIOLayerNewSocket(int32_t family, |
1980 | | const char* host, |
1981 | | int32_t port, |
1982 | | nsIProxyInfo *proxy, |
1983 | | const OriginAttributes& originAttributes, |
1984 | | PRFileDesc** fd, |
1985 | | nsISupports** info, |
1986 | | bool forSTARTTLS, |
1987 | | uint32_t flags, |
1988 | | uint32_t tlsFlags) |
1989 | 0 | { |
1990 | 0 |
|
1991 | 0 | PRFileDesc* sock = PR_OpenTCPSocket(family); |
1992 | 0 | if (!sock) return NS_ERROR_OUT_OF_MEMORY; |
1993 | 0 | |
1994 | 0 | nsresult rv = nsSSLIOLayerAddToSocket(family, host, port, proxy, |
1995 | 0 | originAttributes, sock, info, |
1996 | 0 | forSTARTTLS, flags, tlsFlags); |
1997 | 0 | if (NS_FAILED(rv)) { |
1998 | 0 | PR_Close(sock); |
1999 | 0 | return rv; |
2000 | 0 | } |
2001 | 0 | |
2002 | 0 | *fd = sock; |
2003 | 0 | return NS_OK; |
2004 | 0 | } |
2005 | | |
2006 | | // Creates CA names strings from (CERTDistNames* caNames) |
2007 | | // |
2008 | | // - arena: arena to allocate strings on |
2009 | | // - caNameStrings: filled with CA names strings on return |
2010 | | // - caNames: CERTDistNames to extract strings from |
2011 | | // - return: SECSuccess if successful; error code otherwise |
2012 | | // |
2013 | | // Note: copied in its entirety from Nova code |
2014 | | static SECStatus |
2015 | | nsConvertCANamesToStrings(const UniquePLArenaPool& arena, char** caNameStrings, |
2016 | | CERTDistNames* caNames) |
2017 | 0 | { |
2018 | 0 | MOZ_ASSERT(arena.get()); |
2019 | 0 | MOZ_ASSERT(caNameStrings); |
2020 | 0 | MOZ_ASSERT(caNames); |
2021 | 0 | if (!arena.get() || !caNameStrings || !caNames) { |
2022 | 0 | PR_SetError(SEC_ERROR_INVALID_ARGS, 0); |
2023 | 0 | return SECFailure; |
2024 | 0 | } |
2025 | 0 | |
2026 | 0 | SECItem* dername; |
2027 | 0 | int n; |
2028 | 0 | char* namestring; |
2029 | 0 |
|
2030 | 0 | for (n = 0; n < caNames->nnames; n++) { |
2031 | 0 | dername = &caNames->names[n]; |
2032 | 0 | namestring = CERT_DerNameToAscii(dername); |
2033 | 0 | if (!namestring) { |
2034 | 0 | // XXX - keep going until we fail to convert the name |
2035 | 0 | caNameStrings[n] = const_cast<char*>(""); |
2036 | 0 | } else { |
2037 | 0 | caNameStrings[n] = PORT_ArenaStrdup(arena.get(), namestring); |
2038 | 0 | PR_Free(namestring); // CERT_DerNameToAscii() uses PR_Malloc(). |
2039 | 0 | if (!caNameStrings[n]) { |
2040 | 0 | return SECFailure; |
2041 | 0 | } |
2042 | 0 | } |
2043 | 0 | } |
2044 | 0 |
|
2045 | 0 | return SECSuccess; |
2046 | 0 | } |
2047 | | |
2048 | | // Possible behaviors for choosing a cert for client auth. |
2049 | | enum class UserCertChoice { |
2050 | | // Ask the user to choose a cert. |
2051 | | Ask = 0, |
2052 | | // Automatically choose a cert. |
2053 | | Auto = 1, |
2054 | | }; |
2055 | | |
2056 | | // Returns the most appropriate user cert choice based on the value of the |
2057 | | // security.default_personal_cert preference. |
2058 | | UserCertChoice |
2059 | | nsGetUserCertChoice() |
2060 | 0 | { |
2061 | 0 | nsAutoCString value; |
2062 | 0 | nsresult rv = |
2063 | 0 | Preferences::GetCString("security.default_personal_cert", value); |
2064 | 0 | if (NS_FAILED(rv)) { |
2065 | 0 | return UserCertChoice::Ask; |
2066 | 0 | } |
2067 | 0 | |
2068 | 0 | // There are three cases for what the preference could be set to: |
2069 | 0 | // 1. "Select Automatically" -> Auto. |
2070 | 0 | // 2. "Ask Every Time" -> Ask. |
2071 | 0 | // 3. Something else -> Ask. This might be a nickname from a migrated cert, |
2072 | 0 | // but we no longer support this case. |
2073 | 0 | return value.EqualsLiteral("Select Automatically") ? UserCertChoice::Auto |
2074 | 0 | : UserCertChoice::Ask; |
2075 | 0 | } |
2076 | | |
2077 | | static bool |
2078 | | hasExplicitKeyUsageNonRepudiation(CERTCertificate* cert) |
2079 | 0 | { |
2080 | 0 | // There is no extension, v1 or v2 certificate |
2081 | 0 | if (!cert->extensions) |
2082 | 0 | return false; |
2083 | 0 | |
2084 | 0 | SECStatus srv; |
2085 | 0 | SECItem keyUsageItem; |
2086 | 0 | keyUsageItem.data = nullptr; |
2087 | 0 |
|
2088 | 0 | srv = CERT_FindKeyUsageExtension(cert, &keyUsageItem); |
2089 | 0 | if (srv == SECFailure) |
2090 | 0 | return false; |
2091 | 0 | |
2092 | 0 | unsigned char keyUsage = keyUsageItem.data[0]; |
2093 | 0 | PORT_Free (keyUsageItem.data); |
2094 | 0 |
|
2095 | 0 | return !!(keyUsage & KU_NON_REPUDIATION); |
2096 | 0 | } |
2097 | | |
2098 | | class ClientAuthDataRunnable : public SyncRunnableBase |
2099 | | { |
2100 | | public: |
2101 | | ClientAuthDataRunnable(CERTDistNames* caNames, |
2102 | | CERTCertificate** pRetCert, |
2103 | | SECKEYPrivateKey** pRetKey, |
2104 | | nsNSSSocketInfo* info, |
2105 | | const UniqueCERTCertificate& serverCert) |
2106 | | : mRV(SECFailure) |
2107 | | , mErrorCodeToReport(SEC_ERROR_NO_MEMORY) |
2108 | | , mPRetCert(pRetCert) |
2109 | | , mPRetKey(pRetKey) |
2110 | | , mCANames(caNames) |
2111 | | , mSocketInfo(info) |
2112 | | , mServerCert(serverCert.get()) |
2113 | 0 | { |
2114 | 0 | } |
2115 | | |
2116 | | SECStatus mRV; // out |
2117 | | PRErrorCode mErrorCodeToReport; // out |
2118 | | CERTCertificate** const mPRetCert; // in/out |
2119 | | SECKEYPrivateKey** const mPRetKey; // in/out |
2120 | | protected: |
2121 | | virtual void RunOnTargetThread() override; |
2122 | | private: |
2123 | | CERTDistNames* const mCANames; // in |
2124 | | nsNSSSocketInfo* const mSocketInfo; // in |
2125 | | CERTCertificate* const mServerCert; // in |
2126 | | }; |
2127 | | |
2128 | | // This callback function is used to pull client certificate |
2129 | | // information upon server request |
2130 | | // |
2131 | | // - arg: SSL data connection |
2132 | | // - socket: SSL socket we're dealing with |
2133 | | // - caNames: list of CA names |
2134 | | // - pRetCert: returns a pointer to a pointer to a valid certificate if |
2135 | | // successful; otherwise nullptr |
2136 | | // - pRetKey: returns a pointer to a pointer to the corresponding key if |
2137 | | // successful; otherwise nullptr |
2138 | | SECStatus |
2139 | | nsNSS_SSLGetClientAuthData(void* arg, PRFileDesc* socket, |
2140 | | CERTDistNames* caNames, CERTCertificate** pRetCert, |
2141 | | SECKEYPrivateKey** pRetKey) |
2142 | 0 | { |
2143 | 0 | if (!socket || !caNames || !pRetCert || !pRetKey) { |
2144 | 0 | PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); |
2145 | 0 | return SECFailure; |
2146 | 0 | } |
2147 | 0 | |
2148 | 0 | Telemetry::ScalarAdd(Telemetry::ScalarID::SECURITY_CLIENT_CERT, |
2149 | 0 | NS_LITERAL_STRING("requested"), 1); |
2150 | 0 |
|
2151 | 0 | RefPtr<nsNSSSocketInfo> info( |
2152 | 0 | BitwiseCast<nsNSSSocketInfo*, PRFilePrivate*>(socket->higher->secret)); |
2153 | 0 |
|
2154 | 0 | UniqueCERTCertificate serverCert(SSL_PeerCertificate(socket)); |
2155 | 0 | if (!serverCert) { |
2156 | 0 | MOZ_ASSERT_UNREACHABLE( |
2157 | 0 | "Missing server cert should have been detected during server cert auth."); |
2158 | 0 | PR_SetError(SSL_ERROR_NO_CERTIFICATE, 0); |
2159 | 0 | return SECFailure; |
2160 | 0 | } |
2161 | 0 |
|
2162 | 0 | if (info->GetDenyClientCert()) { |
2163 | 0 | MOZ_LOG(gPIPNSSLog, LogLevel::Debug, |
2164 | 0 | ("[%p] Not returning client cert due to denyClientCert attribute\n", socket)); |
2165 | 0 | *pRetCert = nullptr; |
2166 | 0 | *pRetKey = nullptr; |
2167 | 0 | return SECSuccess; |
2168 | 0 | } |
2169 | 0 |
|
2170 | 0 | if (info->GetJoined()) { |
2171 | 0 | // We refuse to send a client certificate when there are multiple hostnames |
2172 | 0 | // joined on this connection, because we only show the user one hostname |
2173 | 0 | // (mHostName) in the client certificate UI. |
2174 | 0 |
|
2175 | 0 | MOZ_LOG(gPIPNSSLog, LogLevel::Debug, |
2176 | 0 | ("[%p] Not returning client cert due to previous join\n", socket)); |
2177 | 0 | *pRetCert = nullptr; |
2178 | 0 | *pRetKey = nullptr; |
2179 | 0 | return SECSuccess; |
2180 | 0 | } |
2181 | 0 |
|
2182 | 0 | // XXX: This should be done asynchronously; see bug 696976 |
2183 | 0 | RefPtr<ClientAuthDataRunnable> runnable( |
2184 | 0 | new ClientAuthDataRunnable(caNames, pRetCert, pRetKey, info, serverCert)); |
2185 | 0 | nsresult rv = runnable->DispatchToMainThreadAndWait(); |
2186 | 0 | if (NS_FAILED(rv)) { |
2187 | 0 | PR_SetError(SEC_ERROR_NO_MEMORY, 0); |
2188 | 0 | return SECFailure; |
2189 | 0 | } |
2190 | 0 | |
2191 | 0 | if (runnable->mRV != SECSuccess) { |
2192 | 0 | PR_SetError(runnable->mErrorCodeToReport, 0); |
2193 | 0 | } else if (*runnable->mPRetCert || *runnable->mPRetKey) { |
2194 | 0 | // Make joinConnection prohibit joining after we've sent a client cert |
2195 | 0 | info->SetSentClientCert(); |
2196 | 0 | Telemetry::ScalarAdd(Telemetry::ScalarID::SECURITY_CLIENT_CERT, |
2197 | 0 | NS_LITERAL_STRING("sent"), 1); |
2198 | 0 | } |
2199 | 0 |
|
2200 | 0 | return runnable->mRV; |
2201 | 0 | } |
2202 | | |
2203 | | void |
2204 | | ClientAuthDataRunnable::RunOnTargetThread() |
2205 | 0 | { |
2206 | 0 | // We check the value of a pref in this runnable, so this runnable should only |
2207 | 0 | // be run on the main thread. |
2208 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
2209 | 0 |
|
2210 | 0 | UniquePLArenaPool arena; |
2211 | 0 | char** caNameStrings; |
2212 | 0 | UniqueCERTCertificate cert; |
2213 | 0 | UniqueSECKEYPrivateKey privKey; |
2214 | 0 | void* wincx = mSocketInfo; |
2215 | 0 | nsresult rv; |
2216 | 0 |
|
2217 | 0 | if (NS_FAILED(CheckForSmartCardChanges())) { |
2218 | 0 | mRV = SECFailure; |
2219 | 0 | *mPRetCert = nullptr; |
2220 | 0 | *mPRetKey = nullptr; |
2221 | 0 | mErrorCodeToReport = SEC_ERROR_LIBRARY_FAILURE; |
2222 | 0 | return; |
2223 | 0 | } |
2224 | 0 | |
2225 | 0 | nsCOMPtr<nsIX509Cert> socketClientCert; |
2226 | 0 | mSocketInfo->GetClientCert(getter_AddRefs(socketClientCert)); |
2227 | 0 |
|
2228 | 0 | // If a client cert preference was set on the socket info, use that and skip |
2229 | 0 | // the client cert UI and/or search of the user's past cert decisions. |
2230 | 0 | if (socketClientCert) { |
2231 | 0 | cert.reset(socketClientCert->GetCert()); |
2232 | 0 | if (!cert) { |
2233 | 0 | goto loser; |
2234 | 0 | } |
2235 | 0 | |
2236 | 0 | // Get the private key |
2237 | 0 | privKey.reset(PK11_FindKeyByAnyCert(cert.get(), wincx)); |
2238 | 0 | if (!privKey) { |
2239 | 0 | goto loser; |
2240 | 0 | } |
2241 | 0 | |
2242 | 0 | *mPRetCert = cert.release(); |
2243 | 0 | *mPRetKey = privKey.release(); |
2244 | 0 | mRV = SECSuccess; |
2245 | 0 | return; |
2246 | 0 | } |
2247 | 0 | |
2248 | 0 | // create caNameStrings |
2249 | 0 | arena.reset(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); |
2250 | 0 | if (!arena) { |
2251 | 0 | goto loser; |
2252 | 0 | } |
2253 | 0 | |
2254 | 0 | caNameStrings = static_cast<char**>( |
2255 | 0 | PORT_ArenaAlloc(arena.get(), sizeof(char*) * mCANames->nnames)); |
2256 | 0 | if (!caNameStrings) { |
2257 | 0 | goto loser; |
2258 | 0 | } |
2259 | 0 | |
2260 | 0 | mRV = nsConvertCANamesToStrings(arena, caNameStrings, mCANames); |
2261 | 0 | if (mRV != SECSuccess) { |
2262 | 0 | goto loser; |
2263 | 0 | } |
2264 | 0 | |
2265 | 0 | // find valid user cert and key pair |
2266 | 0 | if (nsGetUserCertChoice() == UserCertChoice::Auto) { |
2267 | 0 | // automatically find the right cert |
2268 | 0 |
|
2269 | 0 | // find all user certs that are valid and for SSL |
2270 | 0 | UniqueCERTCertList certList( |
2271 | 0 | CERT_FindUserCertsByUsage(CERT_GetDefaultCertDB(), certUsageSSLClient, |
2272 | 0 | false, true, wincx)); |
2273 | 0 | if (!certList) { |
2274 | 0 | goto loser; |
2275 | 0 | } |
2276 | 0 | |
2277 | 0 | // filter the list to those issued by CAs supported by the server |
2278 | 0 | mRV = CERT_FilterCertListByCANames(certList.get(), mCANames->nnames, |
2279 | 0 | caNameStrings, certUsageSSLClient); |
2280 | 0 | if (mRV != SECSuccess) { |
2281 | 0 | goto loser; |
2282 | 0 | } |
2283 | 0 | |
2284 | 0 | // make sure the list is not empty |
2285 | 0 | if (CERT_LIST_END(CERT_LIST_HEAD(certList), certList)) { |
2286 | 0 | goto loser; |
2287 | 0 | } |
2288 | 0 | |
2289 | 0 | UniqueCERTCertificate lowPrioNonrepCert; |
2290 | 0 |
|
2291 | 0 | // loop through the list until we find a cert with a key |
2292 | 0 | for (CERTCertListNode* node = CERT_LIST_HEAD(certList); |
2293 | 0 | !CERT_LIST_END(node, certList); |
2294 | 0 | node = CERT_LIST_NEXT(node)) { |
2295 | 0 | // if the certificate has restriction and we do not satisfy it we do not |
2296 | 0 | // use it |
2297 | 0 | privKey.reset(PK11_FindKeyByAnyCert(node->cert, wincx)); |
2298 | 0 | if (privKey) { |
2299 | 0 | if (hasExplicitKeyUsageNonRepudiation(node->cert)) { |
2300 | 0 | privKey = nullptr; |
2301 | 0 | // Not a preferred cert |
2302 | 0 | if (!lowPrioNonrepCert) { // did not yet find a low prio cert |
2303 | 0 | lowPrioNonrepCert.reset(CERT_DupCertificate(node->cert)); |
2304 | 0 | } |
2305 | 0 | } else { |
2306 | 0 | // this is a good cert to present |
2307 | 0 | cert.reset(CERT_DupCertificate(node->cert)); |
2308 | 0 | break; |
2309 | 0 | } |
2310 | 0 | } |
2311 | 0 | if (PR_GetError() == SEC_ERROR_BAD_PASSWORD) { |
2312 | 0 | // problem with password: bail |
2313 | 0 | goto loser; |
2314 | 0 | } |
2315 | 0 | } |
2316 | 0 |
|
2317 | 0 | if (!cert && lowPrioNonrepCert) { |
2318 | 0 | cert = std::move(lowPrioNonrepCert); |
2319 | 0 | privKey.reset(PK11_FindKeyByAnyCert(cert.get(), wincx)); |
2320 | 0 | } |
2321 | 0 |
|
2322 | 0 | if (!cert) { |
2323 | 0 | goto loser; |
2324 | 0 | } |
2325 | 0 | } else { // Not Auto => ask |
2326 | 0 | // Get the SSL Certificate |
2327 | 0 |
|
2328 | 0 | const nsACString& hostname = mSocketInfo->GetHostName(); |
2329 | 0 |
|
2330 | 0 | RefPtr<nsClientAuthRememberService> cars = |
2331 | 0 | mSocketInfo->SharedState().GetClientAuthRememberService(); |
2332 | 0 |
|
2333 | 0 | bool hasRemembered = false; |
2334 | 0 | nsCString rememberedDBKey; |
2335 | 0 | if (cars) { |
2336 | 0 | bool found; |
2337 | 0 | rv = cars->HasRememberedDecision(hostname, |
2338 | 0 | mSocketInfo->GetOriginAttributes(), |
2339 | 0 | mServerCert, rememberedDBKey, &found); |
2340 | 0 | if (NS_SUCCEEDED(rv) && found) { |
2341 | 0 | hasRemembered = true; |
2342 | 0 | } |
2343 | 0 | } |
2344 | 0 |
|
2345 | 0 | if (hasRemembered && !rememberedDBKey.IsEmpty()) { |
2346 | 0 | nsCOMPtr<nsIX509CertDB> certdb = do_GetService(NS_X509CERTDB_CONTRACTID); |
2347 | 0 | if (certdb) { |
2348 | 0 | nsCOMPtr<nsIX509Cert> foundCert; |
2349 | 0 | rv = certdb->FindCertByDBKey(rememberedDBKey, getter_AddRefs(foundCert)); |
2350 | 0 | if (NS_SUCCEEDED(rv) && foundCert) { |
2351 | 0 | nsNSSCertificate* objCert = |
2352 | 0 | BitwiseCast<nsNSSCertificate*, nsIX509Cert*>(foundCert.get()); |
2353 | 0 | if (objCert) { |
2354 | 0 | cert.reset(objCert->GetCert()); |
2355 | 0 | } |
2356 | 0 | } |
2357 | 0 |
|
2358 | 0 | if (!cert) { |
2359 | 0 | hasRemembered = false; |
2360 | 0 | } |
2361 | 0 | } |
2362 | 0 | } |
2363 | 0 |
|
2364 | 0 | if (!hasRemembered) { |
2365 | 0 | // user selects a cert to present |
2366 | 0 | nsCOMPtr<nsIClientAuthDialogs> dialogs; |
2367 | 0 |
|
2368 | 0 | // find all user certs that are for SSL |
2369 | 0 | // note that we are allowing expired certs in this list |
2370 | 0 | UniqueCERTCertList certList( |
2371 | 0 | CERT_FindUserCertsByUsage(CERT_GetDefaultCertDB(), certUsageSSLClient, |
2372 | 0 | false, false, wincx)); |
2373 | 0 | if (!certList) { |
2374 | 0 | goto loser; |
2375 | 0 | } |
2376 | 0 | |
2377 | 0 | if (mCANames->nnames != 0) { |
2378 | 0 | // filter the list to those issued by CAs supported by the server |
2379 | 0 | mRV = CERT_FilterCertListByCANames(certList.get(), |
2380 | 0 | mCANames->nnames, |
2381 | 0 | caNameStrings, |
2382 | 0 | certUsageSSLClient); |
2383 | 0 | if (mRV != SECSuccess) { |
2384 | 0 | goto loser; |
2385 | 0 | } |
2386 | 0 | } |
2387 | 0 | |
2388 | 0 | if (CERT_LIST_END(CERT_LIST_HEAD(certList), certList)) { |
2389 | 0 | // list is empty - no matching certs |
2390 | 0 | goto loser; |
2391 | 0 | } |
2392 | 0 | |
2393 | 0 | UniquePORTString corg(CERT_GetOrgName(&mServerCert->subject)); |
2394 | 0 | nsAutoCString org(corg.get()); |
2395 | 0 |
|
2396 | 0 | UniquePORTString cissuer(CERT_GetOrgName(&mServerCert->issuer)); |
2397 | 0 | nsAutoCString issuer(cissuer.get()); |
2398 | 0 |
|
2399 | 0 | nsCOMPtr<nsIMutableArray> certArray = nsArrayBase::Create(); |
2400 | 0 | if (!certArray) { |
2401 | 0 | goto loser; |
2402 | 0 | } |
2403 | 0 | |
2404 | 0 | for (CERTCertListNode* node = CERT_LIST_HEAD(certList); |
2405 | 0 | !CERT_LIST_END(node, certList); |
2406 | 0 | node = CERT_LIST_NEXT(node)) { |
2407 | 0 | nsCOMPtr<nsIX509Cert> tempCert = nsNSSCertificate::Create(node->cert); |
2408 | 0 | if (!tempCert) { |
2409 | 0 | goto loser; |
2410 | 0 | } |
2411 | 0 | |
2412 | 0 | rv = certArray->AppendElement(tempCert); |
2413 | 0 | if (NS_FAILED(rv)) { |
2414 | 0 | goto loser; |
2415 | 0 | } |
2416 | 0 | } |
2417 | 0 |
|
2418 | 0 | // Throw up the client auth dialog and get back the index of the selected cert |
2419 | 0 | rv = getNSSDialogs(getter_AddRefs(dialogs), |
2420 | 0 | NS_GET_IID(nsIClientAuthDialogs), |
2421 | 0 | NS_CLIENTAUTHDIALOGS_CONTRACTID); |
2422 | 0 |
|
2423 | 0 | if (NS_FAILED(rv)) { |
2424 | 0 | goto loser; |
2425 | 0 | } |
2426 | 0 | |
2427 | 0 | uint32_t selectedIndex = 0; |
2428 | 0 | bool certChosen = false; |
2429 | 0 | rv = dialogs->ChooseCertificate(mSocketInfo, hostname, |
2430 | 0 | mSocketInfo->GetPort(), org, issuer, |
2431 | 0 | certArray, &selectedIndex, &certChosen); |
2432 | 0 | if (NS_FAILED(rv)) { |
2433 | 0 | goto loser; |
2434 | 0 | } |
2435 | 0 | |
2436 | 0 | // even if the user has canceled, we want to remember that, to avoid repeating prompts |
2437 | 0 | bool wantRemember = false; |
2438 | 0 | mSocketInfo->GetRememberClientAuthCertificate(&wantRemember); |
2439 | 0 |
|
2440 | 0 | if (certChosen) { |
2441 | 0 | nsCOMPtr<nsIX509Cert> selectedCert = do_QueryElementAt(certArray, |
2442 | 0 | selectedIndex); |
2443 | 0 | if (!selectedCert) { |
2444 | 0 | goto loser; |
2445 | 0 | } |
2446 | 0 | cert.reset(selectedCert->GetCert()); |
2447 | 0 | } |
2448 | 0 |
|
2449 | 0 | if (cars && wantRemember) { |
2450 | 0 | cars->RememberDecision(hostname, mSocketInfo->GetOriginAttributes(), |
2451 | 0 | mServerCert, certChosen ? cert.get() : nullptr); |
2452 | 0 | } |
2453 | 0 | } |
2454 | 0 |
|
2455 | 0 | if (!cert) { |
2456 | 0 | goto loser; |
2457 | 0 | } |
2458 | 0 | |
2459 | 0 | // go get the private key |
2460 | 0 | privKey.reset(PK11_FindKeyByAnyCert(cert.get(), wincx)); |
2461 | 0 | if (!privKey) { |
2462 | 0 | goto loser; |
2463 | 0 | } |
2464 | 0 | } |
2465 | 0 | goto done; |
2466 | 0 |
|
2467 | 0 | loser: |
2468 | 0 | if (mRV == SECSuccess) { |
2469 | 0 | mRV = SECFailure; |
2470 | 0 | } |
2471 | 0 | done: |
2472 | 0 | int error = PR_GetError(); |
2473 | 0 |
|
2474 | 0 | *mPRetCert = cert.release(); |
2475 | 0 | *mPRetKey = privKey.release(); |
2476 | 0 |
|
2477 | 0 | if (mRV == SECFailure) { |
2478 | 0 | mErrorCodeToReport = error; |
2479 | 0 | } |
2480 | 0 | } |
2481 | | |
2482 | | static PRFileDesc* |
2483 | | nsSSLIOLayerImportFD(PRFileDesc* fd, |
2484 | | nsNSSSocketInfo* infoObject, |
2485 | | const char* host) |
2486 | 0 | { |
2487 | 0 | PRFileDesc* sslSock = SSL_ImportFD(nullptr, fd); |
2488 | 0 | if (!sslSock) { |
2489 | 0 | MOZ_ASSERT_UNREACHABLE("NSS: Error importing socket"); |
2490 | 0 | return nullptr; |
2491 | 0 | } |
2492 | 0 | SSL_SetPKCS11PinArg(sslSock, (nsIInterfaceRequestor*) infoObject); |
2493 | 0 | SSL_HandshakeCallback(sslSock, HandshakeCallback, infoObject); |
2494 | 0 | SSL_SetCanFalseStartCallback(sslSock, CanFalseStartCallback, infoObject); |
2495 | 0 |
|
2496 | 0 | // Disable this hook if we connect anonymously. See bug 466080. |
2497 | 0 | uint32_t flags = 0; |
2498 | 0 | infoObject->GetProviderFlags(&flags); |
2499 | 0 | if (flags & nsISocketProvider::ANONYMOUS_CONNECT) { |
2500 | 0 | SSL_GetClientAuthDataHook(sslSock, nullptr, infoObject); |
2501 | 0 | } else { |
2502 | 0 | SSL_GetClientAuthDataHook(sslSock, |
2503 | 0 | (SSLGetClientAuthData) nsNSS_SSLGetClientAuthData, |
2504 | 0 | infoObject); |
2505 | 0 | } |
2506 | 0 | if (flags & nsISocketProvider::MITM_OK) { |
2507 | 0 | MOZ_LOG(gPIPNSSLog, LogLevel::Debug, |
2508 | 0 | ("[%p] nsSSLIOLayerImportFD: bypass authentication flag\n", fd)); |
2509 | 0 | infoObject->SetBypassAuthentication(true); |
2510 | 0 | } |
2511 | 0 | if (SECSuccess != SSL_AuthCertificateHook(sslSock, AuthCertificateHook, |
2512 | 0 | infoObject)) { |
2513 | 0 | MOZ_ASSERT_UNREACHABLE("Failed to configure AuthCertificateHook"); |
2514 | 0 | goto loser; |
2515 | 0 | } |
2516 | 0 |
|
2517 | 0 | if (SECSuccess != SSL_SetURL(sslSock, host)) { |
2518 | 0 | MOZ_ASSERT_UNREACHABLE("SSL_SetURL failed"); |
2519 | 0 | goto loser; |
2520 | 0 | } |
2521 | 0 |
|
2522 | 0 | return sslSock; |
2523 | 0 | loser: |
2524 | 0 | if (sslSock) { |
2525 | 0 | PR_Close(sslSock); |
2526 | 0 | } |
2527 | 0 | return nullptr; |
2528 | 0 | } |
2529 | | |
2530 | | // Please change getSignatureName in nsNSSCallbacks.cpp when changing the list |
2531 | | // here. |
2532 | | static const SSLSignatureScheme sEnabledSignatureSchemes[] = { |
2533 | | ssl_sig_ecdsa_secp256r1_sha256, |
2534 | | ssl_sig_ecdsa_secp384r1_sha384, |
2535 | | ssl_sig_ecdsa_secp521r1_sha512, |
2536 | | ssl_sig_rsa_pss_sha256, |
2537 | | ssl_sig_rsa_pss_sha384, |
2538 | | ssl_sig_rsa_pss_sha512, |
2539 | | ssl_sig_rsa_pkcs1_sha256, |
2540 | | ssl_sig_rsa_pkcs1_sha384, |
2541 | | ssl_sig_rsa_pkcs1_sha512, |
2542 | | ssl_sig_ecdsa_sha1, |
2543 | | ssl_sig_rsa_pkcs1_sha1, |
2544 | | }; |
2545 | | |
2546 | | static nsresult |
2547 | | nsSSLIOLayerSetOptions(PRFileDesc* fd, bool forSTARTTLS, |
2548 | | bool haveProxy, const char* host, int32_t port, |
2549 | | nsNSSSocketInfo* infoObject) |
2550 | 0 | { |
2551 | 0 | if (forSTARTTLS || haveProxy) { |
2552 | 0 | if (SECSuccess != SSL_OptionSet(fd, SSL_SECURITY, false)) { |
2553 | 0 | return NS_ERROR_FAILURE; |
2554 | 0 | } |
2555 | 0 | } |
2556 | 0 | |
2557 | 0 | SSLVersionRange range; |
2558 | 0 | if (SSL_VersionRangeGet(fd, &range) != SECSuccess) { |
2559 | 0 | return NS_ERROR_FAILURE; |
2560 | 0 | } |
2561 | 0 | |
2562 | 0 | // Set TLS 1.3 compat mode. |
2563 | 0 | if (SECSuccess != SSL_OptionSet(fd, SSL_ENABLE_TLS13_COMPAT_MODE, PR_TRUE)) { |
2564 | 0 | MOZ_LOG(gPIPNSSLog, LogLevel::Error, |
2565 | 0 | ("[%p] nsSSLIOLayerSetOptions: Setting compat mode failed\n", fd)); |
2566 | 0 | } |
2567 | 0 |
|
2568 | 0 | // setting TLS max version |
2569 | 0 | uint32_t versionFlags = |
2570 | 0 | getTLSProviderFlagMaxVersion(infoObject->GetProviderTlsFlags()); |
2571 | 0 | if (versionFlags) { |
2572 | 0 | MOZ_LOG(gPIPNSSLog, LogLevel::Debug, |
2573 | 0 | ("[%p] nsSSLIOLayerSetOptions: version flags %d\n", fd, versionFlags)); |
2574 | 0 | if (versionFlags == kTLSProviderFlagMaxVersion10) { |
2575 | 0 | range.max = SSL_LIBRARY_VERSION_TLS_1_0; |
2576 | 0 | } else if (versionFlags == kTLSProviderFlagMaxVersion11) { |
2577 | 0 | range.max = SSL_LIBRARY_VERSION_TLS_1_1; |
2578 | 0 | } else if (versionFlags == kTLSProviderFlagMaxVersion12) { |
2579 | 0 | range.max = SSL_LIBRARY_VERSION_TLS_1_2; |
2580 | 0 | } else if (versionFlags == kTLSProviderFlagMaxVersion13) { |
2581 | 0 | range.max = SSL_LIBRARY_VERSION_TLS_1_3; |
2582 | 0 | } else { |
2583 | 0 | MOZ_LOG(gPIPNSSLog, LogLevel::Error, |
2584 | 0 | ("[%p] nsSSLIOLayerSetOptions: unknown version flags %d\n", |
2585 | 0 | fd, versionFlags)); |
2586 | 0 | } |
2587 | 0 | } |
2588 | 0 |
|
2589 | 0 | if ((infoObject->GetProviderFlags() & nsISocketProvider::BE_CONSERVATIVE) && |
2590 | 0 | (range.max > SSL_LIBRARY_VERSION_TLS_1_2)) { |
2591 | 0 | MOZ_LOG(gPIPNSSLog, LogLevel::Debug, |
2592 | 0 | ("[%p] nsSSLIOLayerSetOptions: range.max limited to 1.2 due to BE_CONSERVATIVE flag\n", |
2593 | 0 | fd)); |
2594 | 0 | range.max = SSL_LIBRARY_VERSION_TLS_1_2; |
2595 | 0 | } |
2596 | 0 |
|
2597 | 0 | uint16_t maxEnabledVersion = range.max; |
2598 | 0 | infoObject->SharedState().IOLayerHelpers() |
2599 | 0 | .adjustForTLSIntolerance(infoObject->GetHostName(), infoObject->GetPort(), |
2600 | 0 | range); |
2601 | 0 | MOZ_LOG(gPIPNSSLog, LogLevel::Debug, |
2602 | 0 | ("[%p] nsSSLIOLayerSetOptions: using TLS version range (0x%04x,0x%04x)\n", |
2603 | 0 | fd, static_cast<unsigned int>(range.min), |
2604 | 0 | static_cast<unsigned int>(range.max))); |
2605 | 0 |
|
2606 | 0 | // If the user has set their minimum version to something higher than what |
2607 | 0 | // we've now set the maximum to, this will result in an inconsistent version |
2608 | 0 | // range unless we fix it up. This will override their preference, but we only |
2609 | 0 | // do this for sites critical to the operation of the browser (e.g. update |
2610 | 0 | // servers) and telemetry experiments. |
2611 | 0 | if (range.min > range.max) { |
2612 | 0 | range.min = range.max; |
2613 | 0 | } |
2614 | 0 |
|
2615 | 0 | if (SSL_VersionRangeSet(fd, &range) != SECSuccess) { |
2616 | 0 | return NS_ERROR_FAILURE; |
2617 | 0 | } |
2618 | 0 | infoObject->SetTLSVersionRange(range); |
2619 | 0 |
|
2620 | 0 | // when adjustForTLSIntolerance tweaks the maximum version downward, |
2621 | 0 | // we tell the server using this SCSV so they can detect a downgrade attack |
2622 | 0 | if (range.max < maxEnabledVersion) { |
2623 | 0 | MOZ_LOG(gPIPNSSLog, LogLevel::Debug, |
2624 | 0 | ("[%p] nsSSLIOLayerSetOptions: enabling TLS_FALLBACK_SCSV\n", fd)); |
2625 | 0 | // Some servers will choke if we send the fallback SCSV with TLS 1.2. |
2626 | 0 | if (range.max < SSL_LIBRARY_VERSION_TLS_1_2) { |
2627 | 0 | if (SECSuccess != SSL_OptionSet(fd, SSL_ENABLE_FALLBACK_SCSV, true)) { |
2628 | 0 | return NS_ERROR_FAILURE; |
2629 | 0 | } |
2630 | 0 | } |
2631 | 0 | // tell NSS the max enabled version to make anti-downgrade effective |
2632 | 0 | if (SECSuccess != SSL_SetDowngradeCheckVersion(fd, maxEnabledVersion)) { |
2633 | 0 | return NS_ERROR_FAILURE; |
2634 | 0 | } |
2635 | 0 | } |
2636 | 0 | |
2637 | 0 | // Include a modest set of named groups. |
2638 | 0 | // Please change getKeaGroupName in nsNSSCallbacks.cpp when changing the list |
2639 | 0 | // here. |
2640 | 0 | const SSLNamedGroup namedGroups[] = { |
2641 | 0 | ssl_grp_ec_curve25519, ssl_grp_ec_secp256r1, ssl_grp_ec_secp384r1, |
2642 | 0 | ssl_grp_ec_secp521r1, ssl_grp_ffdhe_2048, ssl_grp_ffdhe_3072 |
2643 | 0 | }; |
2644 | 0 | if (SECSuccess != SSL_NamedGroupConfig(fd, namedGroups, |
2645 | 0 | mozilla::ArrayLength(namedGroups))) { |
2646 | 0 | return NS_ERROR_FAILURE; |
2647 | 0 | } |
2648 | 0 | // This ensures that we send key shares for X25519 and P-256 in TLS 1.3, so |
2649 | 0 | // that servers are less likely to use HelloRetryRequest. |
2650 | 0 | if (SECSuccess != SSL_SendAdditionalKeyShares(fd, 1)) { |
2651 | 0 | return NS_ERROR_FAILURE; |
2652 | 0 | } |
2653 | 0 | |
2654 | 0 | if (SECSuccess != SSL_SignatureSchemePrefSet(fd, sEnabledSignatureSchemes, |
2655 | 0 | mozilla::ArrayLength(sEnabledSignatureSchemes))) { |
2656 | 0 | return NS_ERROR_FAILURE; |
2657 | 0 | } |
2658 | 0 | |
2659 | 0 | bool enabled = infoObject->SharedState().IsOCSPStaplingEnabled(); |
2660 | 0 | if (SECSuccess != SSL_OptionSet(fd, SSL_ENABLE_OCSP_STAPLING, enabled)) { |
2661 | 0 | return NS_ERROR_FAILURE; |
2662 | 0 | } |
2663 | 0 | |
2664 | 0 | bool sctsEnabled = infoObject->SharedState().IsSignedCertTimestampsEnabled(); |
2665 | 0 | if (SECSuccess != SSL_OptionSet(fd, SSL_ENABLE_SIGNED_CERT_TIMESTAMPS, |
2666 | 0 | sctsEnabled)) { |
2667 | 0 | return NS_ERROR_FAILURE; |
2668 | 0 | } |
2669 | 0 | |
2670 | 0 | if (SECSuccess != SSL_OptionSet(fd, SSL_HANDSHAKE_AS_CLIENT, true)) { |
2671 | 0 | return NS_ERROR_FAILURE; |
2672 | 0 | } |
2673 | 0 | |
2674 | 0 | // Set the Peer ID so that SSL proxy connections work properly and to |
2675 | 0 | // separate anonymous and/or private browsing connections. |
2676 | 0 | uint32_t flags = infoObject->GetProviderFlags(); |
2677 | 0 | nsAutoCString peerId; |
2678 | 0 | if (flags & nsISocketProvider::ANONYMOUS_CONNECT) { // See bug 466080 |
2679 | 0 | peerId.AppendLiteral("anon:"); |
2680 | 0 | } |
2681 | 0 | if (flags & nsISocketProvider::NO_PERMANENT_STORAGE) { |
2682 | 0 | peerId.AppendLiteral("private:"); |
2683 | 0 | } |
2684 | 0 | if (flags & nsISocketProvider::MITM_OK) { |
2685 | 0 | peerId.AppendLiteral("bypassAuth:"); |
2686 | 0 | } |
2687 | 0 | if (flags & nsISocketProvider::BE_CONSERVATIVE) { |
2688 | 0 | peerId.AppendLiteral("beConservative:"); |
2689 | 0 | } |
2690 | 0 |
|
2691 | 0 | peerId.AppendPrintf("tlsflags0x%08x:", infoObject->GetProviderTlsFlags()); |
2692 | 0 |
|
2693 | 0 | peerId.Append(host); |
2694 | 0 | peerId.Append(':'); |
2695 | 0 | peerId.AppendInt(port); |
2696 | 0 | nsAutoCString suffix; |
2697 | 0 | infoObject->GetOriginAttributes().CreateSuffix(suffix); |
2698 | 0 | peerId.Append(suffix); |
2699 | 0 | if (SECSuccess != SSL_SetSockPeerID(fd, peerId.get())) { |
2700 | 0 | return NS_ERROR_FAILURE; |
2701 | 0 | } |
2702 | 0 | |
2703 | 0 | return NS_OK; |
2704 | 0 | } |
2705 | | |
2706 | | nsresult |
2707 | | nsSSLIOLayerAddToSocket(int32_t family, |
2708 | | const char* host, |
2709 | | int32_t port, |
2710 | | nsIProxyInfo* proxy, |
2711 | | const OriginAttributes& originAttributes, |
2712 | | PRFileDesc* fd, |
2713 | | nsISupports** info, |
2714 | | bool forSTARTTLS, |
2715 | | uint32_t providerFlags, |
2716 | | uint32_t providerTlsFlags) |
2717 | 0 | { |
2718 | 0 | PRFileDesc* layer = nullptr; |
2719 | 0 | PRFileDesc* plaintextLayer = nullptr; |
2720 | 0 | nsresult rv; |
2721 | 0 | PRStatus stat; |
2722 | 0 |
|
2723 | 0 | SharedSSLState* sharedState = nullptr; |
2724 | 0 | RefPtr<SharedSSLState> allocatedState; |
2725 | 0 | if (providerTlsFlags) { |
2726 | 0 | allocatedState = new SharedSSLState(providerTlsFlags); |
2727 | 0 | sharedState = allocatedState.get(); |
2728 | 0 | } else { |
2729 | 0 | sharedState = (providerFlags & nsISocketProvider::NO_PERMANENT_STORAGE) ? PrivateSSLState() : PublicSSLState(); |
2730 | 0 | } |
2731 | 0 |
|
2732 | 0 | nsNSSSocketInfo* infoObject = new nsNSSSocketInfo(*sharedState, providerFlags, providerTlsFlags); |
2733 | 0 | if (!infoObject) return NS_ERROR_FAILURE; |
2734 | 0 | |
2735 | 0 | NS_ADDREF(infoObject); |
2736 | 0 | infoObject->SetForSTARTTLS(forSTARTTLS); |
2737 | 0 | infoObject->SetHostName(host); |
2738 | 0 | infoObject->SetPort(port); |
2739 | 0 | infoObject->SetOriginAttributes(originAttributes); |
2740 | 0 | if (allocatedState) { |
2741 | 0 | infoObject->SetSharedOwningReference(allocatedState); |
2742 | 0 | } |
2743 | 0 |
|
2744 | 0 | bool haveProxy = false; |
2745 | 0 | if (proxy) { |
2746 | 0 | nsCString proxyHost; |
2747 | 0 | proxy->GetHost(proxyHost); |
2748 | 0 | haveProxy = !proxyHost.IsEmpty(); |
2749 | 0 | } |
2750 | 0 |
|
2751 | 0 | // A plaintext observer shim is inserted so we can observe some protocol |
2752 | 0 | // details without modifying nss |
2753 | 0 | plaintextLayer = PR_CreateIOLayerStub(nsSSLIOLayerHelpers::nsSSLPlaintextLayerIdentity, |
2754 | 0 | &nsSSLIOLayerHelpers::nsSSLPlaintextLayerMethods); |
2755 | 0 | if (plaintextLayer) { |
2756 | 0 | plaintextLayer->secret = (PRFilePrivate*) infoObject; |
2757 | 0 | stat = PR_PushIOLayer(fd, PR_TOP_IO_LAYER, plaintextLayer); |
2758 | 0 | if (stat == PR_FAILURE) { |
2759 | 0 | plaintextLayer->dtor(plaintextLayer); |
2760 | 0 | plaintextLayer = nullptr; |
2761 | 0 | } |
2762 | 0 | } |
2763 | 0 |
|
2764 | 0 | PRFileDesc* sslSock = nsSSLIOLayerImportFD(fd, infoObject, host); |
2765 | 0 | if (!sslSock) { |
2766 | 0 | MOZ_ASSERT_UNREACHABLE("NSS: Error importing socket"); |
2767 | 0 | goto loser; |
2768 | 0 | } |
2769 | 0 |
|
2770 | 0 | infoObject->SetFileDescPtr(sslSock); |
2771 | 0 |
|
2772 | 0 | rv = nsSSLIOLayerSetOptions(sslSock, forSTARTTLS, haveProxy, host, port, |
2773 | 0 | infoObject); |
2774 | 0 |
|
2775 | 0 | if (NS_FAILED(rv)) |
2776 | 0 | goto loser; |
2777 | 0 | |
2778 | 0 | // Now, layer ourselves on top of the SSL socket... |
2779 | 0 | layer = PR_CreateIOLayerStub(nsSSLIOLayerHelpers::nsSSLIOLayerIdentity, |
2780 | 0 | &nsSSLIOLayerHelpers::nsSSLIOLayerMethods); |
2781 | 0 | if (!layer) |
2782 | 0 | goto loser; |
2783 | 0 | |
2784 | 0 | layer->secret = (PRFilePrivate*) infoObject; |
2785 | 0 | stat = PR_PushIOLayer(sslSock, PR_GetLayersIdentity(sslSock), layer); |
2786 | 0 |
|
2787 | 0 | if (stat == PR_FAILURE) { |
2788 | 0 | goto loser; |
2789 | 0 | } |
2790 | 0 | |
2791 | 0 | MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("[%p] Socket set up\n", (void*) sslSock)); |
2792 | 0 | infoObject->QueryInterface(NS_GET_IID(nsISupports), (void**) (info)); |
2793 | 0 |
|
2794 | 0 | // We are going use a clear connection first // |
2795 | 0 | if (forSTARTTLS || haveProxy) { |
2796 | 0 | infoObject->SetHandshakeNotPending(); |
2797 | 0 | } |
2798 | 0 |
|
2799 | 0 | infoObject->SharedState().NoteSocketCreated(); |
2800 | 0 |
|
2801 | 0 | return NS_OK; |
2802 | 0 | loser: |
2803 | 0 | NS_IF_RELEASE(infoObject); |
2804 | 0 | if (layer) { |
2805 | 0 | layer->dtor(layer); |
2806 | 0 | } |
2807 | 0 | if (plaintextLayer) { |
2808 | 0 | // Note that PR_*IOLayer operations may modify the stack of fds, so a |
2809 | 0 | // previously-valid pointer may no longer point to what we think it points |
2810 | 0 | // to after calling PR_PopIOLayer. We must operate on the pointer returned |
2811 | 0 | // by PR_PopIOLayer. |
2812 | 0 | plaintextLayer = PR_PopIOLayer(fd, nsSSLIOLayerHelpers::nsSSLPlaintextLayerIdentity); |
2813 | 0 | plaintextLayer->dtor(plaintextLayer); |
2814 | 0 | } |
2815 | 0 | return NS_ERROR_FAILURE; |
2816 | 0 | } |