security/manager/ssl/src/nsNSSCallbacks.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial