PageRenderTime 78ms CodeModel.GetById 10ms app.highlight 56ms RepoModel.GetById 1ms app.codeStats 1ms

/security/manager/ssl/src/nsNSSCallbacks.cpp

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