/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Brian Ryner * Terry Hayes * Kai Engert * Petr Kostka * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "nsNSSComponent.h" // for PIPNSS string bundle calls. #include "nsNSSCallbacks.h" #include "nsNSSCertificate.h" #include "nsNSSCleaner.h" #include "nsSSLStatus.h" #include "nsNSSIOLayer.h" // for nsNSSSocketInfo #include "nsIWebProgressListener.h" #include "nsIStringBundle.h" #include "nsXPIDLString.h" #include "nsCOMPtr.h" #include "nsAutoPtr.h" #include "nsIServiceManager.h" #include "nsReadableUtils.h" #include "nsIPrompt.h" #include "nsProxiedService.h" #include "nsIInterfaceRequestor.h" #include "nsIInterfaceRequestorUtils.h" #include "nsProtectedAuthThread.h" #include "nsITokenDialogs.h" #include "nsCRT.h" #include "nsNSSShutDown.h" #include "nsIUploadChannel.h" #include "nsSSLThread.h" #include "nsThreadUtils.h" #include "nsAutoLock.h" #include "nsIThread.h" #include "nsIWindowWatcher.h" #include "nsIPrompt.h" #include "nsProxyRelease.h" #include "ssl.h" #include "cert.h" #include "ocsp.h" static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID); NSSCleanupAutoPtrClass(CERTCertificate, CERT_DestroyCertificate) #ifdef PR_LOGGING extern PRLogModuleInfo* gPIPNSSLog; #endif class nsHTTPDownloadEvent : public nsRunnable { public: nsHTTPDownloadEvent(); ~nsHTTPDownloadEvent(); NS_IMETHOD Run(); nsNSSHttpRequestSession *mRequestSession; nsCOMPtr mListener; PRBool mResponsibleForDoneSignal; }; nsHTTPDownloadEvent::nsHTTPDownloadEvent() :mResponsibleForDoneSignal(PR_TRUE) { } nsHTTPDownloadEvent::~nsHTTPDownloadEvent() { if (mResponsibleForDoneSignal && mListener) mListener->send_done_signal(); mRequestSession->Release(); } NS_IMETHODIMP nsHTTPDownloadEvent::Run() { if (!mListener) return NS_OK; nsresult rv; nsCOMPtr ios = do_GetIOService(); NS_ENSURE_STATE(ios); nsCOMPtr chan; ios->NewChannel(mRequestSession->mURL, nsnull, nsnull, getter_AddRefs(chan)); NS_ENSURE_STATE(chan); // Create a loadgroup for this new channel. This way if the channel // is redirected, we'll have a way to cancel the resulting channel. nsCOMPtr lg = do_CreateInstance(NS_LOADGROUP_CONTRACTID); chan->SetLoadGroup(lg); if (mRequestSession->mHasPostData) { nsCOMPtr uploadStream; rv = NS_NewPostDataStream(getter_AddRefs(uploadStream), PR_FALSE, mRequestSession->mPostData, 0, ios); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr uploadChannel(do_QueryInterface(chan)); NS_ENSURE_STATE(uploadChannel); rv = uploadChannel->SetUploadStream(uploadStream, mRequestSession->mPostContentType, -1); NS_ENSURE_SUCCESS(rv, rv); } nsCOMPtr hchan = do_QueryInterface(chan); NS_ENSURE_STATE(hchan); rv = hchan->SetRequestMethod(mRequestSession->mRequestMethod); NS_ENSURE_SUCCESS(rv, rv); mResponsibleForDoneSignal = PR_FALSE; mListener->mResponsibleForDoneSignal = PR_TRUE; mListener->mLoadGroup = lg.get(); NS_ADDREF(mListener->mLoadGroup); mListener->mLoadGroupOwnerThread = PR_GetCurrentThread(); rv = NS_NewStreamLoader(getter_AddRefs(mListener->mLoader), mListener); if (NS_SUCCEEDED(rv)) rv = hchan->AsyncOpen(mListener->mLoader, nsnull); if (NS_FAILED(rv)) { mListener->mResponsibleForDoneSignal = PR_FALSE; mResponsibleForDoneSignal = PR_TRUE; NS_RELEASE(mListener->mLoadGroup); mListener->mLoadGroup = nsnull; mListener->mLoadGroupOwnerThread = nsnull; } return NS_OK; } struct nsCancelHTTPDownloadEvent : nsRunnable { nsCOMPtr mListener; NS_IMETHOD Run() { mListener->FreeLoadGroup(PR_TRUE); mListener = nsnull; return NS_OK; } }; SECStatus nsNSSHttpServerSession::createSessionFcn(const char *host, PRUint16 portnum, SEC_HTTP_SERVER_SESSION *pSession) { if (!host || !pSession) return SECFailure; nsNSSHttpServerSession *hss = new nsNSSHttpServerSession; if (!hss) return SECFailure; hss->mHost = host; hss->mPort = portnum; *pSession = hss; return SECSuccess; } SECStatus nsNSSHttpRequestSession::createFcn(SEC_HTTP_SERVER_SESSION session, const char *http_protocol_variant, const char *path_and_query_string, const char *http_request_method, const PRIntervalTime timeout, SEC_HTTP_REQUEST_SESSION *pRequest) { if (!session || !http_protocol_variant || !path_and_query_string || !http_request_method || !pRequest) return SECFailure; nsNSSHttpServerSession* hss = static_cast(session); if (!hss) return SECFailure; nsNSSHttpRequestSession *rs = new nsNSSHttpRequestSession; if (!rs) return SECFailure; rs->mTimeoutInterval = timeout; // Use a maximum timeout value of 10 seconds because of bug 404059. // FIXME: Use a better approach once 406120 is ready. PRUint32 maxBug404059Timeout = PR_TicksPerSecond() * 10; if (timeout > maxBug404059Timeout) { rs->mTimeoutInterval = maxBug404059Timeout; } rs->mURL.Append(nsDependentCString(http_protocol_variant)); rs->mURL.AppendLiteral("://"); rs->mURL.Append(hss->mHost); rs->mURL.AppendLiteral(":"); rs->mURL.AppendInt(hss->mPort); rs->mURL.Append(path_and_query_string); rs->mRequestMethod = nsDependentCString(http_request_method); *pRequest = (void*)rs; return SECSuccess; } SECStatus nsNSSHttpRequestSession::setPostDataFcn(const char *http_data, const PRUint32 http_data_len, const char *http_content_type) { mHasPostData = PR_TRUE; mPostData.Assign(http_data, http_data_len); mPostContentType.Assign(http_content_type); return SECSuccess; } SECStatus nsNSSHttpRequestSession::addHeaderFcn(const char *http_header_name, const char *http_header_value) { return SECFailure; // not yet implemented // All http code needs to be postponed to the UI thread. // Once this gets implemented, we need to add a string list member to // nsNSSHttpRequestSession and queue up the headers, // so they can be added in HandleHTTPDownloadPLEvent. // // The header will need to be set using // mHttpChannel->SetRequestHeader(nsDependentCString(http_header_name), // nsDependentCString(http_header_value), // PR_FALSE))); } SECStatus nsNSSHttpRequestSession::trySendAndReceiveFcn(PRPollDesc **pPollDesc, PRUint16 *http_response_code, const char **http_response_content_type, const char **http_response_headers, const char **http_response_data, PRUint32 *http_response_data_len) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsNSSHttpRequestSession::trySendAndReceiveFcn to %s\n", mURL.get())); const int max_retries = 2; int retry_count = 0; PRBool retryable_error = PR_FALSE; SECStatus result_sec_status = SECFailure; do { if (retry_count > 0) { if (retryable_error) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsNSSHttpRequestSession::trySendAndReceiveFcn - sleeping and retrying: %d of %d\n", retry_count, max_retries)); } PR_Sleep( PR_MillisecondsToInterval(300) * retry_count ); } ++retry_count; retryable_error = PR_FALSE; result_sec_status = internal_send_receive_attempt(retryable_error, pPollDesc, http_response_code, http_response_content_type, http_response_headers, http_response_data, http_response_data_len); } while (retryable_error && retry_count < max_retries); #ifdef PR_LOGGING if (retry_count > 1) { if (retryable_error) PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsNSSHttpRequestSession::trySendAndReceiveFcn - still failing, giving up...\n")); else PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsNSSHttpRequestSession::trySendAndReceiveFcn - success at attempt %d\n", retry_count)); } #endif return result_sec_status; } void nsNSSHttpRequestSession::AddRef() { PR_AtomicIncrement(&mRefCount); } void nsNSSHttpRequestSession::Release() { PRInt32 newRefCount = PR_AtomicDecrement(&mRefCount); if (!newRefCount) { delete this; } } SECStatus nsNSSHttpRequestSession::internal_send_receive_attempt(PRBool &retryable_error, PRPollDesc **pPollDesc, PRUint16 *http_response_code, const char **http_response_content_type, const char **http_response_headers, const char **http_response_data, PRUint32 *http_response_data_len) { if (pPollDesc) *pPollDesc = nsnull; if (http_response_code) *http_response_code = 0; if (http_response_content_type) *http_response_content_type = 0; if (http_response_headers) *http_response_headers = 0; if (http_response_data) *http_response_data = 0; PRUint32 acceptableResultSize = 0; if (http_response_data_len) { acceptableResultSize = *http_response_data_len; *http_response_data_len = 0; } if (!mListener) return SECFailure; if (NS_FAILED(mListener->InitLocks())) return SECFailure; PRLock *waitLock = mListener->mLock; PRCondVar *waitCondition = mListener->mCondition; volatile PRBool &waitFlag = mListener->mWaitFlag; waitFlag = PR_TRUE; nsRefPtr event = new nsHTTPDownloadEvent; if (!event) return SECFailure; event->mListener = mListener; this->AddRef(); event->mRequestSession = this; nsresult rv = NS_DispatchToMainThread(event); if (NS_FAILED(rv)) { event->mResponsibleForDoneSignal = PR_FALSE; return SECFailure; } PRBool request_canceled = PR_FALSE; { nsAutoLock locker(waitLock); const PRIntervalTime start_time = PR_IntervalNow(); PRIntervalTime wait_interval; PRBool running_on_main_thread = NS_IsMainThread(); if (running_on_main_thread) { // let's process events quickly wait_interval = PR_MicrosecondsToInterval(50); } else { // On a secondary thread, it's fine to wait some more for // for the condition variable. wait_interval = PR_MillisecondsToInterval(250); } while (waitFlag) { if (running_on_main_thread) { // Networking runs on the main thread, which we happen to block here. // Processing events will allow the OCSP networking to run while we // are waiting. Thanks a lot to Darin Fisher for rewriting the // thread manager. Thanks a lot to Christian Biesinger who // made me aware of this possibility. (kaie) locker.unlock(); NS_ProcessNextEvent(nsnull); locker.lock(); } PR_WaitCondVar(waitCondition, wait_interval); if (!waitFlag) break; if (!request_canceled) { PRBool wantExit = nsSSLThread::exitRequested(); PRBool timeout = (PRIntervalTime)(PR_IntervalNow() - start_time) > mTimeoutInterval; if (wantExit || timeout) { request_canceled = PR_TRUE; nsRefPtr cancelevent = new nsCancelHTTPDownloadEvent; cancelevent->mListener = mListener; rv = NS_DispatchToMainThread(cancelevent); if (NS_FAILED(rv)) { NS_WARNING("cannot post cancel event"); } break; } } } } if (request_canceled) return SECFailure; if (NS_FAILED(mListener->mResultCode)) { if (mListener->mResultCode == NS_ERROR_CONNECTION_REFUSED || mListener->mResultCode == NS_ERROR_NET_RESET) { retryable_error = PR_TRUE; } return SECFailure; } if (http_response_code) *http_response_code = mListener->mHttpResponseCode; if (mListener->mHttpRequestSucceeded && http_response_data && http_response_data_len) { *http_response_data_len = mListener->mResultLen; // acceptableResultSize == 0 means: any size is acceptable if (acceptableResultSize != 0 && acceptableResultSize < mListener->mResultLen) { return SECFailure; } // return data by reference, result data will be valid // until "this" gets destroyed by NSS *http_response_data = (const char*)mListener->mResultData; } if (mListener->mHttpRequestSucceeded && http_response_content_type) { if (mListener->mHttpResponseContentType.Length()) { *http_response_content_type = mListener->mHttpResponseContentType.get(); } } return SECSuccess; } SECStatus nsNSSHttpRequestSession::cancelFcn() { // As of today, only the blocking variant of the http interface // has been implemented. Implementing cancelFcn will be necessary // as soon as we implement the nonblocking variant. return SECSuccess; } SECStatus nsNSSHttpRequestSession::freeFcn() { Release(); return SECSuccess; } nsNSSHttpRequestSession::nsNSSHttpRequestSession() : mRefCount(1), mHasPostData(PR_FALSE), mTimeoutInterval(0), mListener(new nsHTTPListener) { } nsNSSHttpRequestSession::~nsNSSHttpRequestSession() { } SEC_HttpClientFcn nsNSSHttpInterface::sNSSInterfaceTable; void nsNSSHttpInterface::initTable() { sNSSInterfaceTable.version = 1; SEC_HttpClientFcnV1 &v1 = sNSSInterfaceTable.fcnTable.ftable1; v1.createSessionFcn = createSessionFcn; v1.keepAliveSessionFcn = keepAliveFcn; v1.freeSessionFcn = freeSessionFcn; v1.createFcn = createFcn; v1.setPostDataFcn = setPostDataFcn; v1.addHeaderFcn = addHeaderFcn; v1.trySendAndReceiveFcn = trySendAndReceiveFcn; v1.cancelFcn = cancelFcn; v1.freeFcn = freeFcn; } void nsNSSHttpInterface::registerHttpClient() { SEC_RegisterDefaultHttpClient(&sNSSInterfaceTable); } void nsNSSHttpInterface::unregisterHttpClient() { SEC_RegisterDefaultHttpClient(nsnull); } nsHTTPListener::nsHTTPListener() : mResultData(nsnull), mResultLen(0), mLock(nsnull), mCondition(nsnull), mWaitFlag(PR_TRUE), mResponsibleForDoneSignal(PR_FALSE), mLoadGroup(nsnull), mLoadGroupOwnerThread(nsnull) { } nsresult nsHTTPListener::InitLocks() { mLock = PR_NewLock(); if (!mLock) return NS_ERROR_OUT_OF_MEMORY; mCondition = PR_NewCondVar(mLock); if (!mCondition) { PR_DestroyLock(mLock); mLock = nsnull; return NS_ERROR_OUT_OF_MEMORY; } return NS_OK; } nsHTTPListener::~nsHTTPListener() { if (mResponsibleForDoneSignal) send_done_signal(); if (mCondition) PR_DestroyCondVar(mCondition); if (mLock) PR_DestroyLock(mLock); if (mLoader) { nsCOMPtr mainThread(do_GetMainThread()); NS_ProxyRelease(mainThread, mLoader); } } NS_IMPL_THREADSAFE_ISUPPORTS1(nsHTTPListener, nsIStreamLoaderObserver) void nsHTTPListener::FreeLoadGroup(PRBool aCancelLoad) { nsILoadGroup *lg = nsnull; if (mLock) { nsAutoLock locker(mLock); if (mLoadGroup) { if (mLoadGroupOwnerThread != PR_GetCurrentThread()) { NS_ASSERTION(PR_FALSE, "attempt to access nsHTTPDownloadEvent::mLoadGroup on multiple threads, leaking it!"); } else { lg = mLoadGroup; mLoadGroup = nsnull; } } } if (lg) { if (aCancelLoad) { lg->Cancel(NS_ERROR_ABORT); } NS_RELEASE(lg); } } NS_IMETHODIMP nsHTTPListener::OnStreamComplete(nsIStreamLoader* aLoader, nsISupports* aContext, nsresult aStatus, PRUint32 stringLen, const PRUint8* string) { mResultCode = aStatus; FreeLoadGroup(PR_FALSE); nsCOMPtr req; nsCOMPtr hchan; nsresult rv = aLoader->GetRequest(getter_AddRefs(req)); #ifdef PR_LOGGING if (NS_FAILED(aStatus)) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsHTTPListener::OnStreamComplete status failed %d", aStatus)); } #endif if (NS_SUCCEEDED(rv)) hchan = do_QueryInterface(req, &rv); if (NS_SUCCEEDED(rv)) { rv = hchan->GetRequestSucceeded(&mHttpRequestSucceeded); if (NS_FAILED(rv)) mHttpRequestSucceeded = PR_FALSE; mResultLen = stringLen; mResultData = string; // reference. Make sure loader lives as long as this unsigned int rcode; rv = hchan->GetResponseStatus(&rcode); if (NS_FAILED(rv)) mHttpResponseCode = 500; else mHttpResponseCode = rcode; hchan->GetResponseHeader(NS_LITERAL_CSTRING("Content-Type"), mHttpResponseContentType); } if (mResponsibleForDoneSignal) send_done_signal(); return aStatus; } void nsHTTPListener::send_done_signal() { mResponsibleForDoneSignal = PR_FALSE; { nsAutoLock locker(mLock); mWaitFlag = PR_FALSE; PR_NotifyAllCondVar(mCondition); } } static char* ShowProtectedAuthPrompt(PK11SlotInfo* slot, nsIInterfaceRequestor *ir) { char* protAuthRetVal = nsnull; // Get protected auth dialogs nsITokenDialogs* dialogs = 0; nsresult nsrv = getNSSDialogs((void**)&dialogs, NS_GET_IID(nsITokenDialogs), NS_TOKENDIALOGS_CONTRACTID); if (NS_SUCCEEDED(nsrv)) { nsProtectedAuthThread* protectedAuthRunnable = new nsProtectedAuthThread(); if (protectedAuthRunnable) { NS_ADDREF(protectedAuthRunnable); protectedAuthRunnable->SetParams(slot); nsCOMPtr runnable = do_QueryInterface(protectedAuthRunnable); if (runnable) { nsrv = dialogs->DisplayProtectedAuth(ir, runnable); // We call join on the thread, // so we can be sure that no simultaneous access will happen. protectedAuthRunnable->Join(); if (NS_SUCCEEDED(nsrv)) { SECStatus rv = protectedAuthRunnable->GetResult(); switch (rv) { case SECSuccess: protAuthRetVal = ToNewCString(nsDependentCString(PK11_PW_AUTHENTICATED)); break; case SECWouldBlock: protAuthRetVal = ToNewCString(nsDependentCString(PK11_PW_RETRY)); break; default: protAuthRetVal = nsnull; break; } } } NS_RELEASE(protectedAuthRunnable); } NS_RELEASE(dialogs); } return protAuthRetVal; } char* PR_CALLBACK PK11PasswordPrompt(PK11SlotInfo* slot, PRBool retry, void* arg) { nsNSSShutDownPreventionLock locker; nsresult rv = NS_OK; PRUnichar *password = nsnull; PRBool value = PR_FALSE; nsIInterfaceRequestor *ir = static_cast(arg); nsCOMPtr proxyPrompt; /* TODO: Retry should generate a different dialog message */ /* if (retry) return nsnull; */ if (!ir) { nsCOMPtr wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID)); if (!wwatch) return nsnull; nsCOMPtr prompter; wwatch->GetNewPrompter(0, getter_AddRefs(prompter)); if (!prompter) return nsnull; NS_GetProxyForObject(NS_PROXY_TO_MAIN_THREAD, NS_GET_IID(nsIPrompt), prompter, NS_PROXY_SYNC, getter_AddRefs(proxyPrompt)); if (!proxyPrompt) return nsnull; } else { // The interface requestor object may not be safe, so // proxy the call to get the nsIPrompt. nsCOMPtr proxiedCallbacks; NS_GetProxyForObject(NS_PROXY_TO_MAIN_THREAD, NS_GET_IID(nsIInterfaceRequestor), ir, NS_PROXY_SYNC, getter_AddRefs(proxiedCallbacks)); // Get the desired interface nsCOMPtr prompt(do_GetInterface(proxiedCallbacks)); if (!prompt) { NS_ASSERTION(PR_FALSE, "callbacks does not implement nsIPrompt"); return nsnull; } // Finally, get a proxy for the nsIPrompt NS_GetProxyForObject(NS_PROXY_TO_MAIN_THREAD, NS_GET_IID(nsIPrompt), prompt, NS_PROXY_SYNC, getter_AddRefs(proxyPrompt)); } if (PK11_ProtectedAuthenticationPath(slot)) return ShowProtectedAuthPrompt(slot, ir); nsAutoString promptString; nsCOMPtr nssComponent(do_GetService(kNSSComponentCID, &rv)); if (NS_FAILED(rv)) return nsnull; const PRUnichar* formatStrings[1] = { ToNewUnicode(NS_ConvertUTF8toUTF16(PK11_GetTokenName(slot))) }; rv = nssComponent->PIPBundleFormatStringFromName("CertPassPrompt", formatStrings, 1, promptString); nsMemory::Free(const_cast(formatStrings[0])); if (NS_FAILED(rv)) return nsnull; { nsPSMUITracker tracker; if (tracker.isUIForbidden()) { rv = NS_ERROR_NOT_AVAILABLE; } else { rv = proxyPrompt->PromptPassword(nsnull, promptString.get(), &password, nsnull, nsnull, &value); } } if (NS_SUCCEEDED(rv) && value) { char* str = ToNewUTF8String(nsDependentString(password)); NS_Free(password); return str; } return nsnull; } void PR_CALLBACK HandshakeCallback(PRFileDesc* fd, void* client_data) { nsNSSShutDownPreventionLock locker; PRInt32 sslStatus; char* signer = nsnull; char* cipherName = nsnull; PRInt32 keyLength; nsresult rv; PRInt32 encryptBits; if (SECSuccess != SSL_SecurityStatus(fd, &sslStatus, &cipherName, &keyLength, &encryptBits, &signer, nsnull)) { return; } PRInt32 secStatus; if (sslStatus == SSL_SECURITY_STATUS_OFF) secStatus = nsIWebProgressListener::STATE_IS_BROKEN; else if (encryptBits >= 90) secStatus = (nsIWebProgressListener::STATE_IS_SECURE | nsIWebProgressListener::STATE_SECURE_HIGH); else secStatus = (nsIWebProgressListener::STATE_IS_SECURE | nsIWebProgressListener::STATE_SECURE_LOW); CERTCertificate *peerCert = SSL_PeerCertificate(fd); const char* caName = nsnull; // caName is a pointer only, no ownership char* certOrgName = CERT_GetOrgName(&peerCert->issuer); CERT_DestroyCertificate(peerCert); caName = certOrgName ? certOrgName : signer; const char* verisignName = "Verisign, Inc."; // If the CA name is RSA Data Security, then change the name to the real // name of the company i.e. VeriSign, Inc. if (nsCRT::strcmp((const char*)caName, "RSA Data Security, Inc.") == 0) { caName = verisignName; } nsAutoString shortDesc; const PRUnichar* formatStrings[1] = { ToNewUnicode(NS_ConvertUTF8toUTF16(caName)) }; nsCOMPtr nssComponent(do_GetService(kNSSComponentCID, &rv)); if (NS_SUCCEEDED(rv)) { rv = nssComponent->PIPBundleFormatStringFromName("SignedBy", formatStrings, 1, shortDesc); nsMemory::Free(const_cast(formatStrings[0])); nsNSSSocketInfo* infoObject = (nsNSSSocketInfo*) fd->higher->secret; infoObject->SetSecurityState(secStatus); infoObject->SetShortSecurityDescription(shortDesc.get()); /* Set the SSL Status information */ nsRefPtr status = infoObject->SSLStatus(); if (!status) { status = new nsSSLStatus(); infoObject->SetSSLStatus(status); } CERTCertificate *serverCert = SSL_PeerCertificate(fd); if (serverCert) { nsRefPtr nssc = new nsNSSCertificate(serverCert); CERT_DestroyCertificate(serverCert); serverCert = nsnull; nsCOMPtr prevcert; infoObject->GetPreviousCert(getter_AddRefs(prevcert)); PRBool equals_previous = PR_FALSE; if (prevcert) { nsresult rv = nssc->Equals(prevcert, &equals_previous); if (NS_FAILED(rv)) { equals_previous = PR_FALSE; } } if (equals_previous) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("HandshakeCallback using PREV cert %p\n", prevcert.get())); infoObject->SetCert(prevcert); status->mServerCert = prevcert; } else { if (status->mServerCert) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("HandshakeCallback KEEPING cert %p\n", status->mServerCert.get())); infoObject->SetCert(status->mServerCert); } else { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("HandshakeCallback using NEW cert %p\n", nssc.get())); infoObject->SetCert(nssc); status->mServerCert = nssc; } } } status->mHaveKeyLengthAndCipher = PR_TRUE; status->mKeyLength = keyLength; status->mSecretKeyLength = encryptBits; status->mCipherName.Adopt(cipherName); } PR_FREEIF(certOrgName); PR_Free(signer); } SECStatus PR_CALLBACK AuthCertificateCallback(void* client_data, PRFileDesc* fd, PRBool checksig, PRBool isServer) { nsNSSShutDownPreventionLock locker; // first the default action SECStatus rv = SSL_AuthCertificate(CERT_GetDefaultCertDB(), fd, checksig, isServer); // We want to remember the CA certs in the temp db, so that the application can find the // complete chain at any time it might need it. // But we keep only those CA certs in the temp db, that we didn't already know. CERTCertificate *serverCert = SSL_PeerCertificate(fd); CERTCertificateCleaner serverCertCleaner(serverCert); if (serverCert) { nsNSSSocketInfo* infoObject = (nsNSSSocketInfo*) fd->higher->secret; nsRefPtr status = infoObject->SSLStatus(); nsRefPtr nsc; if (!status || !status->mServerCert) { nsc = new nsNSSCertificate(serverCert); } if (SECSuccess == rv) { if (nsc) { PRBool dummyIsEV; nsc->GetIsExtendedValidation(&dummyIsEV); // the nsc object will cache the status } CERTCertList *certList = CERT_GetCertChainFromCert(serverCert, PR_Now(), certUsageSSLCA); nsCOMPtr nssComponent; for (CERTCertListNode *node = CERT_LIST_HEAD(certList); !CERT_LIST_END(node, certList); node = CERT_LIST_NEXT(node)) { if (node->cert->slot) { // This cert was found on a token, no need to remember it in the temp db. continue; } if (node->cert->isperm) { // We don't need to remember certs already stored in perm db. continue; } if (node->cert == serverCert) { // We don't want to remember the server cert, // the code that cares for displaying page info does this already. continue; } // We have found a signer cert that we want to remember. nsCAutoString nickname; nickname = nsNSSCertificate::defaultServerNickname(node->cert); if (!nickname.IsEmpty()) { PK11SlotInfo *slot = PK11_GetInternalKeySlot(); if (slot) { PK11_ImportCert(slot, node->cert, CK_INVALID_HANDLE, const_cast(nickname.get()), PR_FALSE); PK11_FreeSlot(slot); } } } CERT_DestroyCertList(certList); } // The connection may get terminated, for example, if the server requires // a client cert. Let's provide a minimal SSLStatus // to the caller that contains at least the cert and its status. if (!status) { status = new nsSSLStatus(); infoObject->SetSSLStatus(status); } if (status && !status->mServerCert) { status->mServerCert = nsc; PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("AuthCertificateCallback setting NEW cert %p\n", status->mServerCert.get())); } } return rv; }