1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 : *
3 : * ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is mozilla.org code.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * Netscape Communications Corporation.
20 : * Portions created by the Initial Developer are Copyright (C) 1998
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * Brian Ryner <bryner@brianryner.com>
25 : * Terry Hayes <thayes@netscape.com>
26 : * Kai Engert <kengert@redhat.com>
27 : * Petr Kostka <petr.kostka@st.com>
28 : * Honza Bambas <honzab@firemni.cz>
29 : *
30 : * Alternatively, the contents of this file may be used under the terms of
31 : * either the GNU General Public License Version 2 or later (the "GPL"), or
32 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
33 : * in which case the provisions of the GPL or the LGPL are applicable instead
34 : * of those above. If you wish to allow use of your version of this file only
35 : * under the terms of either the GPL or the LGPL, and not to allow others to
36 : * use your version of this file under the terms of the MPL, indicate your
37 : * decision by deleting the provisions above and replace them with the notice
38 : * and other provisions required by the GPL or the LGPL. If you do not delete
39 : * the provisions above, a recipient may use your version of this file under
40 : * the terms of any one of the MPL, the GPL or the LGPL.
41 : *
42 : * ***** END LICENSE BLOCK ***** */
43 : #include "nsNSSComponent.h"
44 : #include "nsNSSCallbacks.h"
45 : #include "nsNSSIOLayer.h"
46 : #include "nsIWebProgressListener.h"
47 : #include "nsProtectedAuthThread.h"
48 : #include "nsITokenDialogs.h"
49 : #include "nsNSSShutDown.h"
50 : #include "nsIUploadChannel.h"
51 : #include "nsThreadUtils.h"
52 : #include "nsIPrompt.h"
53 : #include "nsProxyRelease.h"
54 : #include "PSMRunnable.h"
55 : #include "nsIConsoleService.h"
56 : #include "nsIHttpChannelInternal.h"
57 : #include "nsCRT.h"
58 :
59 : #include "ssl.h"
60 : #include "ocsp.h"
61 : #include "nssb64.h"
62 :
63 : using namespace mozilla;
64 : using namespace mozilla::psm;
65 :
66 : static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID);
67 :
68 : #ifdef PR_LOGGING
69 : extern PRLogModuleInfo* gPIPNSSLog;
70 : #endif
71 :
72 : class nsHTTPDownloadEvent : public nsRunnable {
73 : public:
74 : nsHTTPDownloadEvent();
75 : ~nsHTTPDownloadEvent();
76 :
77 : NS_IMETHOD Run();
78 :
79 : nsNSSHttpRequestSession *mRequestSession;
80 :
81 : nsCOMPtr<nsHTTPListener> mListener;
82 : bool mResponsibleForDoneSignal;
83 : };
84 :
85 0 : nsHTTPDownloadEvent::nsHTTPDownloadEvent()
86 0 : :mResponsibleForDoneSignal(true)
87 : {
88 0 : }
89 :
90 0 : nsHTTPDownloadEvent::~nsHTTPDownloadEvent()
91 : {
92 0 : if (mResponsibleForDoneSignal && mListener)
93 0 : mListener->send_done_signal();
94 :
95 0 : mRequestSession->Release();
96 0 : }
97 :
98 : NS_IMETHODIMP
99 0 : nsHTTPDownloadEvent::Run()
100 : {
101 0 : if (!mListener)
102 0 : return NS_OK;
103 :
104 : nsresult rv;
105 :
106 0 : nsCOMPtr<nsIIOService> ios = do_GetIOService();
107 0 : NS_ENSURE_STATE(ios);
108 :
109 0 : nsCOMPtr<nsIChannel> chan;
110 0 : ios->NewChannel(mRequestSession->mURL, nsnull, nsnull, getter_AddRefs(chan));
111 0 : NS_ENSURE_STATE(chan);
112 :
113 : // Disabled because it breaks authentication with a proxy, when such proxy
114 : // had been setup, and brings blue UI for EV certs.
115 : // chan->SetLoadFlags(nsIRequest::LOAD_ANONYMOUS);
116 :
117 : // Create a loadgroup for this new channel. This way if the channel
118 : // is redirected, we'll have a way to cancel the resulting channel.
119 0 : nsCOMPtr<nsILoadGroup> lg = do_CreateInstance(NS_LOADGROUP_CONTRACTID);
120 0 : chan->SetLoadGroup(lg);
121 :
122 0 : if (mRequestSession->mHasPostData)
123 : {
124 0 : nsCOMPtr<nsIInputStream> uploadStream;
125 0 : rv = NS_NewPostDataStream(getter_AddRefs(uploadStream),
126 : false,
127 : mRequestSession->mPostData,
128 0 : 0, ios);
129 0 : NS_ENSURE_SUCCESS(rv, rv);
130 :
131 0 : nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(chan));
132 0 : NS_ENSURE_STATE(uploadChannel);
133 :
134 0 : rv = uploadChannel->SetUploadStream(uploadStream,
135 : mRequestSession->mPostContentType,
136 0 : -1);
137 0 : NS_ENSURE_SUCCESS(rv, rv);
138 : }
139 :
140 : // Do not use SPDY for internal security operations. It could result
141 : // in the silent upgrade to ssl, which in turn could require an SSL
142 : // operation to fufill something like a CRL fetch, which is an
143 : // endless loop.
144 0 : nsCOMPtr<nsIHttpChannelInternal> internalChannel = do_QueryInterface(chan);
145 0 : if (internalChannel) {
146 0 : rv = internalChannel->SetAllowSpdy(false);
147 0 : NS_ENSURE_SUCCESS(rv, rv);
148 : }
149 :
150 0 : nsCOMPtr<nsIHttpChannel> hchan = do_QueryInterface(chan);
151 0 : NS_ENSURE_STATE(hchan);
152 :
153 0 : rv = hchan->SetRequestMethod(mRequestSession->mRequestMethod);
154 0 : NS_ENSURE_SUCCESS(rv, rv);
155 :
156 0 : mResponsibleForDoneSignal = false;
157 0 : mListener->mResponsibleForDoneSignal = true;
158 :
159 0 : mListener->mLoadGroup = lg.get();
160 0 : NS_ADDREF(mListener->mLoadGroup);
161 0 : mListener->mLoadGroupOwnerThread = PR_GetCurrentThread();
162 :
163 0 : rv = NS_NewStreamLoader(getter_AddRefs(mListener->mLoader),
164 0 : mListener);
165 :
166 0 : if (NS_SUCCEEDED(rv))
167 0 : rv = hchan->AsyncOpen(mListener->mLoader, nsnull);
168 :
169 0 : if (NS_FAILED(rv)) {
170 0 : mListener->mResponsibleForDoneSignal = false;
171 0 : mResponsibleForDoneSignal = true;
172 :
173 0 : NS_RELEASE(mListener->mLoadGroup);
174 0 : mListener->mLoadGroup = nsnull;
175 0 : mListener->mLoadGroupOwnerThread = nsnull;
176 : }
177 :
178 0 : return NS_OK;
179 : }
180 :
181 0 : struct nsCancelHTTPDownloadEvent : nsRunnable {
182 : nsCOMPtr<nsHTTPListener> mListener;
183 :
184 0 : NS_IMETHOD Run() {
185 0 : mListener->FreeLoadGroup(true);
186 0 : mListener = nsnull;
187 0 : return NS_OK;
188 : }
189 : };
190 :
191 0 : SECStatus nsNSSHttpServerSession::createSessionFcn(const char *host,
192 : PRUint16 portnum,
193 : SEC_HTTP_SERVER_SESSION *pSession)
194 : {
195 0 : if (!host || !pSession)
196 0 : return SECFailure;
197 :
198 0 : nsNSSHttpServerSession *hss = new nsNSSHttpServerSession;
199 0 : if (!hss)
200 0 : return SECFailure;
201 :
202 0 : hss->mHost = host;
203 0 : hss->mPort = portnum;
204 :
205 0 : *pSession = hss;
206 0 : return SECSuccess;
207 : }
208 :
209 0 : SECStatus nsNSSHttpRequestSession::createFcn(SEC_HTTP_SERVER_SESSION session,
210 : const char *http_protocol_variant,
211 : const char *path_and_query_string,
212 : const char *http_request_method,
213 : const PRIntervalTime timeout,
214 : SEC_HTTP_REQUEST_SESSION *pRequest)
215 : {
216 0 : if (!session || !http_protocol_variant || !path_and_query_string ||
217 : !http_request_method || !pRequest)
218 0 : return SECFailure;
219 :
220 0 : nsNSSHttpServerSession* hss = static_cast<nsNSSHttpServerSession*>(session);
221 0 : if (!hss)
222 0 : return SECFailure;
223 :
224 0 : nsNSSHttpRequestSession *rs = new nsNSSHttpRequestSession;
225 0 : if (!rs)
226 0 : return SECFailure;
227 :
228 0 : rs->mTimeoutInterval = timeout;
229 :
230 : // Use a maximum timeout value of 10 seconds because of bug 404059.
231 : // FIXME: Use a better approach once 406120 is ready.
232 0 : PRUint32 maxBug404059Timeout = PR_TicksPerSecond() * 10;
233 0 : if (timeout > maxBug404059Timeout) {
234 0 : rs->mTimeoutInterval = maxBug404059Timeout;
235 : }
236 :
237 0 : rs->mURL.Assign(http_protocol_variant);
238 0 : rs->mURL.AppendLiteral("://");
239 0 : rs->mURL.Append(hss->mHost);
240 0 : rs->mURL.AppendLiteral(":");
241 0 : rs->mURL.AppendInt(hss->mPort);
242 0 : rs->mURL.Append(path_and_query_string);
243 :
244 0 : rs->mRequestMethod = http_request_method;
245 :
246 0 : *pRequest = (void*)rs;
247 0 : return SECSuccess;
248 : }
249 :
250 0 : SECStatus nsNSSHttpRequestSession::setPostDataFcn(const char *http_data,
251 : const PRUint32 http_data_len,
252 : const char *http_content_type)
253 : {
254 0 : mHasPostData = true;
255 0 : mPostData.Assign(http_data, http_data_len);
256 0 : mPostContentType.Assign(http_content_type);
257 :
258 0 : return SECSuccess;
259 : }
260 :
261 0 : SECStatus nsNSSHttpRequestSession::addHeaderFcn(const char *http_header_name,
262 : const char *http_header_value)
263 : {
264 0 : return SECFailure; // not yet implemented
265 :
266 : // All http code needs to be postponed to the UI thread.
267 : // Once this gets implemented, we need to add a string list member to
268 : // nsNSSHttpRequestSession and queue up the headers,
269 : // so they can be added in HandleHTTPDownloadPLEvent.
270 : //
271 : // The header will need to be set using
272 : // mHttpChannel->SetRequestHeader(nsDependentCString(http_header_name),
273 : // nsDependentCString(http_header_value),
274 : // false)));
275 : }
276 :
277 0 : SECStatus nsNSSHttpRequestSession::trySendAndReceiveFcn(PRPollDesc **pPollDesc,
278 : PRUint16 *http_response_code,
279 : const char **http_response_content_type,
280 : const char **http_response_headers,
281 : const char **http_response_data,
282 : PRUint32 *http_response_data_len)
283 : {
284 0 : PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
285 : ("nsNSSHttpRequestSession::trySendAndReceiveFcn to %s\n", mURL.get()));
286 :
287 0 : const int max_retries = 2;
288 0 : int retry_count = 0;
289 0 : bool retryable_error = false;
290 0 : SECStatus result_sec_status = SECFailure;
291 :
292 0 : do
293 : {
294 0 : if (retry_count > 0)
295 : {
296 0 : if (retryable_error)
297 : {
298 0 : PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
299 : ("nsNSSHttpRequestSession::trySendAndReceiveFcn - sleeping and retrying: %d of %d\n",
300 : retry_count, max_retries));
301 : }
302 :
303 0 : PR_Sleep( PR_MillisecondsToInterval(300) * retry_count );
304 : }
305 :
306 0 : ++retry_count;
307 0 : retryable_error = false;
308 :
309 : result_sec_status =
310 : internal_send_receive_attempt(retryable_error, pPollDesc, http_response_code,
311 : http_response_content_type, http_response_headers,
312 0 : http_response_data, http_response_data_len);
313 : }
314 : while (retryable_error &&
315 : retry_count < max_retries);
316 :
317 : #ifdef PR_LOGGING
318 0 : if (retry_count > 1)
319 : {
320 0 : if (retryable_error)
321 0 : PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
322 : ("nsNSSHttpRequestSession::trySendAndReceiveFcn - still failing, giving up...\n"));
323 : else
324 0 : PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
325 : ("nsNSSHttpRequestSession::trySendAndReceiveFcn - success at attempt %d\n",
326 : retry_count));
327 : }
328 : #endif
329 :
330 0 : return result_sec_status;
331 : }
332 :
333 : void
334 0 : nsNSSHttpRequestSession::AddRef()
335 : {
336 0 : NS_AtomicIncrementRefcnt(mRefCount);
337 0 : }
338 :
339 : void
340 0 : nsNSSHttpRequestSession::Release()
341 : {
342 0 : PRInt32 newRefCount = NS_AtomicDecrementRefcnt(mRefCount);
343 0 : if (!newRefCount) {
344 0 : delete this;
345 : }
346 0 : }
347 :
348 : SECStatus
349 0 : nsNSSHttpRequestSession::internal_send_receive_attempt(bool &retryable_error,
350 : PRPollDesc **pPollDesc,
351 : PRUint16 *http_response_code,
352 : const char **http_response_content_type,
353 : const char **http_response_headers,
354 : const char **http_response_data,
355 : PRUint32 *http_response_data_len)
356 : {
357 0 : if (pPollDesc) *pPollDesc = nsnull;
358 0 : if (http_response_code) *http_response_code = 0;
359 0 : if (http_response_content_type) *http_response_content_type = 0;
360 0 : if (http_response_headers) *http_response_headers = 0;
361 0 : if (http_response_data) *http_response_data = 0;
362 :
363 0 : PRUint32 acceptableResultSize = 0;
364 :
365 0 : if (http_response_data_len)
366 : {
367 0 : acceptableResultSize = *http_response_data_len;
368 0 : *http_response_data_len = 0;
369 : }
370 :
371 0 : if (!mListener)
372 0 : return SECFailure;
373 :
374 0 : Mutex& waitLock = mListener->mLock;
375 0 : CondVar& waitCondition = mListener->mCondition;
376 0 : volatile bool &waitFlag = mListener->mWaitFlag;
377 0 : waitFlag = true;
378 :
379 0 : nsRefPtr<nsHTTPDownloadEvent> event = new nsHTTPDownloadEvent;
380 0 : if (!event)
381 0 : return SECFailure;
382 :
383 0 : event->mListener = mListener;
384 0 : this->AddRef();
385 0 : event->mRequestSession = this;
386 :
387 0 : nsresult rv = NS_DispatchToMainThread(event);
388 0 : if (NS_FAILED(rv))
389 : {
390 0 : event->mResponsibleForDoneSignal = false;
391 0 : return SECFailure;
392 : }
393 :
394 0 : bool request_canceled = false;
395 :
396 : {
397 0 : MutexAutoLock locker(waitLock);
398 :
399 0 : const PRIntervalTime start_time = PR_IntervalNow();
400 : PRIntervalTime wait_interval;
401 :
402 0 : bool running_on_main_thread = NS_IsMainThread();
403 0 : if (running_on_main_thread)
404 : {
405 : // let's process events quickly
406 0 : wait_interval = PR_MicrosecondsToInterval(50);
407 : }
408 : else
409 : {
410 : // On a secondary thread, it's fine to wait some more for
411 : // for the condition variable.
412 0 : wait_interval = PR_MillisecondsToInterval(250);
413 : }
414 :
415 0 : while (waitFlag)
416 : {
417 0 : if (running_on_main_thread)
418 : {
419 : // Networking runs on the main thread, which we happen to block here.
420 : // Processing events will allow the OCSP networking to run while we
421 : // are waiting. Thanks a lot to Darin Fisher for rewriting the
422 : // thread manager. Thanks a lot to Christian Biesinger who
423 : // made me aware of this possibility. (kaie)
424 :
425 0 : MutexAutoUnlock unlock(waitLock);
426 0 : NS_ProcessNextEvent(nsnull);
427 : }
428 :
429 0 : waitCondition.Wait(wait_interval);
430 :
431 0 : if (!waitFlag)
432 0 : break;
433 :
434 0 : if (!request_canceled)
435 : {
436 : bool timeout =
437 0 : (PRIntervalTime)(PR_IntervalNow() - start_time) > mTimeoutInterval;
438 :
439 0 : if (timeout)
440 : {
441 0 : request_canceled = true;
442 :
443 0 : nsRefPtr<nsCancelHTTPDownloadEvent> cancelevent = new nsCancelHTTPDownloadEvent;
444 0 : cancelevent->mListener = mListener;
445 0 : rv = NS_DispatchToMainThread(cancelevent);
446 0 : if (NS_FAILED(rv)) {
447 0 : NS_WARNING("cannot post cancel event");
448 : }
449 : break;
450 : }
451 : }
452 : }
453 : }
454 :
455 0 : if (request_canceled)
456 0 : return SECFailure;
457 :
458 0 : if (NS_FAILED(mListener->mResultCode))
459 : {
460 0 : if (mListener->mResultCode == NS_ERROR_CONNECTION_REFUSED
461 : ||
462 0 : mListener->mResultCode == NS_ERROR_NET_RESET)
463 : {
464 0 : retryable_error = true;
465 : }
466 0 : return SECFailure;
467 : }
468 :
469 0 : if (http_response_code)
470 0 : *http_response_code = mListener->mHttpResponseCode;
471 :
472 0 : if (mListener->mHttpRequestSucceeded && http_response_data && http_response_data_len) {
473 :
474 0 : *http_response_data_len = mListener->mResultLen;
475 :
476 : // acceptableResultSize == 0 means: any size is acceptable
477 0 : if (acceptableResultSize != 0
478 : &&
479 0 : acceptableResultSize < mListener->mResultLen)
480 : {
481 0 : return SECFailure;
482 : }
483 :
484 : // return data by reference, result data will be valid
485 : // until "this" gets destroyed by NSS
486 0 : *http_response_data = (const char*)mListener->mResultData;
487 : }
488 :
489 0 : if (mListener->mHttpRequestSucceeded && http_response_content_type) {
490 0 : if (mListener->mHttpResponseContentType.Length()) {
491 0 : *http_response_content_type = mListener->mHttpResponseContentType.get();
492 : }
493 : }
494 :
495 0 : return SECSuccess;
496 : }
497 :
498 0 : SECStatus nsNSSHttpRequestSession::cancelFcn()
499 : {
500 : // As of today, only the blocking variant of the http interface
501 : // has been implemented. Implementing cancelFcn will be necessary
502 : // as soon as we implement the nonblocking variant.
503 0 : return SECSuccess;
504 : }
505 :
506 0 : SECStatus nsNSSHttpRequestSession::freeFcn()
507 : {
508 0 : Release();
509 0 : return SECSuccess;
510 : }
511 :
512 0 : nsNSSHttpRequestSession::nsNSSHttpRequestSession()
513 : : mRefCount(1),
514 : mHasPostData(false),
515 : mTimeoutInterval(0),
516 0 : mListener(new nsHTTPListener)
517 : {
518 0 : }
519 :
520 0 : nsNSSHttpRequestSession::~nsNSSHttpRequestSession()
521 : {
522 0 : }
523 :
524 : SEC_HttpClientFcn nsNSSHttpInterface::sNSSInterfaceTable;
525 :
526 328 : void nsNSSHttpInterface::initTable()
527 : {
528 328 : sNSSInterfaceTable.version = 1;
529 328 : SEC_HttpClientFcnV1 &v1 = sNSSInterfaceTable.fcnTable.ftable1;
530 328 : v1.createSessionFcn = createSessionFcn;
531 328 : v1.keepAliveSessionFcn = keepAliveFcn;
532 328 : v1.freeSessionFcn = freeSessionFcn;
533 328 : v1.createFcn = createFcn;
534 328 : v1.setPostDataFcn = setPostDataFcn;
535 328 : v1.addHeaderFcn = addHeaderFcn;
536 328 : v1.trySendAndReceiveFcn = trySendAndReceiveFcn;
537 328 : v1.cancelFcn = cancelFcn;
538 328 : v1.freeFcn = freeFcn;
539 328 : }
540 :
541 328 : void nsNSSHttpInterface::registerHttpClient()
542 : {
543 328 : SEC_RegisterDefaultHttpClient(&sNSSInterfaceTable);
544 328 : }
545 :
546 328 : void nsNSSHttpInterface::unregisterHttpClient()
547 : {
548 328 : SEC_RegisterDefaultHttpClient(nsnull);
549 328 : }
550 :
551 0 : nsHTTPListener::nsHTTPListener()
552 : : mResultData(nsnull),
553 : mResultLen(0),
554 : mLock("nsHTTPListener.mLock"),
555 : mCondition(mLock, "nsHTTPListener.mCondition"),
556 : mWaitFlag(true),
557 : mResponsibleForDoneSignal(false),
558 : mLoadGroup(nsnull),
559 0 : mLoadGroupOwnerThread(nsnull)
560 : {
561 0 : }
562 :
563 0 : nsHTTPListener::~nsHTTPListener()
564 : {
565 0 : if (mResponsibleForDoneSignal)
566 0 : send_done_signal();
567 :
568 0 : if (mLoader) {
569 0 : nsCOMPtr<nsIThread> mainThread(do_GetMainThread());
570 0 : NS_ProxyRelease(mainThread, mLoader);
571 : }
572 0 : }
573 :
574 0 : NS_IMPL_THREADSAFE_ISUPPORTS1(nsHTTPListener, nsIStreamLoaderObserver)
575 :
576 : void
577 0 : nsHTTPListener::FreeLoadGroup(bool aCancelLoad)
578 : {
579 0 : nsILoadGroup *lg = nsnull;
580 :
581 0 : MutexAutoLock locker(mLock);
582 :
583 0 : if (mLoadGroup) {
584 0 : if (mLoadGroupOwnerThread != PR_GetCurrentThread()) {
585 0 : NS_ASSERTION(false,
586 : "attempt to access nsHTTPDownloadEvent::mLoadGroup on multiple threads, leaking it!");
587 : }
588 : else {
589 0 : lg = mLoadGroup;
590 0 : mLoadGroup = nsnull;
591 : }
592 : }
593 :
594 0 : if (lg) {
595 0 : if (aCancelLoad) {
596 0 : lg->Cancel(NS_ERROR_ABORT);
597 : }
598 0 : NS_RELEASE(lg);
599 : }
600 0 : }
601 :
602 : NS_IMETHODIMP
603 0 : nsHTTPListener::OnStreamComplete(nsIStreamLoader* aLoader,
604 : nsISupports* aContext,
605 : nsresult aStatus,
606 : PRUint32 stringLen,
607 : const PRUint8* string)
608 : {
609 0 : mResultCode = aStatus;
610 :
611 0 : FreeLoadGroup(false);
612 :
613 0 : nsCOMPtr<nsIRequest> req;
614 0 : nsCOMPtr<nsIHttpChannel> hchan;
615 :
616 0 : nsresult rv = aLoader->GetRequest(getter_AddRefs(req));
617 :
618 : #ifdef PR_LOGGING
619 0 : if (NS_FAILED(aStatus))
620 : {
621 0 : PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
622 : ("nsHTTPListener::OnStreamComplete status failed %d", aStatus));
623 : }
624 : #endif
625 :
626 0 : if (NS_SUCCEEDED(rv))
627 0 : hchan = do_QueryInterface(req, &rv);
628 :
629 0 : if (NS_SUCCEEDED(rv))
630 : {
631 0 : rv = hchan->GetRequestSucceeded(&mHttpRequestSucceeded);
632 0 : if (NS_FAILED(rv))
633 0 : mHttpRequestSucceeded = false;
634 :
635 0 : mResultLen = stringLen;
636 0 : mResultData = string; // reference. Make sure loader lives as long as this
637 :
638 : unsigned int rcode;
639 0 : rv = hchan->GetResponseStatus(&rcode);
640 0 : if (NS_FAILED(rv))
641 0 : mHttpResponseCode = 500;
642 : else
643 0 : mHttpResponseCode = rcode;
644 :
645 0 : hchan->GetResponseHeader(NS_LITERAL_CSTRING("Content-Type"),
646 0 : mHttpResponseContentType);
647 : }
648 :
649 0 : if (mResponsibleForDoneSignal)
650 0 : send_done_signal();
651 :
652 0 : return aStatus;
653 : }
654 :
655 0 : void nsHTTPListener::send_done_signal()
656 : {
657 0 : mResponsibleForDoneSignal = false;
658 :
659 : {
660 0 : MutexAutoLock locker(mLock);
661 0 : mWaitFlag = false;
662 0 : mCondition.NotifyAll();
663 : }
664 0 : }
665 :
666 : static char*
667 0 : ShowProtectedAuthPrompt(PK11SlotInfo* slot, nsIInterfaceRequestor *ir)
668 : {
669 0 : if (!NS_IsMainThread()) {
670 0 : NS_ERROR("ShowProtectedAuthPrompt called off the main thread");
671 0 : return nsnull;
672 : }
673 :
674 0 : char* protAuthRetVal = nsnull;
675 :
676 : // Get protected auth dialogs
677 0 : nsITokenDialogs* dialogs = 0;
678 : nsresult nsrv = getNSSDialogs((void**)&dialogs,
679 : NS_GET_IID(nsITokenDialogs),
680 0 : NS_TOKENDIALOGS_CONTRACTID);
681 0 : if (NS_SUCCEEDED(nsrv))
682 : {
683 0 : nsProtectedAuthThread* protectedAuthRunnable = new nsProtectedAuthThread();
684 0 : if (protectedAuthRunnable)
685 : {
686 0 : NS_ADDREF(protectedAuthRunnable);
687 :
688 0 : protectedAuthRunnable->SetParams(slot);
689 :
690 0 : nsCOMPtr<nsIProtectedAuthThread> runnable = do_QueryInterface(protectedAuthRunnable);
691 0 : if (runnable)
692 : {
693 0 : nsrv = dialogs->DisplayProtectedAuth(ir, runnable);
694 :
695 : // We call join on the thread,
696 : // so we can be sure that no simultaneous access will happen.
697 0 : protectedAuthRunnable->Join();
698 :
699 0 : if (NS_SUCCEEDED(nsrv))
700 : {
701 0 : SECStatus rv = protectedAuthRunnable->GetResult();
702 0 : switch (rv)
703 : {
704 : case SECSuccess:
705 0 : protAuthRetVal = ToNewCString(nsDependentCString(PK11_PW_AUTHENTICATED));
706 0 : break;
707 : case SECWouldBlock:
708 0 : protAuthRetVal = ToNewCString(nsDependentCString(PK11_PW_RETRY));
709 0 : break;
710 : default:
711 0 : protAuthRetVal = nsnull;
712 0 : break;
713 :
714 : }
715 : }
716 : }
717 :
718 0 : NS_RELEASE(protectedAuthRunnable);
719 : }
720 :
721 0 : NS_RELEASE(dialogs);
722 : }
723 :
724 0 : return protAuthRetVal;
725 : }
726 :
727 : class PK11PasswordPromptRunnable : public SyncRunnableBase
728 0 : {
729 : public:
730 0 : PK11PasswordPromptRunnable(PK11SlotInfo* slot,
731 : nsIInterfaceRequestor* ir)
732 : : mResult(nsnull),
733 : mSlot(slot),
734 0 : mIR(ir)
735 : {
736 0 : }
737 : char * mResult; // out
738 : virtual void RunOnTargetThread();
739 : private:
740 : PK11SlotInfo* const mSlot; // in
741 : nsIInterfaceRequestor* const mIR; // in
742 : };
743 :
744 0 : void PK11PasswordPromptRunnable::RunOnTargetThread()
745 : {
746 0 : nsNSSShutDownPreventionLock locker;
747 0 : nsresult rv = NS_OK;
748 0 : PRUnichar *password = nsnull;
749 0 : bool value = false;
750 0 : nsCOMPtr<nsIPrompt> prompt;
751 :
752 : /* TODO: Retry should generate a different dialog message */
753 : /*
754 : if (retry)
755 : return nsnull;
756 : */
757 :
758 0 : if (!mIR)
759 : {
760 0 : nsNSSComponent::GetNewPrompter(getter_AddRefs(prompt));
761 : }
762 : else
763 : {
764 0 : prompt = do_GetInterface(mIR);
765 0 : NS_ASSERTION(prompt != nsnull, "callbacks does not implement nsIPrompt");
766 : }
767 :
768 0 : if (!prompt)
769 : return;
770 :
771 0 : if (PK11_ProtectedAuthenticationPath(mSlot)) {
772 0 : mResult = ShowProtectedAuthPrompt(mSlot, mIR);
773 : return;
774 : }
775 :
776 0 : nsAutoString promptString;
777 0 : nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(kNSSComponentCID, &rv));
778 :
779 0 : if (NS_FAILED(rv))
780 : return;
781 :
782 : const PRUnichar* formatStrings[1] = {
783 0 : ToNewUnicode(NS_ConvertUTF8toUTF16(PK11_GetTokenName(mSlot)))
784 0 : };
785 0 : rv = nssComponent->PIPBundleFormatStringFromName("CertPassPrompt",
786 : formatStrings, 1,
787 0 : promptString);
788 0 : nsMemory::Free(const_cast<PRUnichar*>(formatStrings[0]));
789 :
790 0 : if (NS_FAILED(rv))
791 : return;
792 :
793 : {
794 0 : nsPSMUITracker tracker;
795 0 : if (tracker.isUIForbidden()) {
796 0 : rv = NS_ERROR_NOT_AVAILABLE;
797 : }
798 : else {
799 : // Although the exact value is ignored, we must not pass invalid
800 : // bool values through XPConnect.
801 0 : bool checkState = false;
802 0 : rv = prompt->PromptPassword(nsnull, promptString.get(),
803 0 : &password, nsnull, &checkState, &value);
804 : }
805 : }
806 :
807 0 : if (NS_SUCCEEDED(rv) && value) {
808 0 : mResult = ToNewUTF8String(nsDependentString(password));
809 0 : NS_Free(password);
810 : }
811 : }
812 :
813 : char* PR_CALLBACK
814 0 : PK11PasswordPrompt(PK11SlotInfo* slot, PRBool retry, void* arg)
815 : {
816 : nsRefPtr<PK11PasswordPromptRunnable> runnable =
817 : new PK11PasswordPromptRunnable(slot,
818 0 : static_cast<nsIInterfaceRequestor*>(arg));
819 0 : runnable->DispatchToMainThreadAndWait();
820 0 : return runnable->mResult;
821 : }
822 :
823 4 : void PR_CALLBACK HandshakeCallback(PRFileDesc* fd, void* client_data) {
824 8 : nsNSSShutDownPreventionLock locker;
825 : PRInt32 sslStatus;
826 4 : char* signer = nsnull;
827 4 : char* cipherName = nsnull;
828 : PRInt32 keyLength;
829 : nsresult rv;
830 : PRInt32 encryptBits;
831 :
832 4 : nsNSSSocketInfo* infoObject = (nsNSSSocketInfo*) fd->higher->secret;
833 :
834 : // If the handshake completed, then we know the site is TLS tolerant (if this
835 : // was a TLS connection).
836 4 : nsSSLIOLayerHelpers::rememberTolerantSite(infoObject);
837 :
838 4 : if (SECSuccess != SSL_SecurityStatus(fd, &sslStatus, &cipherName, &keyLength,
839 4 : &encryptBits, &signer, nsnull)) {
840 : return;
841 : }
842 :
843 : PRInt32 secStatus;
844 4 : if (sslStatus == SSL_SECURITY_STATUS_OFF)
845 0 : secStatus = nsIWebProgressListener::STATE_IS_BROKEN;
846 4 : else if (encryptBits >= 90)
847 : secStatus = (nsIWebProgressListener::STATE_IS_SECURE |
848 4 : nsIWebProgressListener::STATE_SECURE_HIGH);
849 : else
850 : secStatus = (nsIWebProgressListener::STATE_IS_SECURE |
851 0 : nsIWebProgressListener::STATE_SECURE_LOW);
852 :
853 : PRBool siteSupportsSafeRenego;
854 8 : if (SSL_HandshakeNegotiatedExtension(fd, ssl_renegotiation_info_xtn, &siteSupportsSafeRenego) != SECSuccess
855 4 : || !siteSupportsSafeRenego) {
856 :
857 0 : bool wantWarning = (nsSSLIOLayerHelpers::getWarnLevelMissingRFC5746() > 0);
858 :
859 0 : nsCOMPtr<nsIConsoleService> console;
860 0 : if (infoObject && wantWarning) {
861 0 : console = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
862 0 : if (console) {
863 0 : nsXPIDLCString hostName;
864 0 : infoObject->GetHostName(getter_Copies(hostName));
865 :
866 0 : nsAutoString msg;
867 0 : msg.Append(NS_ConvertASCIItoUTF16(hostName));
868 0 : msg.Append(NS_LITERAL_STRING(" : server does not support RFC 5746, see CVE-2009-3555"));
869 :
870 0 : console->LogStringMessage(msg.get());
871 : }
872 : }
873 0 : if (nsSSLIOLayerHelpers::treatUnsafeNegotiationAsBroken()) {
874 0 : secStatus = nsIWebProgressListener::STATE_IS_BROKEN;
875 : }
876 : }
877 :
878 :
879 4 : CERTCertificate *peerCert = SSL_PeerCertificate(fd);
880 4 : const char* caName = nsnull; // caName is a pointer only, no ownership
881 4 : char* certOrgName = CERT_GetOrgName(&peerCert->issuer);
882 4 : CERT_DestroyCertificate(peerCert);
883 4 : caName = certOrgName ? certOrgName : signer;
884 :
885 4 : const char* verisignName = "Verisign, Inc.";
886 : // If the CA name is RSA Data Security, then change the name to the real
887 : // name of the company i.e. VeriSign, Inc.
888 4 : if (nsCRT::strcmp((const char*)caName, "RSA Data Security, Inc.") == 0) {
889 0 : caName = verisignName;
890 : }
891 :
892 8 : nsAutoString shortDesc;
893 4 : const PRUnichar* formatStrings[1] = { ToNewUnicode(NS_ConvertUTF8toUTF16(caName)) };
894 8 : nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(kNSSComponentCID, &rv));
895 4 : if (NS_SUCCEEDED(rv)) {
896 4 : rv = nssComponent->PIPBundleFormatStringFromName("SignedBy",
897 : formatStrings, 1,
898 4 : shortDesc);
899 :
900 4 : nsMemory::Free(const_cast<PRUnichar*>(formatStrings[0]));
901 :
902 4 : nsNSSSocketInfo* infoObject = (nsNSSSocketInfo*) fd->higher->secret;
903 4 : infoObject->SetSecurityState(secStatus);
904 4 : infoObject->SetShortSecurityDescription(shortDesc.get());
905 :
906 : /* Set the SSL Status information */
907 8 : nsRefPtr<nsSSLStatus> status = infoObject->SSLStatus();
908 4 : if (!status) {
909 0 : status = new nsSSLStatus();
910 0 : infoObject->SetSSLStatus(status);
911 : }
912 :
913 : nsSSLIOLayerHelpers::mHostsWithCertErrors->LookupCertErrorBits(
914 4 : infoObject, status);
915 :
916 4 : CERTCertificate *serverCert = SSL_PeerCertificate(fd);
917 4 : if (serverCert) {
918 8 : nsRefPtr<nsNSSCertificate> nssc = nsNSSCertificate::Create(serverCert);
919 4 : CERT_DestroyCertificate(serverCert);
920 4 : serverCert = nsnull;
921 :
922 8 : nsCOMPtr<nsIX509Cert> prevcert;
923 4 : infoObject->GetPreviousCert(getter_AddRefs(prevcert));
924 :
925 4 : bool equals_previous = false;
926 4 : if (prevcert && nssc) {
927 0 : nsresult rv = nssc->Equals(prevcert, &equals_previous);
928 0 : if (NS_FAILED(rv)) {
929 0 : equals_previous = false;
930 : }
931 : }
932 :
933 4 : if (equals_previous) {
934 0 : PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
935 : ("HandshakeCallback using PREV cert %p\n", prevcert.get()));
936 0 : status->mServerCert = prevcert;
937 : }
938 : else {
939 4 : if (status->mServerCert) {
940 4 : PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
941 : ("HandshakeCallback KEEPING cert %p\n", status->mServerCert.get()));
942 : }
943 : else {
944 0 : PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
945 : ("HandshakeCallback using NEW cert %p\n", nssc.get()));
946 0 : status->mServerCert = nssc;
947 : }
948 : }
949 : }
950 :
951 4 : status->mHaveKeyLengthAndCipher = true;
952 4 : status->mKeyLength = keyLength;
953 4 : status->mSecretKeyLength = encryptBits;
954 4 : status->mCipherName.Assign(cipherName);
955 :
956 : // Get the NPN value. Do this on the stack and copy it into
957 : // a string rather than preallocating the string because right
958 : // now we expect NPN to fail more often than it succeeds.
959 : SSLNextProtoState state;
960 : unsigned char npnbuf[256];
961 : unsigned int npnlen;
962 :
963 4 : if (SSL_GetNextProto(fd, &state, npnbuf, &npnlen, 256) == SECSuccess &&
964 : state == SSL_NEXT_PROTO_NEGOTIATED)
965 0 : infoObject->SetNegotiatedNPN(reinterpret_cast<char *>(npnbuf), npnlen);
966 : else
967 4 : infoObject->SetNegotiatedNPN(nsnull, 0);
968 :
969 4 : infoObject->SetHandshakeCompleted();
970 : }
971 :
972 4 : PORT_Free(cipherName);
973 4 : PR_FREEIF(certOrgName);
974 4 : PR_Free(signer);
975 : }
976 :
977 : struct OCSPDefaultResponders {
978 : const char *issuerName_string;
979 : CERTName *issuerName;
980 : const char *issuerKeyID_base64;
981 : SECItem *issuerKeyID;
982 : const char *ocspUrl;
983 : };
984 :
985 : static struct OCSPDefaultResponders myDefaultOCSPResponders[] = {
986 : /* COMODO */
987 : {
988 : "CN=AddTrust External CA Root,OU=AddTrust External TTP Network,O=AddTrust AB,C=SE",
989 : nsnull, "rb2YejS0Jvf6xCZU7wO94CTLVBo=", nsnull,
990 : "http://ocsp.comodoca.com"
991 : },
992 : {
993 : "CN=COMODO Certification Authority,O=COMODO CA Limited,L=Salford,ST=Greater Manchester,C=GB",
994 : nsnull, "C1jli8ZMFTekQKkwqSG+RzZaVv8=", nsnull,
995 : "http://ocsp.comodoca.com"
996 : },
997 : {
998 : "CN=COMODO EV SGC CA,O=COMODO CA Limited,L=Salford,ST=Greater Manchester,C=GB",
999 : nsnull, "f/ZMNigUrs0eN6/eWvJbw6CsK/4=", nsnull,
1000 : "http://ocsp.comodoca.com"
1001 : },
1002 : {
1003 : "CN=COMODO EV SSL CA,O=COMODO CA Limited,L=Salford,ST=Greater Manchester,C=GB",
1004 : nsnull, "aRZJ7LZ1ZFrpAyNgL1RipTRcPuI=", nsnull,
1005 : "http://ocsp.comodoca.com"
1006 : },
1007 : {
1008 : "CN=UTN - DATACorp SGC,OU=http://www.usertrust.com,O=The USERTRUST Network,L=Salt Lake City,ST=UT,C=US",
1009 : nsnull, "UzLRs89/+uDxoF2FTpLSnkUdtE8=", nsnull,
1010 : "http://ocsp.usertrust.com"
1011 : },
1012 : {
1013 : "CN=UTN-USERFirst-Hardware,OU=http://www.usertrust.com,O=The USERTRUST Network,L=Salt Lake City,ST=UT,C=US",
1014 : nsnull, "oXJfJhsomEOVXQc31YWWnUvSw0U=", nsnull,
1015 : "http://ocsp.usertrust.com"
1016 : },
1017 : /* Network Solutions */
1018 : {
1019 : "CN=Network Solutions Certificate Authority,O=Network Solutions L.L.C.,C=US",
1020 : nsnull, "ITDJ+wDXTpjah6oq0KcusUAxp0w=", nsnull,
1021 : "http://ocsp.netsolssl.com"
1022 : },
1023 : {
1024 : "CN=Network Solutions EV SSL CA,O=Network Solutions L.L.C.,C=US",
1025 : nsnull, "tk6FnYQfGx3UUolOB5Yt+d7xj8w=", nsnull,
1026 : "http://ocsp.netsolssl.com"
1027 : },
1028 : /* GlobalSign */
1029 : {
1030 : "CN=GlobalSign Root CA,OU=Root CA,O=GlobalSign nv-sa,C=BE",
1031 : nsnull, "YHtmGkUNl8qJUC99BM00qP/8/Us=", nsnull,
1032 : "http://ocsp.globalsign.com/ExtendedSSLCACross"
1033 : },
1034 : {
1035 : "CN=GlobalSign,O=GlobalSign,OU=GlobalSign Root CA - R2",
1036 : nsnull, "m+IHV2ccHsBqBt5ZtJot39wZhi4=", nsnull,
1037 : "http://ocsp.globalsign.com/ExtendedSSLCA"
1038 : },
1039 : {
1040 : "CN=GlobalSign Extended Validation CA,O=GlobalSign,OU=Extended Validation CA",
1041 : nsnull, "NLH5yYxrNUTMCGkK7uOjuVy/FuA=", nsnull,
1042 : "http://ocsp.globalsign.com/ExtendedSSL"
1043 : },
1044 : /* Trustwave */
1045 : {
1046 : "CN=SecureTrust CA,O=SecureTrust Corporation,C=US",
1047 : nsnull, "QjK2FvoE/f5dS3rD/fdMQB1aQ68=", nsnull,
1048 : "http://ocsp.trustwave.com"
1049 : }
1050 : };
1051 :
1052 : static const unsigned int numResponders =
1053 : (sizeof myDefaultOCSPResponders) / (sizeof myDefaultOCSPResponders[0]);
1054 :
1055 : static CERT_StringFromCertFcn oldOCSPAIAInfoCallback = nsnull;
1056 :
1057 : /*
1058 : * See if we have a hard-coded default responder for this certificate's
1059 : * issuer (unless this certificate is a root certificate).
1060 : *
1061 : * The result needs to be freed (PORT_Free) when no longer in use.
1062 : */
1063 4 : char* PR_CALLBACK MyAlternateOCSPAIAInfoCallback(CERTCertificate *cert) {
1064 4 : if (cert && !cert->isRoot) {
1065 : unsigned int i;
1066 52 : for (i=0; i < numResponders; i++) {
1067 48 : if (!(myDefaultOCSPResponders[i].issuerName));
1068 48 : else if (!(myDefaultOCSPResponders[i].issuerKeyID));
1069 48 : else if (!(cert->authKeyID));
1070 48 : else if (CERT_CompareName(myDefaultOCSPResponders[i].issuerName,
1071 48 : &(cert->issuer)) != SECEqual);
1072 0 : else if (SECITEM_CompareItem(myDefaultOCSPResponders[i].issuerKeyID,
1073 0 : &(cert->authKeyID->keyID)) != SECEqual);
1074 : else // Issuer Name and Key Identifier match, so use this OCSP URL.
1075 0 : return PORT_Strdup(myDefaultOCSPResponders[i].ocspUrl);
1076 : }
1077 : }
1078 :
1079 : // If we've not found a hard-coded default responder, chain to the old
1080 : // callback function (if there is one).
1081 4 : if (oldOCSPAIAInfoCallback)
1082 0 : return (*oldOCSPAIAInfoCallback)(cert);
1083 :
1084 4 : return nsnull;
1085 : }
1086 :
1087 328 : void cleanUpMyDefaultOCSPResponders() {
1088 : unsigned int i;
1089 :
1090 4264 : for (i=0; i < numResponders; i++) {
1091 3936 : if (myDefaultOCSPResponders[i].issuerName) {
1092 3936 : CERT_DestroyName(myDefaultOCSPResponders[i].issuerName);
1093 3936 : myDefaultOCSPResponders[i].issuerName = nsnull;
1094 : }
1095 3936 : if (myDefaultOCSPResponders[i].issuerKeyID) {
1096 3936 : SECITEM_FreeItem(myDefaultOCSPResponders[i].issuerKeyID, true);
1097 3936 : myDefaultOCSPResponders[i].issuerKeyID = nsnull;
1098 : }
1099 : }
1100 328 : }
1101 :
1102 328 : SECStatus RegisterMyOCSPAIAInfoCallback() {
1103 : // Prevent multiple registrations.
1104 328 : if (myDefaultOCSPResponders[0].issuerName)
1105 0 : return SECSuccess; // Already registered ok.
1106 :
1107 : // Populate various fields in the myDefaultOCSPResponders[] array.
1108 328 : SECStatus rv = SECFailure;
1109 : unsigned int i;
1110 4264 : for (i=0; i < numResponders; i++) {
1111 : // Create a CERTName structure from the issuer name string.
1112 : myDefaultOCSPResponders[i].issuerName = CERT_AsciiToName(
1113 3936 : const_cast<char*>(myDefaultOCSPResponders[i].issuerName_string));
1114 3936 : if (!(myDefaultOCSPResponders[i].issuerName))
1115 0 : goto loser;
1116 : // Create a SECItem from the Base64 authority key identifier keyID.
1117 : myDefaultOCSPResponders[i].issuerKeyID = NSSBase64_DecodeBuffer(nsnull,
1118 : nsnull, myDefaultOCSPResponders[i].issuerKeyID_base64,
1119 3936 : (PRUint32)PORT_Strlen(myDefaultOCSPResponders[i].issuerKeyID_base64));
1120 3936 : if (!(myDefaultOCSPResponders[i].issuerKeyID))
1121 0 : goto loser;
1122 : }
1123 :
1124 : // Register our alternate OCSP Responder URL lookup function.
1125 : rv = CERT_RegisterAlternateOCSPAIAInfoCallBack(MyAlternateOCSPAIAInfoCallback,
1126 328 : &oldOCSPAIAInfoCallback);
1127 328 : if (rv != SECSuccess)
1128 0 : goto loser;
1129 :
1130 328 : return SECSuccess;
1131 :
1132 : loser:
1133 0 : cleanUpMyDefaultOCSPResponders();
1134 0 : return rv;
1135 : }
1136 :
1137 328 : SECStatus UnregisterMyOCSPAIAInfoCallback() {
1138 : SECStatus rv;
1139 :
1140 : // Only allow unregistration if we're already registered.
1141 328 : if (!(myDefaultOCSPResponders[0].issuerName))
1142 0 : return SECFailure;
1143 :
1144 : // Unregister our alternate OCSP Responder URL lookup function.
1145 : rv = CERT_RegisterAlternateOCSPAIAInfoCallBack(oldOCSPAIAInfoCallback,
1146 328 : nsnull);
1147 328 : if (rv != SECSuccess)
1148 0 : return rv;
1149 :
1150 : // Tidy up.
1151 328 : oldOCSPAIAInfoCallback = nsnull;
1152 328 : cleanUpMyDefaultOCSPResponders();
1153 328 : return SECSuccess;
1154 : }
|