Wed, 31 Dec 2014 06:09:35 +0100
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 | } |