|
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 "nsNSSCallbacks.h" |
|
8 #include "pkix/pkixtypes.h" |
|
9 #include "mozilla/Telemetry.h" |
|
10 #include "mozilla/TimeStamp.h" |
|
11 #include "nsNSSComponent.h" |
|
12 #include "nsNSSIOLayer.h" |
|
13 #include "nsIWebProgressListener.h" |
|
14 #include "nsProtectedAuthThread.h" |
|
15 #include "nsITokenDialogs.h" |
|
16 #include "nsIUploadChannel.h" |
|
17 #include "nsIPrompt.h" |
|
18 #include "nsProxyRelease.h" |
|
19 #include "PSMRunnable.h" |
|
20 #include "nsContentUtils.h" |
|
21 #include "nsIHttpChannelInternal.h" |
|
22 #include "nsISupportsPriority.h" |
|
23 #include "nsNetUtil.h" |
|
24 #include "SharedSSLState.h" |
|
25 #include "ssl.h" |
|
26 #include "sslproto.h" |
|
27 |
|
28 using namespace mozilla; |
|
29 using namespace mozilla::psm; |
|
30 |
|
31 #ifdef PR_LOGGING |
|
32 extern PRLogModuleInfo* gPIPNSSLog; |
|
33 #endif |
|
34 |
|
35 static void AccumulateCipherSuite(Telemetry::ID probe, |
|
36 const SSLChannelInfo& channelInfo); |
|
37 |
|
38 namespace { |
|
39 |
|
40 // Bits in bit mask for SSL_REASONS_FOR_NOT_FALSE_STARTING telemetry probe |
|
41 // These bits are numbered so that the least subtle issues have higher values. |
|
42 // This should make it easier for us to interpret the results. |
|
43 const uint32_t NPN_NOT_NEGOTIATED = 64; |
|
44 const uint32_t KEA_NOT_FORWARD_SECRET = 32; |
|
45 const uint32_t KEA_NOT_SAME_AS_EXPECTED = 16; |
|
46 const uint32_t KEA_NOT_ALLOWED = 8; |
|
47 const uint32_t POSSIBLE_VERSION_DOWNGRADE = 4; |
|
48 const uint32_t POSSIBLE_CIPHER_SUITE_DOWNGRADE = 2; |
|
49 const uint32_t KEA_NOT_SUPPORTED = 1; |
|
50 |
|
51 } |
|
52 |
|
53 class nsHTTPDownloadEvent : public nsRunnable { |
|
54 public: |
|
55 nsHTTPDownloadEvent(); |
|
56 ~nsHTTPDownloadEvent(); |
|
57 |
|
58 NS_IMETHOD Run(); |
|
59 |
|
60 nsNSSHttpRequestSession *mRequestSession; |
|
61 |
|
62 nsRefPtr<nsHTTPListener> mListener; |
|
63 bool mResponsibleForDoneSignal; |
|
64 TimeStamp mStartTime; |
|
65 }; |
|
66 |
|
67 nsHTTPDownloadEvent::nsHTTPDownloadEvent() |
|
68 :mResponsibleForDoneSignal(true) |
|
69 { |
|
70 } |
|
71 |
|
72 nsHTTPDownloadEvent::~nsHTTPDownloadEvent() |
|
73 { |
|
74 if (mResponsibleForDoneSignal && mListener) |
|
75 mListener->send_done_signal(); |
|
76 |
|
77 mRequestSession->Release(); |
|
78 } |
|
79 |
|
80 NS_IMETHODIMP |
|
81 nsHTTPDownloadEvent::Run() |
|
82 { |
|
83 if (!mListener) |
|
84 return NS_OK; |
|
85 |
|
86 nsresult rv; |
|
87 |
|
88 nsCOMPtr<nsIIOService> ios = do_GetIOService(); |
|
89 NS_ENSURE_STATE(ios); |
|
90 |
|
91 nsCOMPtr<nsIChannel> chan; |
|
92 ios->NewChannel(mRequestSession->mURL, nullptr, nullptr, getter_AddRefs(chan)); |
|
93 NS_ENSURE_STATE(chan); |
|
94 |
|
95 // Security operations scheduled through normal HTTP channels are given |
|
96 // high priority to accommodate real time OCSP transactions. Background CRL |
|
97 // fetches happen through a different path (CRLDownloadEvent). |
|
98 nsCOMPtr<nsISupportsPriority> priorityChannel = do_QueryInterface(chan); |
|
99 if (priorityChannel) |
|
100 priorityChannel->AdjustPriority(nsISupportsPriority::PRIORITY_HIGHEST); |
|
101 |
|
102 chan->SetLoadFlags(nsIRequest::LOAD_ANONYMOUS); |
|
103 |
|
104 // Create a loadgroup for this new channel. This way if the channel |
|
105 // is redirected, we'll have a way to cancel the resulting channel. |
|
106 nsCOMPtr<nsILoadGroup> lg = do_CreateInstance(NS_LOADGROUP_CONTRACTID); |
|
107 chan->SetLoadGroup(lg); |
|
108 |
|
109 if (mRequestSession->mHasPostData) |
|
110 { |
|
111 nsCOMPtr<nsIInputStream> uploadStream; |
|
112 rv = NS_NewPostDataStream(getter_AddRefs(uploadStream), |
|
113 false, |
|
114 mRequestSession->mPostData); |
|
115 NS_ENSURE_SUCCESS(rv, rv); |
|
116 |
|
117 nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(chan)); |
|
118 NS_ENSURE_STATE(uploadChannel); |
|
119 |
|
120 rv = uploadChannel->SetUploadStream(uploadStream, |
|
121 mRequestSession->mPostContentType, |
|
122 -1); |
|
123 NS_ENSURE_SUCCESS(rv, rv); |
|
124 } |
|
125 |
|
126 // Do not use SPDY for internal security operations. It could result |
|
127 // in the silent upgrade to ssl, which in turn could require an SSL |
|
128 // operation to fufill something like a CRL fetch, which is an |
|
129 // endless loop. |
|
130 nsCOMPtr<nsIHttpChannelInternal> internalChannel = do_QueryInterface(chan); |
|
131 if (internalChannel) { |
|
132 rv = internalChannel->SetAllowSpdy(false); |
|
133 NS_ENSURE_SUCCESS(rv, rv); |
|
134 } |
|
135 |
|
136 nsCOMPtr<nsIHttpChannel> hchan = do_QueryInterface(chan); |
|
137 NS_ENSURE_STATE(hchan); |
|
138 |
|
139 rv = hchan->SetRequestMethod(mRequestSession->mRequestMethod); |
|
140 NS_ENSURE_SUCCESS(rv, rv); |
|
141 |
|
142 mResponsibleForDoneSignal = false; |
|
143 mListener->mResponsibleForDoneSignal = true; |
|
144 |
|
145 mListener->mLoadGroup = lg.get(); |
|
146 NS_ADDREF(mListener->mLoadGroup); |
|
147 mListener->mLoadGroupOwnerThread = PR_GetCurrentThread(); |
|
148 |
|
149 rv = NS_NewStreamLoader(getter_AddRefs(mListener->mLoader), |
|
150 mListener); |
|
151 |
|
152 if (NS_SUCCEEDED(rv)) { |
|
153 mStartTime = TimeStamp::Now(); |
|
154 rv = hchan->AsyncOpen(mListener->mLoader, nullptr); |
|
155 } |
|
156 |
|
157 if (NS_FAILED(rv)) { |
|
158 mListener->mResponsibleForDoneSignal = false; |
|
159 mResponsibleForDoneSignal = true; |
|
160 |
|
161 NS_RELEASE(mListener->mLoadGroup); |
|
162 mListener->mLoadGroup = nullptr; |
|
163 mListener->mLoadGroupOwnerThread = nullptr; |
|
164 } |
|
165 |
|
166 return NS_OK; |
|
167 } |
|
168 |
|
169 struct nsCancelHTTPDownloadEvent : nsRunnable { |
|
170 nsRefPtr<nsHTTPListener> mListener; |
|
171 |
|
172 NS_IMETHOD Run() { |
|
173 mListener->FreeLoadGroup(true); |
|
174 mListener = nullptr; |
|
175 return NS_OK; |
|
176 } |
|
177 }; |
|
178 |
|
179 SECStatus nsNSSHttpServerSession::createSessionFcn(const char *host, |
|
180 uint16_t portnum, |
|
181 SEC_HTTP_SERVER_SESSION *pSession) |
|
182 { |
|
183 if (!host || !pSession) |
|
184 return SECFailure; |
|
185 |
|
186 nsNSSHttpServerSession *hss = new nsNSSHttpServerSession; |
|
187 if (!hss) |
|
188 return SECFailure; |
|
189 |
|
190 hss->mHost = host; |
|
191 hss->mPort = portnum; |
|
192 |
|
193 *pSession = hss; |
|
194 return SECSuccess; |
|
195 } |
|
196 |
|
197 SECStatus nsNSSHttpRequestSession::createFcn(SEC_HTTP_SERVER_SESSION session, |
|
198 const char *http_protocol_variant, |
|
199 const char *path_and_query_string, |
|
200 const char *http_request_method, |
|
201 const PRIntervalTime timeout, |
|
202 SEC_HTTP_REQUEST_SESSION *pRequest) |
|
203 { |
|
204 if (!session || !http_protocol_variant || !path_and_query_string || |
|
205 !http_request_method || !pRequest) |
|
206 return SECFailure; |
|
207 |
|
208 nsNSSHttpServerSession* hss = static_cast<nsNSSHttpServerSession*>(session); |
|
209 if (!hss) |
|
210 return SECFailure; |
|
211 |
|
212 nsNSSHttpRequestSession *rs = new nsNSSHttpRequestSession; |
|
213 if (!rs) |
|
214 return SECFailure; |
|
215 |
|
216 rs->mTimeoutInterval = timeout; |
|
217 |
|
218 // Use a maximum timeout value of 10 seconds because of bug 404059. |
|
219 // FIXME: Use a better approach once 406120 is ready. |
|
220 uint32_t maxBug404059Timeout = PR_TicksPerSecond() * 10; |
|
221 if (timeout > maxBug404059Timeout) { |
|
222 rs->mTimeoutInterval = maxBug404059Timeout; |
|
223 } |
|
224 |
|
225 rs->mURL.Assign(http_protocol_variant); |
|
226 rs->mURL.AppendLiteral("://"); |
|
227 rs->mURL.Append(hss->mHost); |
|
228 rs->mURL.AppendLiteral(":"); |
|
229 rs->mURL.AppendInt(hss->mPort); |
|
230 rs->mURL.Append(path_and_query_string); |
|
231 |
|
232 rs->mRequestMethod = http_request_method; |
|
233 |
|
234 *pRequest = (void*)rs; |
|
235 return SECSuccess; |
|
236 } |
|
237 |
|
238 SECStatus nsNSSHttpRequestSession::setPostDataFcn(const char *http_data, |
|
239 const uint32_t http_data_len, |
|
240 const char *http_content_type) |
|
241 { |
|
242 mHasPostData = true; |
|
243 mPostData.Assign(http_data, http_data_len); |
|
244 mPostContentType.Assign(http_content_type); |
|
245 |
|
246 return SECSuccess; |
|
247 } |
|
248 |
|
249 SECStatus nsNSSHttpRequestSession::addHeaderFcn(const char *http_header_name, |
|
250 const char *http_header_value) |
|
251 { |
|
252 return SECFailure; // not yet implemented |
|
253 |
|
254 // All http code needs to be postponed to the UI thread. |
|
255 // Once this gets implemented, we need to add a string list member to |
|
256 // nsNSSHttpRequestSession and queue up the headers, |
|
257 // so they can be added in HandleHTTPDownloadPLEvent. |
|
258 // |
|
259 // The header will need to be set using |
|
260 // mHttpChannel->SetRequestHeader(nsDependentCString(http_header_name), |
|
261 // nsDependentCString(http_header_value), |
|
262 // false))); |
|
263 } |
|
264 |
|
265 SECStatus nsNSSHttpRequestSession::trySendAndReceiveFcn(PRPollDesc **pPollDesc, |
|
266 uint16_t *http_response_code, |
|
267 const char **http_response_content_type, |
|
268 const char **http_response_headers, |
|
269 const char **http_response_data, |
|
270 uint32_t *http_response_data_len) |
|
271 { |
|
272 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, |
|
273 ("nsNSSHttpRequestSession::trySendAndReceiveFcn to %s\n", mURL.get())); |
|
274 |
|
275 bool onSTSThread; |
|
276 nsresult nrv; |
|
277 nsCOMPtr<nsIEventTarget> sts |
|
278 = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &nrv); |
|
279 if (NS_FAILED(nrv)) { |
|
280 NS_ERROR("Could not get STS service"); |
|
281 PR_SetError(PR_INVALID_STATE_ERROR, 0); |
|
282 return SECFailure; |
|
283 } |
|
284 |
|
285 nrv = sts->IsOnCurrentThread(&onSTSThread); |
|
286 if (NS_FAILED(nrv)) { |
|
287 NS_ERROR("IsOnCurrentThread failed"); |
|
288 PR_SetError(PR_INVALID_STATE_ERROR, 0); |
|
289 return SECFailure; |
|
290 } |
|
291 |
|
292 if (onSTSThread) { |
|
293 NS_ERROR("nsNSSHttpRequestSession::trySendAndReceiveFcn called on socket " |
|
294 "thread; this will not work."); |
|
295 PR_SetError(PR_INVALID_STATE_ERROR, 0); |
|
296 return SECFailure; |
|
297 } |
|
298 |
|
299 const int max_retries = 2; |
|
300 int retry_count = 0; |
|
301 bool retryable_error = false; |
|
302 SECStatus result_sec_status = SECFailure; |
|
303 |
|
304 do |
|
305 { |
|
306 if (retry_count > 0) |
|
307 { |
|
308 if (retryable_error) |
|
309 { |
|
310 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, |
|
311 ("nsNSSHttpRequestSession::trySendAndReceiveFcn - sleeping and retrying: %d of %d\n", |
|
312 retry_count, max_retries)); |
|
313 } |
|
314 |
|
315 PR_Sleep( PR_MillisecondsToInterval(300) * retry_count ); |
|
316 } |
|
317 |
|
318 ++retry_count; |
|
319 retryable_error = false; |
|
320 |
|
321 result_sec_status = |
|
322 internal_send_receive_attempt(retryable_error, pPollDesc, http_response_code, |
|
323 http_response_content_type, http_response_headers, |
|
324 http_response_data, http_response_data_len); |
|
325 } |
|
326 while (retryable_error && |
|
327 retry_count < max_retries); |
|
328 |
|
329 #ifdef PR_LOGGING |
|
330 if (retry_count > 1) |
|
331 { |
|
332 if (retryable_error) |
|
333 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, |
|
334 ("nsNSSHttpRequestSession::trySendAndReceiveFcn - still failing, giving up...\n")); |
|
335 else |
|
336 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, |
|
337 ("nsNSSHttpRequestSession::trySendAndReceiveFcn - success at attempt %d\n", |
|
338 retry_count)); |
|
339 } |
|
340 #endif |
|
341 |
|
342 return result_sec_status; |
|
343 } |
|
344 |
|
345 void |
|
346 nsNSSHttpRequestSession::AddRef() |
|
347 { |
|
348 ++mRefCount; |
|
349 } |
|
350 |
|
351 void |
|
352 nsNSSHttpRequestSession::Release() |
|
353 { |
|
354 int32_t newRefCount = --mRefCount; |
|
355 if (!newRefCount) { |
|
356 delete this; |
|
357 } |
|
358 } |
|
359 |
|
360 SECStatus |
|
361 nsNSSHttpRequestSession::internal_send_receive_attempt(bool &retryable_error, |
|
362 PRPollDesc **pPollDesc, |
|
363 uint16_t *http_response_code, |
|
364 const char **http_response_content_type, |
|
365 const char **http_response_headers, |
|
366 const char **http_response_data, |
|
367 uint32_t *http_response_data_len) |
|
368 { |
|
369 if (pPollDesc) *pPollDesc = nullptr; |
|
370 if (http_response_code) *http_response_code = 0; |
|
371 if (http_response_content_type) *http_response_content_type = 0; |
|
372 if (http_response_headers) *http_response_headers = 0; |
|
373 if (http_response_data) *http_response_data = 0; |
|
374 |
|
375 uint32_t acceptableResultSize = 0; |
|
376 |
|
377 if (http_response_data_len) |
|
378 { |
|
379 acceptableResultSize = *http_response_data_len; |
|
380 *http_response_data_len = 0; |
|
381 } |
|
382 |
|
383 if (!mListener) |
|
384 return SECFailure; |
|
385 |
|
386 Mutex& waitLock = mListener->mLock; |
|
387 CondVar& waitCondition = mListener->mCondition; |
|
388 volatile bool &waitFlag = mListener->mWaitFlag; |
|
389 waitFlag = true; |
|
390 |
|
391 RefPtr<nsHTTPDownloadEvent> event(new nsHTTPDownloadEvent); |
|
392 if (!event) |
|
393 return SECFailure; |
|
394 |
|
395 event->mListener = mListener; |
|
396 this->AddRef(); |
|
397 event->mRequestSession = this; |
|
398 |
|
399 nsresult rv = NS_DispatchToMainThread(event); |
|
400 if (NS_FAILED(rv)) |
|
401 { |
|
402 event->mResponsibleForDoneSignal = false; |
|
403 return SECFailure; |
|
404 } |
|
405 |
|
406 bool request_canceled = false; |
|
407 |
|
408 { |
|
409 MutexAutoLock locker(waitLock); |
|
410 |
|
411 const PRIntervalTime start_time = PR_IntervalNow(); |
|
412 PRIntervalTime wait_interval; |
|
413 |
|
414 bool running_on_main_thread = NS_IsMainThread(); |
|
415 if (running_on_main_thread) |
|
416 { |
|
417 // The result of running this on the main thread |
|
418 // is a series of small timeouts mixed with spinning the |
|
419 // event loop - this is always dangerous as there is so much main |
|
420 // thread code that does not expect to be called re-entrantly. Your |
|
421 // app really shouldn't do that. |
|
422 NS_WARNING("Security network blocking I/O on Main Thread"); |
|
423 |
|
424 // let's process events quickly |
|
425 wait_interval = PR_MicrosecondsToInterval(50); |
|
426 } |
|
427 else |
|
428 { |
|
429 // On a secondary thread, it's fine to wait some more for |
|
430 // for the condition variable. |
|
431 wait_interval = PR_MillisecondsToInterval(250); |
|
432 } |
|
433 |
|
434 while (waitFlag) |
|
435 { |
|
436 if (running_on_main_thread) |
|
437 { |
|
438 // Networking runs on the main thread, which we happen to block here. |
|
439 // Processing events will allow the OCSP networking to run while we |
|
440 // are waiting. Thanks a lot to Darin Fisher for rewriting the |
|
441 // thread manager. Thanks a lot to Christian Biesinger who |
|
442 // made me aware of this possibility. (kaie) |
|
443 |
|
444 MutexAutoUnlock unlock(waitLock); |
|
445 NS_ProcessNextEvent(nullptr); |
|
446 } |
|
447 |
|
448 waitCondition.Wait(wait_interval); |
|
449 |
|
450 if (!waitFlag) |
|
451 break; |
|
452 |
|
453 if (!request_canceled) |
|
454 { |
|
455 bool timeout = |
|
456 (PRIntervalTime)(PR_IntervalNow() - start_time) > mTimeoutInterval; |
|
457 |
|
458 if (timeout) |
|
459 { |
|
460 request_canceled = true; |
|
461 |
|
462 RefPtr<nsCancelHTTPDownloadEvent> cancelevent( |
|
463 new nsCancelHTTPDownloadEvent); |
|
464 cancelevent->mListener = mListener; |
|
465 rv = NS_DispatchToMainThread(cancelevent); |
|
466 if (NS_FAILED(rv)) { |
|
467 NS_WARNING("cannot post cancel event"); |
|
468 } |
|
469 break; |
|
470 } |
|
471 } |
|
472 } |
|
473 } |
|
474 |
|
475 if (!event->mStartTime.IsNull()) { |
|
476 if (request_canceled) { |
|
477 Telemetry::Accumulate(Telemetry::CERT_VALIDATION_HTTP_REQUEST_RESULT, 0); |
|
478 Telemetry::AccumulateTimeDelta( |
|
479 Telemetry::CERT_VALIDATION_HTTP_REQUEST_CANCELED_TIME, |
|
480 event->mStartTime, TimeStamp::Now()); |
|
481 } |
|
482 else if (NS_SUCCEEDED(mListener->mResultCode) && |
|
483 mListener->mHttpResponseCode == 200) { |
|
484 Telemetry::Accumulate(Telemetry::CERT_VALIDATION_HTTP_REQUEST_RESULT, 1); |
|
485 Telemetry::AccumulateTimeDelta( |
|
486 Telemetry::CERT_VALIDATION_HTTP_REQUEST_SUCCEEDED_TIME, |
|
487 event->mStartTime, TimeStamp::Now()); |
|
488 } |
|
489 else { |
|
490 Telemetry::Accumulate(Telemetry::CERT_VALIDATION_HTTP_REQUEST_RESULT, 2); |
|
491 Telemetry::AccumulateTimeDelta( |
|
492 Telemetry::CERT_VALIDATION_HTTP_REQUEST_FAILED_TIME, |
|
493 event->mStartTime, TimeStamp::Now()); |
|
494 } |
|
495 } |
|
496 else { |
|
497 Telemetry::Accumulate(Telemetry::CERT_VALIDATION_HTTP_REQUEST_RESULT, 3); |
|
498 } |
|
499 |
|
500 if (request_canceled) |
|
501 return SECFailure; |
|
502 |
|
503 if (NS_FAILED(mListener->mResultCode)) |
|
504 { |
|
505 if (mListener->mResultCode == NS_ERROR_CONNECTION_REFUSED |
|
506 || |
|
507 mListener->mResultCode == NS_ERROR_NET_RESET) |
|
508 { |
|
509 retryable_error = true; |
|
510 } |
|
511 return SECFailure; |
|
512 } |
|
513 |
|
514 if (http_response_code) |
|
515 *http_response_code = mListener->mHttpResponseCode; |
|
516 |
|
517 if (mListener->mHttpRequestSucceeded && http_response_data && http_response_data_len) { |
|
518 |
|
519 *http_response_data_len = mListener->mResultLen; |
|
520 |
|
521 // acceptableResultSize == 0 means: any size is acceptable |
|
522 if (acceptableResultSize != 0 |
|
523 && |
|
524 acceptableResultSize < mListener->mResultLen) |
|
525 { |
|
526 return SECFailure; |
|
527 } |
|
528 |
|
529 // return data by reference, result data will be valid |
|
530 // until "this" gets destroyed by NSS |
|
531 *http_response_data = (const char*)mListener->mResultData; |
|
532 } |
|
533 |
|
534 if (mListener->mHttpRequestSucceeded && http_response_content_type) { |
|
535 if (mListener->mHttpResponseContentType.Length()) { |
|
536 *http_response_content_type = mListener->mHttpResponseContentType.get(); |
|
537 } |
|
538 } |
|
539 |
|
540 return SECSuccess; |
|
541 } |
|
542 |
|
543 SECStatus nsNSSHttpRequestSession::cancelFcn() |
|
544 { |
|
545 // As of today, only the blocking variant of the http interface |
|
546 // has been implemented. Implementing cancelFcn will be necessary |
|
547 // as soon as we implement the nonblocking variant. |
|
548 return SECSuccess; |
|
549 } |
|
550 |
|
551 SECStatus nsNSSHttpRequestSession::freeFcn() |
|
552 { |
|
553 Release(); |
|
554 return SECSuccess; |
|
555 } |
|
556 |
|
557 nsNSSHttpRequestSession::nsNSSHttpRequestSession() |
|
558 : mRefCount(1), |
|
559 mHasPostData(false), |
|
560 mTimeoutInterval(0), |
|
561 mListener(new nsHTTPListener) |
|
562 { |
|
563 } |
|
564 |
|
565 nsNSSHttpRequestSession::~nsNSSHttpRequestSession() |
|
566 { |
|
567 } |
|
568 |
|
569 SEC_HttpClientFcn nsNSSHttpInterface::sNSSInterfaceTable; |
|
570 |
|
571 void nsNSSHttpInterface::initTable() |
|
572 { |
|
573 sNSSInterfaceTable.version = 1; |
|
574 SEC_HttpClientFcnV1 &v1 = sNSSInterfaceTable.fcnTable.ftable1; |
|
575 v1.createSessionFcn = createSessionFcn; |
|
576 v1.keepAliveSessionFcn = keepAliveFcn; |
|
577 v1.freeSessionFcn = freeSessionFcn; |
|
578 v1.createFcn = createFcn; |
|
579 v1.setPostDataFcn = setPostDataFcn; |
|
580 v1.addHeaderFcn = addHeaderFcn; |
|
581 v1.trySendAndReceiveFcn = trySendAndReceiveFcn; |
|
582 v1.cancelFcn = cancelFcn; |
|
583 v1.freeFcn = freeFcn; |
|
584 } |
|
585 |
|
586 void nsNSSHttpInterface::registerHttpClient() |
|
587 { |
|
588 SEC_RegisterDefaultHttpClient(&sNSSInterfaceTable); |
|
589 } |
|
590 |
|
591 void nsNSSHttpInterface::unregisterHttpClient() |
|
592 { |
|
593 SEC_RegisterDefaultHttpClient(nullptr); |
|
594 } |
|
595 |
|
596 nsHTTPListener::nsHTTPListener() |
|
597 : mResultData(nullptr), |
|
598 mResultLen(0), |
|
599 mLock("nsHTTPListener.mLock"), |
|
600 mCondition(mLock, "nsHTTPListener.mCondition"), |
|
601 mWaitFlag(true), |
|
602 mResponsibleForDoneSignal(false), |
|
603 mLoadGroup(nullptr), |
|
604 mLoadGroupOwnerThread(nullptr) |
|
605 { |
|
606 } |
|
607 |
|
608 nsHTTPListener::~nsHTTPListener() |
|
609 { |
|
610 if (mResponsibleForDoneSignal) |
|
611 send_done_signal(); |
|
612 |
|
613 if (mResultData) { |
|
614 NS_Free(const_cast<uint8_t *>(mResultData)); |
|
615 } |
|
616 |
|
617 if (mLoader) { |
|
618 nsCOMPtr<nsIThread> mainThread(do_GetMainThread()); |
|
619 NS_ProxyRelease(mainThread, mLoader); |
|
620 } |
|
621 } |
|
622 |
|
623 NS_IMPL_ISUPPORTS(nsHTTPListener, nsIStreamLoaderObserver) |
|
624 |
|
625 void |
|
626 nsHTTPListener::FreeLoadGroup(bool aCancelLoad) |
|
627 { |
|
628 nsILoadGroup *lg = nullptr; |
|
629 |
|
630 MutexAutoLock locker(mLock); |
|
631 |
|
632 if (mLoadGroup) { |
|
633 if (mLoadGroupOwnerThread != PR_GetCurrentThread()) { |
|
634 NS_ASSERTION(false, |
|
635 "attempt to access nsHTTPDownloadEvent::mLoadGroup on multiple threads, leaking it!"); |
|
636 } |
|
637 else { |
|
638 lg = mLoadGroup; |
|
639 mLoadGroup = nullptr; |
|
640 } |
|
641 } |
|
642 |
|
643 if (lg) { |
|
644 if (aCancelLoad) { |
|
645 lg->Cancel(NS_ERROR_ABORT); |
|
646 } |
|
647 NS_RELEASE(lg); |
|
648 } |
|
649 } |
|
650 |
|
651 NS_IMETHODIMP |
|
652 nsHTTPListener::OnStreamComplete(nsIStreamLoader* aLoader, |
|
653 nsISupports* aContext, |
|
654 nsresult aStatus, |
|
655 uint32_t stringLen, |
|
656 const uint8_t* string) |
|
657 { |
|
658 mResultCode = aStatus; |
|
659 |
|
660 FreeLoadGroup(false); |
|
661 |
|
662 nsCOMPtr<nsIRequest> req; |
|
663 nsCOMPtr<nsIHttpChannel> hchan; |
|
664 |
|
665 nsresult rv = aLoader->GetRequest(getter_AddRefs(req)); |
|
666 |
|
667 #ifdef PR_LOGGING |
|
668 if (NS_FAILED(aStatus)) |
|
669 { |
|
670 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, |
|
671 ("nsHTTPListener::OnStreamComplete status failed %d", aStatus)); |
|
672 } |
|
673 #endif |
|
674 |
|
675 if (NS_SUCCEEDED(rv)) |
|
676 hchan = do_QueryInterface(req, &rv); |
|
677 |
|
678 if (NS_SUCCEEDED(rv)) |
|
679 { |
|
680 rv = hchan->GetRequestSucceeded(&mHttpRequestSucceeded); |
|
681 if (NS_FAILED(rv)) |
|
682 mHttpRequestSucceeded = false; |
|
683 |
|
684 mResultLen = stringLen; |
|
685 mResultData = string; // take ownership of allocation |
|
686 aStatus = NS_SUCCESS_ADOPTED_DATA; |
|
687 |
|
688 unsigned int rcode; |
|
689 rv = hchan->GetResponseStatus(&rcode); |
|
690 if (NS_FAILED(rv)) |
|
691 mHttpResponseCode = 500; |
|
692 else |
|
693 mHttpResponseCode = rcode; |
|
694 |
|
695 hchan->GetResponseHeader(NS_LITERAL_CSTRING("Content-Type"), |
|
696 mHttpResponseContentType); |
|
697 } |
|
698 |
|
699 if (mResponsibleForDoneSignal) |
|
700 send_done_signal(); |
|
701 |
|
702 return aStatus; |
|
703 } |
|
704 |
|
705 void nsHTTPListener::send_done_signal() |
|
706 { |
|
707 mResponsibleForDoneSignal = false; |
|
708 |
|
709 { |
|
710 MutexAutoLock locker(mLock); |
|
711 mWaitFlag = false; |
|
712 mCondition.NotifyAll(); |
|
713 } |
|
714 } |
|
715 |
|
716 static char* |
|
717 ShowProtectedAuthPrompt(PK11SlotInfo* slot, nsIInterfaceRequestor *ir) |
|
718 { |
|
719 if (!NS_IsMainThread()) { |
|
720 NS_ERROR("ShowProtectedAuthPrompt called off the main thread"); |
|
721 return nullptr; |
|
722 } |
|
723 |
|
724 char* protAuthRetVal = nullptr; |
|
725 |
|
726 // Get protected auth dialogs |
|
727 nsITokenDialogs* dialogs = 0; |
|
728 nsresult nsrv = getNSSDialogs((void**)&dialogs, |
|
729 NS_GET_IID(nsITokenDialogs), |
|
730 NS_TOKENDIALOGS_CONTRACTID); |
|
731 if (NS_SUCCEEDED(nsrv)) |
|
732 { |
|
733 nsProtectedAuthThread* protectedAuthRunnable = new nsProtectedAuthThread(); |
|
734 if (protectedAuthRunnable) |
|
735 { |
|
736 NS_ADDREF(protectedAuthRunnable); |
|
737 |
|
738 protectedAuthRunnable->SetParams(slot); |
|
739 |
|
740 nsCOMPtr<nsIProtectedAuthThread> runnable = do_QueryInterface(protectedAuthRunnable); |
|
741 if (runnable) |
|
742 { |
|
743 nsrv = dialogs->DisplayProtectedAuth(ir, runnable); |
|
744 |
|
745 // We call join on the thread, |
|
746 // so we can be sure that no simultaneous access will happen. |
|
747 protectedAuthRunnable->Join(); |
|
748 |
|
749 if (NS_SUCCEEDED(nsrv)) |
|
750 { |
|
751 SECStatus rv = protectedAuthRunnable->GetResult(); |
|
752 switch (rv) |
|
753 { |
|
754 case SECSuccess: |
|
755 protAuthRetVal = ToNewCString(nsDependentCString(PK11_PW_AUTHENTICATED)); |
|
756 break; |
|
757 case SECWouldBlock: |
|
758 protAuthRetVal = ToNewCString(nsDependentCString(PK11_PW_RETRY)); |
|
759 break; |
|
760 default: |
|
761 protAuthRetVal = nullptr; |
|
762 break; |
|
763 |
|
764 } |
|
765 } |
|
766 } |
|
767 |
|
768 NS_RELEASE(protectedAuthRunnable); |
|
769 } |
|
770 |
|
771 NS_RELEASE(dialogs); |
|
772 } |
|
773 |
|
774 return protAuthRetVal; |
|
775 } |
|
776 |
|
777 class PK11PasswordPromptRunnable : public SyncRunnableBase |
|
778 { |
|
779 public: |
|
780 PK11PasswordPromptRunnable(PK11SlotInfo* slot, |
|
781 nsIInterfaceRequestor* ir) |
|
782 : mResult(nullptr), |
|
783 mSlot(slot), |
|
784 mIR(ir) |
|
785 { |
|
786 } |
|
787 char * mResult; // out |
|
788 virtual void RunOnTargetThread(); |
|
789 private: |
|
790 PK11SlotInfo* const mSlot; // in |
|
791 nsIInterfaceRequestor* const mIR; // in |
|
792 }; |
|
793 |
|
794 void PK11PasswordPromptRunnable::RunOnTargetThread() |
|
795 { |
|
796 static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID); |
|
797 |
|
798 nsNSSShutDownPreventionLock locker; |
|
799 nsresult rv = NS_OK; |
|
800 char16_t *password = nullptr; |
|
801 bool value = false; |
|
802 nsCOMPtr<nsIPrompt> prompt; |
|
803 |
|
804 /* TODO: Retry should generate a different dialog message */ |
|
805 /* |
|
806 if (retry) |
|
807 return nullptr; |
|
808 */ |
|
809 |
|
810 if (!mIR) |
|
811 { |
|
812 nsNSSComponent::GetNewPrompter(getter_AddRefs(prompt)); |
|
813 } |
|
814 else |
|
815 { |
|
816 prompt = do_GetInterface(mIR); |
|
817 NS_ASSERTION(prompt, "callbacks does not implement nsIPrompt"); |
|
818 } |
|
819 |
|
820 if (!prompt) |
|
821 return; |
|
822 |
|
823 if (PK11_ProtectedAuthenticationPath(mSlot)) { |
|
824 mResult = ShowProtectedAuthPrompt(mSlot, mIR); |
|
825 return; |
|
826 } |
|
827 |
|
828 nsAutoString promptString; |
|
829 nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(kNSSComponentCID, &rv)); |
|
830 |
|
831 if (NS_FAILED(rv)) |
|
832 return; |
|
833 |
|
834 const char16_t* formatStrings[1] = { |
|
835 ToNewUnicode(NS_ConvertUTF8toUTF16(PK11_GetTokenName(mSlot))) |
|
836 }; |
|
837 rv = nssComponent->PIPBundleFormatStringFromName("CertPassPrompt", |
|
838 formatStrings, 1, |
|
839 promptString); |
|
840 nsMemory::Free(const_cast<char16_t*>(formatStrings[0])); |
|
841 |
|
842 if (NS_FAILED(rv)) |
|
843 return; |
|
844 |
|
845 { |
|
846 nsPSMUITracker tracker; |
|
847 if (tracker.isUIForbidden()) { |
|
848 rv = NS_ERROR_NOT_AVAILABLE; |
|
849 } |
|
850 else { |
|
851 // Although the exact value is ignored, we must not pass invalid |
|
852 // bool values through XPConnect. |
|
853 bool checkState = false; |
|
854 rv = prompt->PromptPassword(nullptr, promptString.get(), |
|
855 &password, nullptr, &checkState, &value); |
|
856 } |
|
857 } |
|
858 |
|
859 if (NS_SUCCEEDED(rv) && value) { |
|
860 mResult = ToNewUTF8String(nsDependentString(password)); |
|
861 NS_Free(password); |
|
862 } |
|
863 } |
|
864 |
|
865 char* |
|
866 PK11PasswordPrompt(PK11SlotInfo* slot, PRBool retry, void* arg) |
|
867 { |
|
868 RefPtr<PK11PasswordPromptRunnable> runnable( |
|
869 new PK11PasswordPromptRunnable(slot, |
|
870 static_cast<nsIInterfaceRequestor*>(arg))); |
|
871 runnable->DispatchToMainThreadAndWait(); |
|
872 return runnable->mResult; |
|
873 } |
|
874 |
|
875 // call with shutdown prevention lock held |
|
876 static void |
|
877 PreliminaryHandshakeDone(PRFileDesc* fd) |
|
878 { |
|
879 nsNSSSocketInfo* infoObject = (nsNSSSocketInfo*) fd->higher->secret; |
|
880 if (!infoObject) |
|
881 return; |
|
882 |
|
883 if (infoObject->IsPreliminaryHandshakeDone()) |
|
884 return; |
|
885 |
|
886 infoObject->SetPreliminaryHandshakeDone(); |
|
887 |
|
888 SSLChannelInfo channelInfo; |
|
889 if (SSL_GetChannelInfo(fd, &channelInfo, sizeof(channelInfo)) == SECSuccess) { |
|
890 infoObject->SetSSLVersionUsed(channelInfo.protocolVersion); |
|
891 } |
|
892 |
|
893 // Get the NPN value. |
|
894 SSLNextProtoState state; |
|
895 unsigned char npnbuf[256]; |
|
896 unsigned int npnlen; |
|
897 |
|
898 if (SSL_GetNextProto(fd, &state, npnbuf, &npnlen, 256) == SECSuccess) { |
|
899 if (state == SSL_NEXT_PROTO_NEGOTIATED || |
|
900 state == SSL_NEXT_PROTO_SELECTED) { |
|
901 infoObject->SetNegotiatedNPN(reinterpret_cast<char *>(npnbuf), npnlen); |
|
902 } |
|
903 else { |
|
904 infoObject->SetNegotiatedNPN(nullptr, 0); |
|
905 } |
|
906 mozilla::Telemetry::Accumulate(Telemetry::SSL_NPN_TYPE, state); |
|
907 } |
|
908 else { |
|
909 infoObject->SetNegotiatedNPN(nullptr, 0); |
|
910 } |
|
911 } |
|
912 |
|
913 SECStatus |
|
914 CanFalseStartCallback(PRFileDesc* fd, void* client_data, PRBool *canFalseStart) |
|
915 { |
|
916 *canFalseStart = false; |
|
917 |
|
918 nsNSSShutDownPreventionLock locker; |
|
919 |
|
920 nsNSSSocketInfo* infoObject = (nsNSSSocketInfo*) fd->higher->secret; |
|
921 if (!infoObject) { |
|
922 PR_SetError(PR_INVALID_STATE_ERROR, 0); |
|
923 return SECFailure; |
|
924 } |
|
925 |
|
926 infoObject->SetFalseStartCallbackCalled(); |
|
927 |
|
928 if (infoObject->isAlreadyShutDown()) { |
|
929 MOZ_CRASH("SSL socket used after NSS shut down"); |
|
930 PR_SetError(PR_INVALID_STATE_ERROR, 0); |
|
931 return SECFailure; |
|
932 } |
|
933 |
|
934 PreliminaryHandshakeDone(fd); |
|
935 |
|
936 uint32_t reasonsForNotFalseStarting = 0; |
|
937 |
|
938 SSLChannelInfo channelInfo; |
|
939 if (SSL_GetChannelInfo(fd, &channelInfo, sizeof(channelInfo)) != SECSuccess) { |
|
940 return SECSuccess; |
|
941 } |
|
942 |
|
943 SSLCipherSuiteInfo cipherInfo; |
|
944 if (SSL_GetCipherSuiteInfo(channelInfo.cipherSuite, &cipherInfo, |
|
945 sizeof (cipherInfo)) != SECSuccess) { |
|
946 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("CanFalseStartCallback [%p] failed - " |
|
947 " KEA %d\n", fd, |
|
948 static_cast<int32_t>(cipherInfo.keaType))); |
|
949 return SECSuccess; |
|
950 } |
|
951 |
|
952 nsSSLIOLayerHelpers& helpers = infoObject->SharedState().IOLayerHelpers(); |
|
953 |
|
954 // Prevent version downgrade attacks from TLS 1.x to SSL 3.0. |
|
955 // TODO(bug 861310): If we negotiate less than our highest-supported version, |
|
956 // then check that a previously-completed handshake negotiated that version; |
|
957 // eventually, require that the highest-supported version of TLS is used. |
|
958 if (channelInfo.protocolVersion < SSL_LIBRARY_VERSION_TLS_1_0) { |
|
959 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("CanFalseStartCallback [%p] failed - " |
|
960 "SSL Version must be >= TLS1 %x\n", fd, |
|
961 static_cast<int32_t>(channelInfo.protocolVersion))); |
|
962 reasonsForNotFalseStarting |= POSSIBLE_VERSION_DOWNGRADE; |
|
963 } |
|
964 |
|
965 // never do false start without one of these key exchange algorithms |
|
966 if (cipherInfo.keaType != ssl_kea_rsa && |
|
967 cipherInfo.keaType != ssl_kea_dh && |
|
968 cipherInfo.keaType != ssl_kea_ecdh) { |
|
969 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("CanFalseStartCallback [%p] failed - " |
|
970 "unsupported KEA %d\n", fd, |
|
971 static_cast<int32_t>(cipherInfo.keaType))); |
|
972 reasonsForNotFalseStarting |= KEA_NOT_SUPPORTED; |
|
973 } |
|
974 |
|
975 // XXX: This assumes that all TLS_DH_* and TLS_ECDH_* cipher suites |
|
976 // are disabled. |
|
977 if (cipherInfo.keaType != ssl_kea_ecdh && |
|
978 cipherInfo.keaType != ssl_kea_dh) { |
|
979 if (helpers.mFalseStartRequireForwardSecrecy) { |
|
980 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, |
|
981 ("CanFalseStartCallback [%p] failed - KEA used is %d, but " |
|
982 "require-forward-secrecy configured.\n", fd, |
|
983 static_cast<int32_t>(cipherInfo.keaType))); |
|
984 reasonsForNotFalseStarting |= KEA_NOT_FORWARD_SECRET; |
|
985 } else if (cipherInfo.keaType == ssl_kea_rsa) { |
|
986 // Make sure we've seen the same kea from this host in the past, to limit |
|
987 // the potential for downgrade attacks. |
|
988 int16_t expected = infoObject->GetKEAExpected(); |
|
989 if (cipherInfo.keaType != expected) { |
|
990 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, |
|
991 ("CanFalseStartCallback [%p] failed - " |
|
992 "KEA used is %d, expected %d\n", fd, |
|
993 static_cast<int32_t>(cipherInfo.keaType), |
|
994 static_cast<int32_t>(expected))); |
|
995 reasonsForNotFalseStarting |= KEA_NOT_SAME_AS_EXPECTED; |
|
996 } |
|
997 } else { |
|
998 reasonsForNotFalseStarting |= KEA_NOT_ALLOWED; |
|
999 } |
|
1000 } |
|
1001 |
|
1002 // Prevent downgrade attacks on the symmetric cipher. We accept downgrades |
|
1003 // from 256-bit keys to 128-bit keys and we treat AES and Camellia as being |
|
1004 // equally secure. We consider every message authentication mechanism that we |
|
1005 // support *for these ciphers* to be equally-secure. We assume that for CBC |
|
1006 // mode, that the server has implemented all the same mitigations for |
|
1007 // published attacks that we have, or that those attacks are not relevant in |
|
1008 // the decision to false start. |
|
1009 if (cipherInfo.symCipher != ssl_calg_aes_gcm && |
|
1010 cipherInfo.symCipher != ssl_calg_aes && |
|
1011 cipherInfo.symCipher != ssl_calg_camellia) { |
|
1012 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, |
|
1013 ("CanFalseStartCallback [%p] failed - Symmetric cipher used, %d, " |
|
1014 "is not supported with False Start.\n", fd, |
|
1015 static_cast<int32_t>(cipherInfo.symCipher))); |
|
1016 reasonsForNotFalseStarting |= POSSIBLE_CIPHER_SUITE_DOWNGRADE; |
|
1017 } |
|
1018 |
|
1019 // XXX: An attacker can choose which protocols are advertised in the |
|
1020 // NPN extension. TODO(Bug 861311): We should restrict the ability |
|
1021 // of an attacker leverage this capability by restricting false start |
|
1022 // to the same protocol we previously saw for the server, after the |
|
1023 // first successful connection to the server. |
|
1024 |
|
1025 // Enforce NPN to do false start if policy requires it. Do this as an |
|
1026 // indicator if server compatibility. |
|
1027 if (helpers.mFalseStartRequireNPN) { |
|
1028 nsAutoCString negotiatedNPN; |
|
1029 if (NS_FAILED(infoObject->GetNegotiatedNPN(negotiatedNPN)) || |
|
1030 !negotiatedNPN.Length()) { |
|
1031 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("CanFalseStartCallback [%p] failed - " |
|
1032 "NPN cannot be verified\n", fd)); |
|
1033 reasonsForNotFalseStarting |= NPN_NOT_NEGOTIATED; |
|
1034 } |
|
1035 } |
|
1036 |
|
1037 Telemetry::Accumulate(Telemetry::SSL_REASONS_FOR_NOT_FALSE_STARTING, |
|
1038 reasonsForNotFalseStarting); |
|
1039 |
|
1040 if (reasonsForNotFalseStarting == 0) { |
|
1041 *canFalseStart = PR_TRUE; |
|
1042 infoObject->SetFalseStarted(); |
|
1043 infoObject->NoteTimeUntilReady(); |
|
1044 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("CanFalseStartCallback [%p] ok\n", fd)); |
|
1045 } |
|
1046 |
|
1047 return SECSuccess; |
|
1048 } |
|
1049 |
|
1050 static void |
|
1051 AccumulateNonECCKeySize(Telemetry::ID probe, uint32_t bits) |
|
1052 { |
|
1053 unsigned int value = bits < 512 ? 1 : bits == 512 ? 2 |
|
1054 : bits < 768 ? 3 : bits == 768 ? 4 |
|
1055 : bits < 1024 ? 5 : bits == 1024 ? 6 |
|
1056 : bits < 1280 ? 7 : bits == 1280 ? 8 |
|
1057 : bits < 1536 ? 9 : bits == 1536 ? 10 |
|
1058 : bits < 2048 ? 11 : bits == 2048 ? 12 |
|
1059 : bits < 3072 ? 13 : bits == 3072 ? 14 |
|
1060 : bits < 4096 ? 15 : bits == 4096 ? 16 |
|
1061 : bits < 8192 ? 17 : bits == 8192 ? 18 |
|
1062 : bits < 16384 ? 19 : bits == 16384 ? 20 |
|
1063 : 0; |
|
1064 Telemetry::Accumulate(probe, value); |
|
1065 } |
|
1066 |
|
1067 // XXX: This attempts to map a bit count to an ECC named curve identifier. In |
|
1068 // the vast majority of situations, we only have the Suite B curves available. |
|
1069 // In that case, this mapping works fine. If we were to have more curves |
|
1070 // available, the mapping would be ambiguous since there could be multiple |
|
1071 // named curves for a given size (e.g. secp256k1 vs. secp256r1). We punt on |
|
1072 // that for now. See also NSS bug 323674. |
|
1073 static void |
|
1074 AccumulateECCCurve(Telemetry::ID probe, uint32_t bits) |
|
1075 { |
|
1076 unsigned int value = bits == 256 ? 23 // P-256 |
|
1077 : bits == 384 ? 24 // P-384 |
|
1078 : bits == 521 ? 25 // P-521 |
|
1079 : 0; // Unknown |
|
1080 Telemetry::Accumulate(probe, value); |
|
1081 } |
|
1082 |
|
1083 static void |
|
1084 AccumulateCipherSuite(Telemetry::ID probe, const SSLChannelInfo& channelInfo) |
|
1085 { |
|
1086 uint32_t value; |
|
1087 switch (channelInfo.cipherSuite) { |
|
1088 // ECDHE key exchange |
|
1089 case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: value = 1; break; |
|
1090 case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: value = 2; break; |
|
1091 case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: value = 3; break; |
|
1092 case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: value = 4; break; |
|
1093 case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: value = 5; break; |
|
1094 case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: value = 6; break; |
|
1095 case TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA: value = 7; break; |
|
1096 case TLS_ECDHE_RSA_WITH_RC4_128_SHA: value = 8; break; |
|
1097 case TLS_ECDHE_ECDSA_WITH_RC4_128_SHA: value = 9; break; |
|
1098 case TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA: value = 10; break; |
|
1099 // DHE key exchange |
|
1100 case TLS_DHE_RSA_WITH_AES_128_CBC_SHA: value = 21; break; |
|
1101 case TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA: value = 22; break; |
|
1102 case TLS_DHE_RSA_WITH_AES_256_CBC_SHA: value = 23; break; |
|
1103 case TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA: value = 24; break; |
|
1104 case TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA: value = 25; break; |
|
1105 case TLS_DHE_DSS_WITH_AES_128_CBC_SHA: value = 26; break; |
|
1106 case TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA: value = 27; break; |
|
1107 case TLS_DHE_DSS_WITH_AES_256_CBC_SHA: value = 28; break; |
|
1108 case TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA: value = 29; break; |
|
1109 case TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA: value = 30; break; |
|
1110 // ECDH key exchange |
|
1111 case TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA: value = 41; break; |
|
1112 case TLS_ECDH_RSA_WITH_AES_128_CBC_SHA: value = 42; break; |
|
1113 case TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA: value = 43; break; |
|
1114 case TLS_ECDH_RSA_WITH_AES_256_CBC_SHA: value = 44; break; |
|
1115 case TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA: value = 45; break; |
|
1116 case TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA: value = 46; break; |
|
1117 case TLS_ECDH_ECDSA_WITH_RC4_128_SHA: value = 47; break; |
|
1118 case TLS_ECDH_RSA_WITH_RC4_128_SHA: value = 48; break; |
|
1119 // RSA key exchange |
|
1120 case TLS_RSA_WITH_AES_128_CBC_SHA: value = 61; break; |
|
1121 case TLS_RSA_WITH_CAMELLIA_128_CBC_SHA: value = 62; break; |
|
1122 case TLS_RSA_WITH_AES_256_CBC_SHA: value = 63; break; |
|
1123 case TLS_RSA_WITH_CAMELLIA_256_CBC_SHA: value = 64; break; |
|
1124 case SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA: value = 65; break; |
|
1125 case TLS_RSA_WITH_3DES_EDE_CBC_SHA: value = 66; break; |
|
1126 case TLS_RSA_WITH_SEED_CBC_SHA: value = 67; break; |
|
1127 case TLS_RSA_WITH_RC4_128_SHA: value = 68; break; |
|
1128 case TLS_RSA_WITH_RC4_128_MD5: value = 69; break; |
|
1129 // unknown |
|
1130 default: |
|
1131 value = 0; |
|
1132 break; |
|
1133 } |
|
1134 MOZ_ASSERT(value != 0); |
|
1135 Telemetry::Accumulate(probe, value); |
|
1136 } |
|
1137 |
|
1138 void HandshakeCallback(PRFileDesc* fd, void* client_data) { |
|
1139 nsNSSShutDownPreventionLock locker; |
|
1140 SECStatus rv; |
|
1141 |
|
1142 nsNSSSocketInfo* infoObject = (nsNSSSocketInfo*) fd->higher->secret; |
|
1143 |
|
1144 // Do the bookkeeping that needs to be done after the |
|
1145 // server's ServerHello...ServerHelloDone have been processed, but that doesn't |
|
1146 // need the handshake to be completed. |
|
1147 PreliminaryHandshakeDone(fd); |
|
1148 |
|
1149 nsSSLIOLayerHelpers& ioLayerHelpers |
|
1150 = infoObject->SharedState().IOLayerHelpers(); |
|
1151 |
|
1152 SSLVersionRange versions(infoObject->GetTLSVersionRange()); |
|
1153 |
|
1154 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, |
|
1155 ("[%p] HandshakeCallback: succeeded using TLS version range (0x%04x,0x%04x)\n", |
|
1156 fd, static_cast<unsigned int>(versions.min), |
|
1157 static_cast<unsigned int>(versions.max))); |
|
1158 |
|
1159 // If the handshake completed, then we know the site is TLS tolerant |
|
1160 ioLayerHelpers.rememberTolerantAtVersion(infoObject->GetHostName(), |
|
1161 infoObject->GetPort(), |
|
1162 versions.max); |
|
1163 |
|
1164 PRBool siteSupportsSafeRenego; |
|
1165 rv = SSL_HandshakeNegotiatedExtension(fd, ssl_renegotiation_info_xtn, |
|
1166 &siteSupportsSafeRenego); |
|
1167 MOZ_ASSERT(rv == SECSuccess); |
|
1168 if (rv != SECSuccess) { |
|
1169 siteSupportsSafeRenego = false; |
|
1170 } |
|
1171 |
|
1172 if (siteSupportsSafeRenego || |
|
1173 !ioLayerHelpers.treatUnsafeNegotiationAsBroken()) { |
|
1174 infoObject->SetSecurityState(nsIWebProgressListener::STATE_IS_SECURE | |
|
1175 nsIWebProgressListener::STATE_SECURE_HIGH); |
|
1176 } else { |
|
1177 infoObject->SetSecurityState(nsIWebProgressListener::STATE_IS_BROKEN); |
|
1178 } |
|
1179 |
|
1180 // XXX Bug 883674: We shouldn't be formatting messages here in PSM; instead, |
|
1181 // we should set a flag on the channel that higher (UI) level code can check |
|
1182 // to log the warning. In particular, these warnings should go to the web |
|
1183 // console instead of to the error console. Also, the warning is not |
|
1184 // localized. |
|
1185 if (!siteSupportsSafeRenego && |
|
1186 ioLayerHelpers.getWarnLevelMissingRFC5746() > 0) { |
|
1187 nsXPIDLCString hostName; |
|
1188 infoObject->GetHostName(getter_Copies(hostName)); |
|
1189 |
|
1190 nsAutoString msg; |
|
1191 msg.Append(NS_ConvertASCIItoUTF16(hostName)); |
|
1192 msg.Append(NS_LITERAL_STRING(" : server does not support RFC 5746, see CVE-2009-3555")); |
|
1193 |
|
1194 nsContentUtils::LogSimpleConsoleError(msg, "SSL"); |
|
1195 } |
|
1196 |
|
1197 mozilla::pkix::ScopedCERTCertificate serverCert(SSL_PeerCertificate(fd)); |
|
1198 |
|
1199 /* Set the SSL Status information */ |
|
1200 RefPtr<nsSSLStatus> status(infoObject->SSLStatus()); |
|
1201 if (!status) { |
|
1202 status = new nsSSLStatus(); |
|
1203 infoObject->SetSSLStatus(status); |
|
1204 } |
|
1205 |
|
1206 RememberCertErrorsTable::GetInstance().LookupCertErrorBits(infoObject, |
|
1207 status); |
|
1208 |
|
1209 RefPtr<nsNSSCertificate> nssc(nsNSSCertificate::Create(serverCert.get())); |
|
1210 nsCOMPtr<nsIX509Cert> prevcert; |
|
1211 infoObject->GetPreviousCert(getter_AddRefs(prevcert)); |
|
1212 |
|
1213 bool equals_previous = false; |
|
1214 if (prevcert && nssc) { |
|
1215 nsresult rv = nssc->Equals(prevcert, &equals_previous); |
|
1216 if (NS_FAILED(rv)) { |
|
1217 equals_previous = false; |
|
1218 } |
|
1219 } |
|
1220 |
|
1221 if (equals_previous) { |
|
1222 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, |
|
1223 ("HandshakeCallback using PREV cert %p\n", prevcert.get())); |
|
1224 status->mServerCert = prevcert; |
|
1225 } |
|
1226 else { |
|
1227 if (status->mServerCert) { |
|
1228 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, |
|
1229 ("HandshakeCallback KEEPING cert %p\n", status->mServerCert.get())); |
|
1230 } |
|
1231 else { |
|
1232 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, |
|
1233 ("HandshakeCallback using NEW cert %p\n", nssc.get())); |
|
1234 status->mServerCert = nssc; |
|
1235 } |
|
1236 } |
|
1237 |
|
1238 SSLChannelInfo channelInfo; |
|
1239 rv = SSL_GetChannelInfo(fd, &channelInfo, sizeof(channelInfo)); |
|
1240 MOZ_ASSERT(rv == SECSuccess); |
|
1241 if (rv == SECSuccess) { |
|
1242 // Get the protocol version for telemetry |
|
1243 // 0=ssl3, 1=tls1, 2=tls1.1, 3=tls1.2 |
|
1244 unsigned int versionEnum = channelInfo.protocolVersion & 0xFF; |
|
1245 Telemetry::Accumulate(Telemetry::SSL_HANDSHAKE_VERSION, versionEnum); |
|
1246 AccumulateCipherSuite( |
|
1247 infoObject->IsFullHandshake() ? Telemetry::SSL_CIPHER_SUITE_FULL |
|
1248 : Telemetry::SSL_CIPHER_SUITE_RESUMED, |
|
1249 channelInfo); |
|
1250 |
|
1251 SSLCipherSuiteInfo cipherInfo; |
|
1252 rv = SSL_GetCipherSuiteInfo(channelInfo.cipherSuite, &cipherInfo, |
|
1253 sizeof cipherInfo); |
|
1254 MOZ_ASSERT(rv == SECSuccess); |
|
1255 if (rv == SECSuccess) { |
|
1256 status->mHaveKeyLengthAndCipher = true; |
|
1257 status->mKeyLength = cipherInfo.symKeyBits; |
|
1258 status->mSecretKeyLength = cipherInfo.effectiveKeyBits; |
|
1259 status->mCipherName.Assign(cipherInfo.cipherSuiteName); |
|
1260 |
|
1261 // keyExchange null=0, rsa=1, dh=2, fortezza=3, ecdh=4 |
|
1262 Telemetry::Accumulate( |
|
1263 infoObject->IsFullHandshake() |
|
1264 ? Telemetry::SSL_KEY_EXCHANGE_ALGORITHM_FULL |
|
1265 : Telemetry::SSL_KEY_EXCHANGE_ALGORITHM_RESUMED, |
|
1266 cipherInfo.keaType); |
|
1267 infoObject->SetKEAUsed(cipherInfo.keaType); |
|
1268 |
|
1269 if (infoObject->IsFullHandshake()) { |
|
1270 switch (cipherInfo.keaType) { |
|
1271 case ssl_kea_rsa: |
|
1272 AccumulateNonECCKeySize(Telemetry::SSL_KEA_RSA_KEY_SIZE_FULL, |
|
1273 channelInfo.keaKeyBits); |
|
1274 break; |
|
1275 case ssl_kea_dh: |
|
1276 AccumulateNonECCKeySize(Telemetry::SSL_KEA_DHE_KEY_SIZE_FULL, |
|
1277 channelInfo.keaKeyBits); |
|
1278 break; |
|
1279 case ssl_kea_ecdh: |
|
1280 AccumulateECCCurve(Telemetry::SSL_KEA_ECDHE_CURVE_FULL, |
|
1281 channelInfo.keaKeyBits); |
|
1282 break; |
|
1283 default: |
|
1284 MOZ_CRASH("impossible KEA"); |
|
1285 break; |
|
1286 } |
|
1287 |
|
1288 Telemetry::Accumulate(Telemetry::SSL_AUTH_ALGORITHM_FULL, |
|
1289 cipherInfo.authAlgorithm); |
|
1290 |
|
1291 // RSA key exchange doesn't use a signature for auth. |
|
1292 if (cipherInfo.keaType != ssl_kea_rsa) { |
|
1293 switch (cipherInfo.authAlgorithm) { |
|
1294 case ssl_auth_rsa: |
|
1295 AccumulateNonECCKeySize(Telemetry::SSL_AUTH_RSA_KEY_SIZE_FULL, |
|
1296 channelInfo.authKeyBits); |
|
1297 break; |
|
1298 case ssl_auth_dsa: |
|
1299 AccumulateNonECCKeySize(Telemetry::SSL_AUTH_DSA_KEY_SIZE_FULL, |
|
1300 channelInfo.authKeyBits); |
|
1301 break; |
|
1302 case ssl_auth_ecdsa: |
|
1303 AccumulateECCCurve(Telemetry::SSL_AUTH_ECDSA_CURVE_FULL, |
|
1304 channelInfo.authKeyBits); |
|
1305 break; |
|
1306 default: |
|
1307 MOZ_CRASH("impossible auth algorithm"); |
|
1308 break; |
|
1309 } |
|
1310 } |
|
1311 } |
|
1312 |
|
1313 Telemetry::Accumulate( |
|
1314 infoObject->IsFullHandshake() |
|
1315 ? Telemetry::SSL_SYMMETRIC_CIPHER_FULL |
|
1316 : Telemetry::SSL_SYMMETRIC_CIPHER_RESUMED, |
|
1317 cipherInfo.symCipher); |
|
1318 } |
|
1319 } |
|
1320 |
|
1321 infoObject->NoteTimeUntilReady(); |
|
1322 infoObject->SetHandshakeCompleted(); |
|
1323 } |