diff --git a/security/manager/locales/en-US/chrome/pipnss/pipnss.properties b/security/manager/locales/en-US/chrome/pipnss/pipnss.properties index 0bcd3c5cd825..cc2faa770fde 100644 --- a/security/manager/locales/en-US/chrome/pipnss/pipnss.properties +++ b/security/manager/locales/en-US/chrome/pipnss/pipnss.properties @@ -306,6 +306,7 @@ OCSPUnauthorizedResponse=Error trying to validate certificate from %S using OCSP OCSPUnknownCert=Error trying to validate certificate from %S using OCSP - unknown certificate. OCSPNoDefaultResponder=Error trying to validate certificate from %S using OCSP - no default responder specified. OCSPDirLookup=Error trying to validate certificate from %S using OCSP - directory lookup error. +OCSPDeadlock=An internal failure has been detected. It is not possible to complete the requested OCSP operation. CertInfoIssuedFor=Issued to: CertInfoIssuedBy=Issued by: CertInfoValid=Valid diff --git a/security/manager/pki/resources/content/viewCertDetails.js b/security/manager/pki/resources/content/viewCertDetails.js index a940d85a7d7c..8763e2525c9b 100644 --- a/security/manager/pki/resources/content/viewCertDetails.js +++ b/security/manager/pki/resources/content/viewCertDetails.js @@ -22,6 +22,7 @@ * Bob Lord * Ian McGreer * Javier Delgadillo + * Kai Engert * * 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 @@ -38,6 +39,7 @@ * ***** END LICENSE BLOCK ***** */ const nsIX509Cert = Components.interfaces.nsIX509Cert; +const nsIX509Cert3 = Components.interfaces.nsIX509Cert3; const nsX509CertDB = "@mozilla.org/security/x509certdb;1"; const nsIX509CertDB = Components.interfaces.nsIX509CertDB; const nsPK11TokenDB = "@mozilla.org/security/pk11tokendb;1"; @@ -121,6 +123,13 @@ function setWindowName() AddCertChain("treesetDump", chain, "dump_"); DisplayGeneralDataFromCert(cert); BuildPrettyPrint(cert); + + if (cert instanceof nsIX509Cert3) + { + cert.requestUsagesArrayAsync( + getProxyOnUIThread(new listener(), + Components.interfaces.nsICertVerificationListener)); + } } @@ -181,14 +190,44 @@ function addAttributeFromCert(nodeName, value) node.setAttribute('value',value) } -function DisplayGeneralDataFromCert(cert) + + +function listener() { +} + +listener.prototype.QueryInterface = + function(iid) { + if (iid.equals(Components.interfaces.nsISupports) || + iid.equals(Components.interfaces.nsICertVerificationListener)) + return this; + + throw Components.results.NS_ERROR_NO_INTERFACE; + } + +listener.prototype.notify = + function(cert, result) { + DisplayVerificationData(cert, result); + } + +function DisplayVerificationData(cert, result) { + if (!result || !cert) + return; // no results could be produced + + if (!(cert instanceof Components.interfaces.nsIX509Cert)) + return; + // Verification and usage var verifystr = ""; var o1 = {}; var o2 = {}; var o3 = {}; - cert.getUsagesArray(false, o1, o2, o3); // do not ignore OCSP when checking + + if (!(result instanceof Components.interfaces.nsICertVerificationResult)) + return; + + result.getUsagesArrayResult(o1, o2, o3); + var verifystate = o1.value; var count = o2.value; var usageList = o3.value; @@ -217,7 +256,10 @@ function DisplayGeneralDataFromCert(cert) AddUsage(usageList[i],verifyInfoBox); } } +} +function DisplayGeneralDataFromCert(cert) +{ // Common Name addAttributeFromCert('commonname', cert.commonName); // Organization @@ -263,3 +305,21 @@ function updateCertDump() } displaySelected(); } + +function getProxyOnUIThread(aObject, aInterface) { + var eventQSvc = Components. + classes["@mozilla.org/event-queue-service;1"]. + getService(Components.interfaces.nsIEventQueueService); + + var uiQueue = eventQSvc. + getSpecialEventQueue(Components.interfaces. + nsIEventQueueService.UI_THREAD_EVENT_QUEUE); + + var proxyMgr = Components. + classes["@mozilla.org/xpcomproxy;1"]. + getService(Components.interfaces.nsIProxyObjectManager); + + return proxyMgr.getProxyForObject(uiQueue, + aInterface, aObject, 5); + // 5 == PROXY_ALWAYS | PROXY_SYNC +} diff --git a/security/manager/ssl/public/Makefile.in b/security/manager/ssl/public/Makefile.in index 429841fb7ec9..4416c3207a3f 100644 --- a/security/manager/ssl/public/Makefile.in +++ b/security/manager/ssl/public/Makefile.in @@ -55,6 +55,7 @@ SDK_XPIDLSRCS = \ nsICertificateDialogs.idl \ nsICRLInfo.idl \ nsIX509Cert.idl \ + nsIX509Cert3.idl \ nsIX509CertDB.idl \ nsIX509CertValidity.idl \ $(NULL) @@ -82,6 +83,7 @@ XPIDLSRCS = \ nsICMSEncoder.idl \ nsICMSMessageErrors.idl \ nsICMSMessage.idl \ + nsICMSMessage2.idl \ nsINSSCertCache.idl \ nsIOCSPResponder.idl \ nsIPK11Token.idl \ diff --git a/security/manager/ssl/src/Makefile.in b/security/manager/ssl/src/Makefile.in index e2123e2d8a43..c9b1cfa55ea0 100644 --- a/security/manager/ssl/src/Makefile.in +++ b/security/manager/ssl/src/Makefile.in @@ -57,6 +57,9 @@ LIBXUL_LIBRARY = 1 PACKAGE_FILE = pipnss.pkg CPPSRCS = \ + nsPSMBackgroundThread.cpp \ + nsSSLThread.cpp \ + nsCertVerificationThread.cpp \ nsCipherInfo.cpp \ nsNSSCallbacks.cpp \ nsNSSComponent.cpp \ diff --git a/security/manager/ssl/src/nsCMS.cpp b/security/manager/ssl/src/nsCMS.cpp index 6916e8d512fb..997abf2ee0f6 100644 --- a/security/manager/ssl/src/nsCMS.cpp +++ b/security/manager/ssl/src/nsCMS.cpp @@ -20,6 +20,7 @@ * the Initial Developer. All Rights Reserved. * * Contributor(s): David Drinan + * Kai Engert * * 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 @@ -43,6 +44,7 @@ #include "cms.h" #include "nsICMSMessageErrors.h" #include "nsArray.h" +#include "nsCertVerificationThread.h" #include "prlog.h" #ifdef PR_LOGGING @@ -52,7 +54,9 @@ extern PRLogModuleInfo* gPIPNSSLog; #include "nsNSSCleaner.h" NSSCleanupAutoPtrClass(CERTCertificate, CERT_DestroyCertificate) -NS_IMPL_THREADSAFE_ISUPPORTS1(nsCMSMessage, nsICMSMessage) + +NS_IMPL_THREADSAFE_ISUPPORTS2(nsCMSMessage, nsICMSMessage, + nsICMSMessage2) nsCMSMessage::nsCMSMessage() { @@ -224,10 +228,6 @@ NS_IMETHODIMP nsCMSMessage::GetEncryptionCert(nsIX509Cert **ecert) NS_IMETHODIMP nsCMSMessage::VerifyDetachedSignature(unsigned char* aDigestData, PRUint32 aDigestDataLen) { - nsNSSShutDownPreventionLock locker; - if (isAlreadyShutDown()) - return NS_ERROR_NOT_AVAILABLE; - if (!aDigestData || !aDigestDataLen) return NS_ERROR_FAILURE; @@ -340,6 +340,56 @@ loser: return rv; } +NS_IMETHODIMP nsCMSMessage::AsyncVerifySignature( + nsISMimeVerificationListener *aListener) +{ + return CommonAsyncVerifySignature(aListener, nsnull, 0); +} + +NS_IMETHODIMP nsCMSMessage::AsyncVerifyDetachedSignature( + nsISMimeVerificationListener *aListener, + unsigned char* aDigestData, PRUint32 aDigestDataLen) +{ + if (!aDigestData || !aDigestDataLen) + return NS_ERROR_FAILURE; + + return CommonAsyncVerifySignature(aListener, aDigestData, aDigestDataLen); +} + +nsresult nsCMSMessage::CommonAsyncVerifySignature(nsISMimeVerificationListener *aListener, + unsigned char* aDigestData, PRUint32 aDigestDataLen) +{ + nsSMimeVerificationJob *job = new nsSMimeVerificationJob; + if (!job) + return NS_ERROR_OUT_OF_MEMORY; + + if (aDigestData) + { + job->digest_data = new unsigned char[aDigestDataLen]; + if (!job->digest_data) + { + delete job; + return NS_ERROR_OUT_OF_MEMORY; + } + + memcpy(job->digest_data, aDigestData, aDigestDataLen); + } + else + { + job->digest_data = nsnull; + } + + job->digest_len = aDigestDataLen; + job->mMessage = this; + job->mListener = aListener; + + nsresult rv = nsCertVerificationThread::addJob(job); + if (NS_FAILED(rv)) + delete job; + + return rv; +} + class nsZeroTerminatedCertArray : public nsNSSShutDownObject { public: diff --git a/security/manager/ssl/src/nsCMS.h b/security/manager/ssl/src/nsCMS.h index be5656dbd594..6a3745e75d34 100644 --- a/security/manager/ssl/src/nsCMS.h +++ b/security/manager/ssl/src/nsCMS.h @@ -20,6 +20,7 @@ * the Initial Developer. All Rights Reserved. * * Contributor(s): David Drinan + * Kai Engert * * 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 @@ -40,8 +41,12 @@ #include "nsISupports.h" #include "nsCOMPtr.h" +#include "nsXPIDLString.h" #include "nsIInterfaceRequestor.h" #include "nsICMSMessage.h" +#include "nsICMSMessage2.h" +#include "nsIX509Cert3.h" +#include "nsVerificationJob.h" #include "nsICMSEncoder.h" #include "nsICMSDecoder.h" #include "sechash.h" @@ -53,11 +58,13 @@ { 0xa4557478, 0xae16, 0x11d5, { 0xba,0x4b,0x00,0x10,0x83,0x03,0xb1,0x17 } } class nsCMSMessage : public nsICMSMessage, + public nsICMSMessage2, public nsNSSShutDownObject { public: NS_DECL_ISUPPORTS NS_DECL_NSICMSMESSAGE + NS_DECL_NSICMSMESSAGE2 nsCMSMessage(); nsCMSMessage(NSSCMSMessage* aCMSMsg); @@ -70,10 +77,15 @@ private: NSSCMSMessage * m_cmsMsg; NSSCMSSignerInfo* GetTopLevelSignerInfo(); nsresult CommonVerifySignature(unsigned char* aDigestData, PRUint32 aDigestDataLen); + + nsresult CommonAsyncVerifySignature(nsISMimeVerificationListener *aListener, + unsigned char* aDigestData, PRUint32 aDigestDataLen); + virtual void virtualDestroyNSSReference(); void destructorSafeDestroyNSSReference(); -}; +friend class nsSMimeVerificationJob; +}; // =============================================== // nsCMSDecoder - implementation of nsICMSDecoder diff --git a/security/manager/ssl/src/nsNSSCallbacks.cpp b/security/manager/ssl/src/nsNSSCallbacks.cpp index 142363cdeb87..008f0d2dd02c 100644 --- a/security/manager/ssl/src/nsNSSCallbacks.cpp +++ b/security/manager/ssl/src/nsNSSCallbacks.cpp @@ -23,6 +23,7 @@ * Contributor(s): * Brian Ryner * Terry Hayes + * Kai Engert * * 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 @@ -54,13 +55,516 @@ #include "nsIInterfaceRequestorUtils.h" #include "nsCRT.h" #include "nsNSSShutDown.h" +#include "nsNSSEvent.h" +#include "nsIUploadChannel.h" +#include "nsSSLThread.h" +#include "nsAutoLock.h" +#include "nsIThread.h" +#include "nsIWindowWatcher.h" +#include "nsIPrompt.h" #include "ssl.h" #include "cert.h" - +#include "ocsp.h" static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID); +struct nsHTTPDownloadEvent : PLEvent { + nsHTTPDownloadEvent(); + ~nsHTTPDownloadEvent(); + + nsNSSHttpRequestSession *mRequestSession; // no ownership + + nsCOMPtr mListener; + PRBool mResponsibleForDoneSignal; +}; + +nsHTTPDownloadEvent::nsHTTPDownloadEvent() +:mResponsibleForDoneSignal(PR_TRUE) +{ +} + +nsHTTPDownloadEvent::~nsHTTPDownloadEvent() +{ + if (mResponsibleForDoneSignal && mListener) + mListener->send_done_signal(); +} + +static void PR_CALLBACK HandleHTTPDownloadPLEvent(nsHTTPDownloadEvent *aEvent) +{ + if((!aEvent) || (!aEvent->mListener)) + return; + + nsresult rv; + nsCOMPtr ios = do_GetIOService(&rv); + if (NS_FAILED(rv)) + return; + + nsCOMPtr chan; + ios->NewChannel(aEvent->mRequestSession->mURL, nsnull, nsnull, getter_AddRefs(chan)); + if (NS_FAILED(rv)) + return; + + // 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 loadGroup = + do_CreateInstance(NS_LOADGROUP_CONTRACTID); + chan->SetLoadGroup(loadGroup); + + if (aEvent->mRequestSession->mHasPostData) + { + nsCOMPtr uploadStream; + rv = NS_NewPostDataStream(getter_AddRefs(uploadStream), + PR_FALSE, + aEvent->mRequestSession->mPostData, + 0, ios); + if (NS_FAILED(rv)) + return; + + nsCOMPtr uploadChannel(do_QueryInterface(chan, &rv)); + if (NS_FAILED(rv)) + return; + + rv = uploadChannel->SetUploadStream(uploadStream, + aEvent->mRequestSession->mPostContentType, + -1); + if (NS_FAILED(rv)) + return; + } + + nsCOMPtr hchan = do_QueryInterface(chan, &rv); + if (NS_FAILED(rv)) + return; + + rv = hchan->SetRequestMethod(aEvent->mRequestSession->mRequestMethod); + if (NS_FAILED(rv)) + return; + + nsSSLThread::rememberPendingHTTPRequest(loadGroup); + + aEvent->mResponsibleForDoneSignal = PR_FALSE; + aEvent->mListener->mResponsibleForDoneSignal = PR_TRUE; + + rv = NS_NewStreamLoader(getter_AddRefs(aEvent->mListener->mLoader), + hchan, + aEvent->mListener, + nsnull); + + if (NS_FAILED(rv)) { + aEvent->mListener->mResponsibleForDoneSignal = PR_FALSE; + aEvent->mResponsibleForDoneSignal = PR_TRUE; + + nsSSLThread::rememberPendingHTTPRequest(nsnull); + } +} + +static void PR_CALLBACK DestroyHTTPDownloadPLEvent(nsHTTPDownloadEvent* aEvent) +{ + delete aEvent; +} + +struct nsCancelHTTPDownloadEvent : PLEvent { +}; + +static void PR_CALLBACK HandleCancelHTTPDownloadPLEvent(nsCancelHTTPDownloadEvent *aEvent) +{ + nsSSLThread::cancelPendingHTTPRequest(); +} + +static void PR_CALLBACK DestroyCancelHTTPDownloadPLEvent(nsCancelHTTPDownloadEvent* aEvent) +{ + delete aEvent; +} + +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 = NS_STATIC_CAST(nsNSSHttpServerSession*, session); + if (!hss) + return SECFailure; + + nsNSSHttpRequestSession *rs = new nsNSSHttpRequestSession; + if (!rs) + return SECFailure; + + rs->mTimeoutInterval = timeout; + + 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))); +} + +#define CONDITION_WAIT_TIME PR_MillisecondsToInterval(250) + +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) +{ + if (nsIThread::IsMainThread()) + { + nsresult rv; + nsCOMPtr nssComponent(do_GetService(kNSSComponentCID, &rv)); + if (NS_FAILED(rv)) + return SECFailure; + + nsCOMPtr wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID)); + if (wwatch){ + nsCOMPtr prompter; + wwatch->GetNewPrompter(0, getter_AddRefs(prompter)); + + nsString message; + nssComponent->GetPIPNSSBundleString("OCSPDeadlock", message); + + if(prompter) { + nsPSMUITracker tracker; + if (!tracker.isUIForbidden()) { + prompter->Alert(0, message.get()); + } + } + } + + return SECFailure; + } + + 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; + } + + nsCOMPtr uiQueue = nsNSSEventGetUIEventQueue(); + if (!uiQueue) + return SECFailure; + + 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; + + nsHTTPDownloadEvent *event = new nsHTTPDownloadEvent; + if (!event) + return SECFailure; + + event->mListener = mListener; + event->mRequestSession = this; + + PL_InitEvent(event, nsnull, (PLHandleEventProc)HandleHTTPDownloadPLEvent, + (PLDestroyEventProc)DestroyHTTPDownloadPLEvent); + nsresult rv = uiQueue->PostEvent(event); + if (NS_FAILED(rv)) + { + event->mResponsibleForDoneSignal = PR_FALSE; + delete event; + return SECFailure; + } + + PRBool request_canceled = PR_FALSE; + PRBool aborted_wait = PR_FALSE; + + { + nsAutoLock locker(waitLock); + + const PRIntervalTime start_time = PR_IntervalNow(); + const PRIntervalTime wait_interval = CONDITION_WAIT_TIME; + + while (waitFlag) + { + PR_WaitCondVar(waitCondition, wait_interval); + + if (!waitFlag) + break; + + if (!request_canceled) + { + if ((PRIntervalTime)(PR_IntervalNow() - start_time) > mTimeoutInterval) + { + request_canceled = PR_TRUE; + // but we'll to continue to wait for waitFlag + + nsCancelHTTPDownloadEvent *cancelevent = new nsCancelHTTPDownloadEvent; + PL_InitEvent(cancelevent, nsnull, (PLHandleEventProc)HandleCancelHTTPDownloadPLEvent, + (PLDestroyEventProc)DestroyCancelHTTPDownloadPLEvent); + rv = uiQueue->PostEvent(cancelevent); + if (NS_FAILED(rv)) + { + NS_WARNING("cannot post cancel event"); + delete cancelevent; + aborted_wait = PR_TRUE; + break; + } + } + } + } + } + + if (aborted_wait) + { + // we couldn't cancel it, let's no longer reference it + nsSSLThread::rememberPendingHTTPRequest(nsnull); + } + + if (request_canceled) + return SECFailure; + + if (NS_FAILED(mListener->mResultCode)) + 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() +{ + delete this; + return SECSuccess; +} + +nsNSSHttpRequestSession::nsNSSHttpRequestSession() +: 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) +{ +} + +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); +} + +NS_IMPL_THREADSAFE_ISUPPORTS1(nsHTTPListener, nsIStreamLoaderObserver) + +NS_IMETHODIMP +nsHTTPListener::OnStreamComplete(nsIStreamLoader* aLoader, + nsISupports* aContext, + nsresult aStatus, + PRUint32 stringLen, + const PRUint8* string) +{ + mResultCode = aStatus; + + nsCOMPtr req; + nsCOMPtr hchan; + + nsresult rv = aLoader->GetRequest(getter_AddRefs(req)); + + 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() +{ + nsSSLThread::rememberPendingHTTPRequest(nsnull); + + mResponsibleForDoneSignal = PR_FALSE; + + { + nsAutoLock locker(mLock); + mWaitFlag = PR_FALSE; + PR_NotifyAllCondVar(mCondition); + } +} + /* Implementation of nsISSLStatus */ class nsSSLStatus : public nsISSLStatus diff --git a/security/manager/ssl/src/nsNSSCallbacks.h b/security/manager/ssl/src/nsNSSCallbacks.h index 8d925f20f189..c04bb7e6aac3 100644 --- a/security/manager/ssl/src/nsNSSCallbacks.h +++ b/security/manager/ssl/src/nsNSSCallbacks.h @@ -22,6 +22,7 @@ * * Contributor(s): * Brian Ryner + * Kai Engert * * 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 @@ -42,6 +43,8 @@ #include "pk11func.h" #include "nspr.h" +#include "ocspt.h" +#include "nsIStreamLoader.h" char* PR_CALLBACK PK11PasswordPrompt(PK11SlotInfo *slot, PRBool retry, void* arg); @@ -50,6 +53,180 @@ void PR_CALLBACK HandshakeCallback(PRFileDesc *fd, void *client_data); SECStatus PR_CALLBACK AuthCertificateCallback(void* client_data, PRFileDesc* fd, PRBool checksig, PRBool isServer); +class nsHTTPListener : public nsIStreamLoaderObserver +{ +private: + // For XPCOM implementations that are not a base class for some other + // class, it is good practice to make the destructor non-virtual and + // private. Then the only way to delete the object is via Release. + ~nsHTTPListener(); + +public: + nsHTTPListener(); + + NS_DECL_ISUPPORTS + NS_DECL_NSISTREAMLOADEROBSERVER + + nsCOMPtr mLoader; + + nsresult mResultCode; + + PRBool mHttpRequestSucceeded; + PRUint16 mHttpResponseCode; + nsCString mHttpResponseContentType; + + const PRUint8* mResultData; // not owned, refers to mLoader + PRUint32 mResultLen; + + nsresult InitLocks(); + + PRLock *mLock; + PRCondVar *mCondition; + volatile PRBool mWaitFlag; + + PRBool mResponsibleForDoneSignal; + void send_done_signal(); +}; + +class nsNSSHttpServerSession +{ +public: + nsCString mHost; + PRUint16 mPort; + + static SECStatus createSessionFcn(const char *host, + PRUint16 portnum, + SEC_HTTP_SERVER_SESSION *pSession); +}; + +class nsNSSHttpRequestSession +{ +public: + static SECStatus 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); + + SECStatus setPostDataFcn(const char *http_data, + const PRUint32 http_data_len, + const char *http_content_type); + + SECStatus addHeaderFcn(const char *http_header_name, + const char *http_header_value); + + SECStatus 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); + + SECStatus cancelFcn(); + SECStatus freeFcn(); + + nsCString mURL; + nsCString mRequestMethod; + + PRBool mHasPostData; + nsCString mPostData; + nsCString mPostContentType; + + PRIntervalTime mTimeoutInterval; + + nsCOMPtr mListener; + +protected: + nsNSSHttpRequestSession(); + ~nsNSSHttpRequestSession(); +}; + +class nsNSSHttpInterface +{ +public: + static SECStatus createSessionFcn(const char *host, + PRUint16 portnum, + SEC_HTTP_SERVER_SESSION *pSession) + { + return nsNSSHttpServerSession::createSessionFcn(host, portnum, pSession); + } + + static SECStatus keepAliveFcn(SEC_HTTP_SERVER_SESSION session, + PRPollDesc **pPollDesc) + { + // Not yet implemented, however, Necko does transparent keep-alive + // anyway, when enabled in Necko's prefs. + return SECSuccess; + } + + static SECStatus freeSessionFcn(SEC_HTTP_SERVER_SESSION session) + { + delete NS_STATIC_CAST(nsNSSHttpServerSession*, session); + return SECSuccess; + } + + static SECStatus 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) + { + return nsNSSHttpRequestSession::createFcn(session, http_protocol_variant, + path_and_query_string, http_request_method, + timeout, pRequest); + } + + static SECStatus setPostDataFcn(SEC_HTTP_REQUEST_SESSION request, + const char *http_data, + const PRUint32 http_data_len, + const char *http_content_type) + { + return NS_STATIC_CAST(nsNSSHttpRequestSession*, request) + ->setPostDataFcn(http_data, http_data_len, http_content_type); + } + + static SECStatus addHeaderFcn(SEC_HTTP_REQUEST_SESSION request, + const char *http_header_name, + const char *http_header_value) + { + return NS_STATIC_CAST(nsNSSHttpRequestSession*, request) + ->addHeaderFcn(http_header_name, http_header_value); + } + + static SECStatus trySendAndReceiveFcn(SEC_HTTP_REQUEST_SESSION request, + 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) + { + return NS_STATIC_CAST(nsNSSHttpRequestSession*, request) + ->trySendAndReceiveFcn(pPollDesc, http_response_code, http_response_content_type, + http_response_headers, http_response_data, http_response_data_len); + } + + static SECStatus cancelFcn(SEC_HTTP_REQUEST_SESSION request) + { + return NS_STATIC_CAST(nsNSSHttpRequestSession*, request) + ->cancelFcn(); + } + + static SECStatus freeFcn(SEC_HTTP_REQUEST_SESSION request) + { + return NS_STATIC_CAST(nsNSSHttpRequestSession*, request) + ->freeFcn(); + } + + static void initTable(); + static SEC_HttpClientFcn sNSSInterfaceTable; + + void registerHttpClient(); + void unregisterHttpClient(); +}; + #endif // _NSNSSCALLBACKS_H_ diff --git a/security/manager/ssl/src/nsNSSCertificate.cpp b/security/manager/ssl/src/nsNSSCertificate.cpp index a1362650794e..5c7c226eb506 100644 --- a/security/manager/ssl/src/nsNSSCertificate.cpp +++ b/security/manager/ssl/src/nsNSSCertificate.cpp @@ -21,6 +21,7 @@ * Contributor(s): * Ian McGreer * Javier Delgadillo + * Kai Engert * * 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 @@ -48,6 +49,7 @@ #include "nsPKCS12Blob.h" #include "nsPK11TokenDB.h" #include "nsIX509Cert.h" +#include "nsIX509Cert3.h" #include "nsISMimeCert.h" #include "nsNSSASN1Object.h" #include "nsString.h" @@ -64,6 +66,7 @@ #include "nsNSSCertHelper.h" #include "nsISupportsPrimitives.h" #include "nsUnicharUtils.h" +#include "nsCertVerificationThread.h" #include "nspr.h" extern "C" { @@ -88,8 +91,9 @@ static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID); /* nsNSSCertificate */ -NS_IMPL_THREADSAFE_ISUPPORTS3(nsNSSCertificate, nsIX509Cert, +NS_IMPL_THREADSAFE_ISUPPORTS4(nsNSSCertificate, nsIX509Cert, nsIX509Cert2, + nsIX509Cert3, nsISMimeCert) nsNSSCertificate* @@ -1052,6 +1056,26 @@ nsNSSCertificate::GetUsagesArray(PRBool ignoreOcsp, return NS_OK; } +NS_IMETHODIMP +nsNSSCertificate::RequestUsagesArrayAsync(nsICertVerificationListener *aResultListener) +{ + if (!aResultListener) + return NS_ERROR_FAILURE; + + nsCertVerificationJob *job = new nsCertVerificationJob; + if (!job) + return NS_ERROR_OUT_OF_MEMORY; + + job->mCert = this; + job->mListener = aResultListener; + + nsresult rv = nsCertVerificationThread::addJob(job); + if (NS_FAILED(rv)) + delete job; + + return rv; +} + NS_IMETHODIMP nsNSSCertificate::GetUsagesString(PRBool ignoreOcsp, PRUint32 *_verified, diff --git a/security/manager/ssl/src/nsNSSCertificate.h b/security/manager/ssl/src/nsNSSCertificate.h index 52777fad53ff..0b89911005d3 100644 --- a/security/manager/ssl/src/nsNSSCertificate.h +++ b/security/manager/ssl/src/nsNSSCertificate.h @@ -21,6 +21,7 @@ * Contributor(s): * Ian McGreer * Javier Delgadillo + * Kai Engert * * 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 @@ -41,6 +42,7 @@ #include "nsIX509Cert.h" #include "nsIX509Cert2.h" +#include "nsIX509Cert3.h" #include "nsIX509CertDB.h" #include "nsIX509CertList.h" #include "nsIASN1Object.h" @@ -56,6 +58,7 @@ class nsIASN1Sequence; /* Certificate */ class nsNSSCertificate : public nsIX509Cert, public nsIX509Cert2, + public nsIX509Cert3, public nsISMimeCert, public nsNSSShutDownObject { @@ -63,6 +66,7 @@ public: NS_DECL_ISUPPORTS NS_DECL_NSIX509CERT NS_DECL_NSIX509CERT2 + NS_DECL_NSIX509CERT3 NS_DECL_NSISMIMECERT nsNSSCertificate(CERTCertificate *cert); diff --git a/security/manager/ssl/src/nsNSSComponent.cpp b/security/manager/ssl/src/nsNSSComponent.cpp index f56071874b42..623e49f5ce00 100644 --- a/security/manager/ssl/src/nsNSSComponent.cpp +++ b/security/manager/ssl/src/nsNSSComponent.cpp @@ -26,6 +26,7 @@ * Mitch Stoltz * Brian Ryner * Kai Engert + * Kai Engert * * 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 @@ -44,6 +45,8 @@ #include "nsNSSComponent.h" #include "nsNSSCallbacks.h" #include "nsNSSIOLayer.h" +#include "nsSSLThread.h" +#include "nsCertVerificationThread.h" #include "nsNSSEvent.h" #include "nsNetUtil.h" @@ -289,15 +292,34 @@ nsNSSComponent::nsNSSComponent() mTimer = nsnull; mCrlTimerLock = nsnull; mObserversRegistered = PR_FALSE; + + nsSSLIOLayerHelpers::Init(); NS_ASSERTION( (0 == mInstanceCount), "nsNSSComponent is a singleton, but instantiated multiple times!"); ++mInstanceCount; hashTableCerts = nsnull; mShutdownObjectList = nsNSSShutDownList::construct(); + mIsNetworkDown = PR_FALSE; + mSSLThread = new nsSSLThread(); + mCertVerificationThread = new nsCertVerificationThread(); } nsNSSComponent::~nsNSSComponent() { + if (mSSLThread) + { + mSSLThread->requestExit(); + delete mSSLThread; + mSSLThread = nsnull; + } + + if (mCertVerificationThread) + { + mCertVerificationThread->requestExit(); + delete mCertVerificationThread; + mCertVerificationThread = nsnull; + } + PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsNSSComponent::dtor\n")); if(mUpdateTimerInitialized == PR_TRUE){ @@ -319,7 +341,7 @@ nsNSSComponent::~nsNSSComponent() // All cleanup code requiring services needs to happen in xpcom_shutdown ShutdownNSS(); - nsSSLIOLayerFreeTLSIntolerantSites(); + nsSSLIOLayerHelpers::Cleanup(); --mInstanceCount; delete mShutdownObjectList; @@ -1455,6 +1477,9 @@ nsNSSComponent::InitializeNSS(PRBool showWarningBox) // Set up OCSP // setOCSPOptions(mPrefBranch); + mHttpForNSS.initTable(); + mHttpForNSS.registerHttpClient(); + InstallLoadableRoots(); LaunchSmartCardThreads(); @@ -1499,6 +1524,7 @@ nsNSSComponent::ShutdownNSS() mNSSInitialized = PR_FALSE; PK11_SetPasswordFunc((PK11PasswordFunc)nsnull); + mHttpForNSS.unregisterHttpClient(); if (mPrefBranch) { nsCOMPtr pbi = do_QueryInterface(mPrefBranch); @@ -1530,6 +1556,14 @@ nsNSSComponent::Init() nsresult rv = NS_OK; PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("Beginning NSS initialization\n")); + + if (!mutex || !mShutdownObjectList || + !mSSLThread || !mCertVerificationThread) + { + PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("NSS init, out of memory in constructor\n")); + return NS_ERROR_OUT_OF_MEMORY; + } + rv = InitializePIPNSSBundle(); if (NS_FAILED(rv)) { PR_LOG(gPIPNSSLog, PR_LOG_ERROR, ("Unable to create pipnss bundle.\n")); @@ -1744,13 +1778,8 @@ nsNSSComponent::RandomUpdate(void *entropy, PRInt32 bufLen) return NS_OK; } -#define DEBUG_PSM_PROFILE - -#ifdef DEBUG_PSM_PROFILE #define PROFILE_CHANGE_NET_TEARDOWN_TOPIC "profile-change-net-teardown" #define PROFILE_CHANGE_NET_RESTORE_TOPIC "profile-change-net-restore" -#endif - #define PROFILE_APPROVE_CHANGE_TOPIC "profile-approve-change" #define PROFILE_CHANGE_TEARDOWN_TOPIC "profile-change-teardown" #define PROFILE_CHANGE_TEARDOWN_VETO_TOPIC "profile-change-teardown-veto" @@ -1762,10 +1791,6 @@ NS_IMETHODIMP nsNSSComponent::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *someData) { -#ifdef DEBUG - static PRBool isNetworkDown = PR_FALSE; -#endif - if (nsCRT::strcmp(aTopic, PROFILE_APPROVE_CHANGE_TOPIC) == 0) { if (mShutdownObjectList->isUIActive()) { ShowAlert(ai_crypto_ui_active); @@ -1801,9 +1826,7 @@ nsNSSComponent::Observe(nsISupports *aSubject, const char *aTopic, } else if (nsCRT::strcmp(aTopic, PROFILE_BEFORE_CHANGE_TOPIC) == 0) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("receiving profile change topic\n")); -#ifdef DEBUG - NS_ASSERTION(isNetworkDown, "nsNSSComponent relies on profile manager to wait for synchronous shutdown of all network activity"); -#endif + NS_ASSERTION(mIsNetworkDown, "nsNSSComponent relies on profile manager to wait for synchronous shutdown of all network activity"); PRBool needsCleanup = PR_TRUE; @@ -1918,17 +1941,22 @@ nsNSSComponent::Observe(nsISupports *aSubject, const char *aTopic, } } } - -#ifdef DEBUG else if (nsCRT::strcmp(aTopic, PROFILE_CHANGE_NET_TEARDOWN_TOPIC) == 0) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("receiving network teardown topic\n")); - isNetworkDown = PR_TRUE; + if (mSSLThread) + mSSLThread->requestExit(); + if (mCertVerificationThread) + mCertVerificationThread->requestExit(); + mIsNetworkDown = PR_TRUE; } else if (nsCRT::strcmp(aTopic, PROFILE_CHANGE_NET_RESTORE_TOPIC) == 0) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("receiving network restore topic\n")); - isNetworkDown = PR_FALSE; + delete mSSLThread; + mSSLThread = new nsSSLThread(); + delete mCertVerificationThread; + mCertVerificationThread = new nsCertVerificationThread(); + mIsNetworkDown = PR_FALSE; } -#endif return NS_OK; } @@ -2019,10 +2047,8 @@ nsNSSComponent::RegisterObservers() observerService->AddObserver(this, PROFILE_BEFORE_CHANGE_TOPIC, PR_FALSE); observerService->AddObserver(this, PROFILE_AFTER_CHANGE_TOPIC, PR_FALSE); observerService->AddObserver(this, SESSION_LOGOUT_TOPIC, PR_FALSE); -#ifdef DEBUG observerService->AddObserver(this, PROFILE_CHANGE_NET_TEARDOWN_TOPIC, PR_FALSE); observerService->AddObserver(this, PROFILE_CHANGE_NET_RESTORE_TOPIC, PR_FALSE); -#endif } return NS_OK; } @@ -2370,8 +2396,8 @@ PSMContentDownloader::OnDataAvailable(nsIRequest* request, do { err = aIStream->Read(mByteData+mBufferOffset, aLength, &amt); - if (amt == 0) break; if (NS_FAILED(err)) return err; + if (amt == 0) break; aLength -= amt; mBufferOffset += amt; diff --git a/security/manager/ssl/src/nsNSSComponent.h b/security/manager/ssl/src/nsNSSComponent.h index caaeb644b3b5..39eb65ed98ee 100644 --- a/security/manager/ssl/src/nsNSSComponent.h +++ b/security/manager/ssl/src/nsNSSComponent.h @@ -25,6 +25,7 @@ * Doug Turner * Brian Ryner * Kai Engert + * Kai Engert * * 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 @@ -63,6 +64,7 @@ #include "prlock.h" #include "nsICryptoHash.h" #include "hasht.h" +#include "nsNSSCallbacks.h" #include "nsNSSHelper.h" @@ -177,6 +179,8 @@ private: struct PRLock; class nsNSSShutDownList; +class nsSSLThread; +class nsCertVerificationThread; // Implementation of the PSM component interface. class nsNSSComponent : public nsISignatureVerifier, @@ -269,6 +273,10 @@ private: static int mInstanceCount; nsNSSShutDownList *mShutdownObjectList; SmartCardThreadList *mThreadList; + PRBool mIsNetworkDown; + nsSSLThread *mSSLThread; + nsCertVerificationThread *mCertVerificationThread; + nsNSSHttpInterface mHttpForNSS; }; class PSMContentListener : public nsIURIContentListener, diff --git a/security/manager/ssl/src/nsNSSIOLayer.cpp b/security/manager/ssl/src/nsNSSIOLayer.cpp index 1b62b2a58108..9d6e4c079765 100644 --- a/security/manager/ssl/src/nsNSSIOLayer.cpp +++ b/security/manager/ssl/src/nsNSSIOLayer.cpp @@ -23,6 +23,7 @@ * Contributor(s): * Brian Ryner * Javier Delgadillo + * Kai Engert * * 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 @@ -63,6 +64,8 @@ #include "nsHashSets.h" #include "nsCRT.h" #include "nsPrintfCString.h" +#include "nsAutoLock.h" +#include "nsSSLThread.h" #include "nsNSSShutDown.h" #include "nsNSSCertHelper.h" @@ -99,11 +102,6 @@ nsNSS_SSLGetClientAuthData(void *arg, PRFileDesc *socket, CERTDistNames *caNames, CERTCertificate **pRetCert, SECKEYPrivateKey **pRetKey); -static PRBool firstTime = PR_TRUE; -static PRDescIdentity nsSSLIOLayerIdentity; -static PRIOMethods nsSSLIOLayerMethods; -static nsCStringHashSet *gTLSIntolerantSites = nsnull; - #ifdef PR_LOGGING extern PRLogModuleInfo* gPIPNSSLog; #endif @@ -135,6 +133,45 @@ void MyLogFunction(const char *fmt, ...) #endif +nsSSLSocketThreadData::nsSSLSocketThreadData() +: mSSLState(ssl_idle) +, mPRErrorCode(PR_SUCCESS) +, mSSLDataBuffer(nsnull) +, mSSLDataBufferAllocatedSize(0) +, mSSLRequestedTransferAmount(0) +, mSSLRemainingReadResultData(nsnull) +, mSSLResultRemainingBytes(0) +, mReplacedSSLFileDesc(nsnull) +{ +} + +nsSSLSocketThreadData::~nsSSLSocketThreadData() +{ + NS_ASSERTION(mSSLState != ssl_pending_write + && + mSSLState != ssl_pending_read, + "oops??? ssl socket is not idle at the time it is being destroyed"); +} + +PRBool nsSSLSocketThreadData::ensure_buffer_size(PRInt32 amount) +{ + if (amount > mSSLDataBufferAllocatedSize) { + if (mSSLDataBuffer) { + mSSLDataBuffer = (char*)nsMemory::Realloc(mSSLDataBuffer, amount); + } + else { + mSSLDataBuffer = (char*)nsMemory::Alloc(amount); + } + + if (!mSSLDataBuffer) + return PR_FALSE; + + mSSLDataBufferAllocatedSize = amount; + } + + return PR_TRUE; +} + nsNSSSocketInfo::nsNSSSocketInfo() : mFd(nsnull), mSecurityState(nsIWebProgressListener::STATE_IS_INSECURE), @@ -145,11 +182,14 @@ nsNSSSocketInfo::nsNSSSocketInfo() mHandshakeInProgress(PR_FALSE), mPort(0), mCAChain(nsnull) -{ +{ + mThreadData = new nsSSLSocketThreadData; } nsNSSSocketInfo::~nsNSSSocketInfo() { + delete mThreadData; + nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return; @@ -369,11 +409,10 @@ nsresult nsNSSSocketInfo::ActivateSSL() if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; - if (SECSuccess != SSL_OptionSet(mFd, SSL_SECURITY, PR_TRUE)) - return NS_ERROR_FAILURE; - - if (SECSuccess != SSL_ResetHandshake(mFd, PR_FALSE)) - return NS_ERROR_FAILURE; + nsresult rv = nsSSLThread::requestActivateSSL(this); + + if (NS_FAILED(rv)) + return rv; mHandshakePending = PR_TRUE; @@ -422,14 +461,18 @@ nsresult nsNSSSocketInfo::SetSSLStatus(nsISSLStatus *aSSLStatus) return NS_OK; } -nsresult -nsSSLIOLayerFreeTLSIntolerantSites() +void nsSSLIOLayerHelpers::Cleanup() { - if (gTLSIntolerantSites) { - delete gTLSIntolerantSites; - gTLSIntolerantSites = nsnull; + if (mTLSIntolerantSites) { + delete mTLSIntolerantSites; + mTLSIntolerantSites = nsnull; } - return NS_OK; + + if (mSharedPollableEvent) + PR_DestroyPollableEvent(mSharedPollableEvent); + + if (mutex) + PR_DestroyLock(mutex); } static nsresult @@ -873,26 +916,14 @@ nsSSLIOLayerConnect(PRFileDesc* fd, const PRNetAddr* addr, return status; } -static PRInt32 PR_CALLBACK -nsSSLIOLayerAvailable(PRFileDesc *fd) -{ - nsNSSShutDownPreventionLock locker; - if (!fd || !fd->lower) - return PR_FAILURE; - - PRInt32 bytesAvailable = SSL_DataPending(fd->lower); - PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p] available %d bytes\n", (void*)fd, bytesAvailable)); - return bytesAvailable; -} - // Call this function to report a site that is possibly TLS intolerant. // This function will return true, if the given socket is currently using TLS. -static PRBool -rememberPossibleTLSProblemSite(PRFileDesc* fd, nsNSSSocketInfo *socketInfo) +PRBool +nsSSLIOLayerHelpers::rememberPossibleTLSProblemSite(PRFileDesc* ssl_layer_fd, nsNSSSocketInfo *socketInfo) { PRBool currentlyUsesTLS = PR_FALSE; - SSL_OptionGet(fd->lower, SSL_ENABLE_TLS, ¤tlyUsesTLS); + SSL_OptionGet(ssl_layer_fd, SSL_ENABLE_TLS, ¤tlyUsesTLS); if (currentlyUsesTLS) { // Add this site to the list of TLS intolerant sites. PRInt32 port; @@ -901,8 +932,8 @@ rememberPossibleTLSProblemSite(PRFileDesc* fd, nsNSSSocketInfo *socketInfo) socketInfo->GetHostName(getter_Copies(host)); nsCAutoString key; key = host + NS_LITERAL_CSTRING(":") + nsPrintfCString("%d", port); - // If it's in the set, that means it's TLS intolerant. - gTLSIntolerantSites->Put(key); + + addIntolerantSite(key); } return currentlyUsesTLS; @@ -916,24 +947,33 @@ nsSSLIOLayerClose(PRFileDesc *fd) return PR_FAILURE; PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p] Shutting down socket\n", (void*)fd)); + + nsNSSSocketInfo *socketInfo = (nsNSSSocketInfo*)fd->secret; + NS_ASSERTION(socketInfo,"nsNSSSocketInfo was null for an fd"); + + return nsSSLThread::requestClose(socketInfo); +} + +PRStatus nsNSSSocketInfo::CloseSocketAndDestroy() +{ + nsNSSShutDownPreventionLock locker; nsNSSShutDownList::trackSSLSocketClose(); - PRFileDesc* popped = PR_PopIOLayer(fd, PR_TOP_IO_LAYER); - nsNSSSocketInfo *infoObject = (nsNSSSocketInfo *)popped->secret; + PRFileDesc* popped = PR_PopIOLayer(mFd, PR_TOP_IO_LAYER); - if (infoObject->GetHandshakeInProgress()) { - rememberPossibleTLSProblemSite(fd, infoObject); + if (GetHandshakeInProgress()) { + nsSSLIOLayerHelpers::rememberPossibleTLSProblemSite(mFd->lower, this); } - PRStatus status = fd->methods->close(fd); + PRStatus status = mFd->methods->close(mFd); if (status != PR_SUCCESS) return status; popped->identity = PR_INVALID_IO_LAYER; - NS_RELEASE(infoObject); + NS_RELEASE_THIS(); popped->dtor(popped); - return status; + return PR_SUCCESS; } #if defined(DEBUG_SSL_VERBOSE) && defined(DUMP_BUFFER) @@ -1021,8 +1061,8 @@ isTLSIntoleranceError(PRInt32 err, PRBool withInitialCleartext) return PR_FALSE; } -static PRInt32 -checkHandshake(PRInt32 bytesTransfered, PRFileDesc* fd, nsNSSSocketInfo *socketInfo) +PRInt32 +nsSSLThread::checkHandshake(PRInt32 bytesTransfered, PRFileDesc* ssl_layer_fd, nsNSSSocketInfo *socketInfo) { // This is where we work around all of those SSL servers that don't // conform to the SSL spec and shutdown a connection when we request @@ -1062,7 +1102,7 @@ checkHandshake(PRInt32 bytesTransfered, PRFileDesc* fd, nsNSSSocketInfo *socketI // to retry without TLS. if (isTLSIntoleranceError(err, withInitialCleartext)) { - wantRetry = rememberPossibleTLSProblemSite(fd, socketInfo); + wantRetry = nsSSLIOLayerHelpers::rememberPossibleTLSProblemSite(ssl_layer_fd, socketInfo); if (wantRetry) { // We want to cause the network layer to retry the connection. @@ -1087,6 +1127,26 @@ checkHandshake(PRInt32 bytesTransfered, PRFileDesc* fd, nsNSSSocketInfo *socketI return bytesTransfered; } +static PRInt16 PR_CALLBACK +nsSSLIOLayerPoll(PRFileDesc *fd, PRInt16 in_flags, PRInt16 *out_flags) +{ + nsNSSShutDownPreventionLock locker; + + if (out_flags) + *out_flags = 0; + + if (!fd) + { + NS_WARNING("nsSSLIOLayerPoll called with null fd"); + return 0; + } + + nsNSSSocketInfo *socketInfo = (nsNSSSocketInfo*)fd->secret; + NS_ASSERTION(socketInfo,"nsNSSSocketInfo was null for an fd"); + + return nsSSLThread::requestPoll(socketInfo, in_flags, out_flags); +} + static PRInt32 PR_CALLBACK nsSSLIOLayerRead(PRFileDesc* fd, void* buf, PRInt32 amount) { @@ -1095,26 +1155,10 @@ nsSSLIOLayerRead(PRFileDesc* fd, void* buf, PRInt32 amount) return PR_FAILURE; } - nsNSSSocketInfo *socketInfo = nsnull; - socketInfo = (nsNSSSocketInfo*)fd->secret; + nsNSSSocketInfo *socketInfo = (nsNSSSocketInfo*)fd->secret; NS_ASSERTION(socketInfo,"nsNSSSocketInfo was null for an fd"); - if (socketInfo->isPK11LoggedOut() || socketInfo->isAlreadyShutDown()) { - PR_SetError(PR_SOCKET_SHUTDOWN_ERROR, 0); - return -1; - } - - if (socketInfo->GetCanceled()) { - return PR_FAILURE; - } - - PRInt32 bytesRead = fd->lower->methods->read(fd->lower, buf, amount); -#ifdef DEBUG_SSL_VERBOSE - PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p] read %d bytes\n", (void*)fd, bytesRead)); - DEBUG_DUMP_BUFFER((unsigned char*)buf, bytesRead); -#endif - - return checkHandshake(bytesRead, fd, socketInfo); + return nsSSLThread::requestRead(socketInfo, buf, amount); } static PRInt32 PR_CALLBACK @@ -1128,37 +1172,213 @@ nsSSLIOLayerWrite(PRFileDesc* fd, const void* buf, PRInt32 amount) #ifdef DEBUG_SSL_VERBOSE DEBUG_DUMP_BUFFER((unsigned char*)buf, amount); #endif - nsNSSSocketInfo *socketInfo = nsnull; - socketInfo = (nsNSSSocketInfo*)fd->secret; + nsNSSSocketInfo *socketInfo = (nsNSSSocketInfo*)fd->secret; NS_ASSERTION(socketInfo,"nsNSSSocketInfo was null for an fd"); - if (socketInfo->isPK11LoggedOut() || socketInfo->isAlreadyShutDown()) { - PR_SetError(PR_SOCKET_SHUTDOWN_ERROR, 0); - return -1; - } + return nsSSLThread::requestWrite(socketInfo, buf, amount); +} - if (socketInfo->GetCanceled()) { +PRDescIdentity nsSSLIOLayerHelpers::nsSSLIOLayerIdentity; +PRIOMethods nsSSLIOLayerHelpers::nsSSLIOLayerMethods; +PRLock *nsSSLIOLayerHelpers::mutex = nsnull; +nsCStringHashSet *nsSSLIOLayerHelpers::mTLSIntolerantSites = nsnull; +PRFileDesc *nsSSLIOLayerHelpers::mSharedPollableEvent = nsnull; +nsNSSSocketInfo *nsSSLIOLayerHelpers::mSocketOwningPollableEvent = nsnull; +PRBool nsSSLIOLayerHelpers::mPollableEventCurrentlySet = PR_FALSE; + +static PRIntn _PSM_InvalidInt(void) +{ + PR_ASSERT(!"I/O method is invalid"); + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return -1; +} + +static PRInt64 _PSM_InvalidInt64(void) +{ + PR_ASSERT(!"I/O method is invalid"); + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return -1; +} + +static PRStatus _PSM_InvalidStatus(void) +{ + PR_ASSERT(!"I/O method is invalid"); + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return PR_FAILURE; +} + +static PRFileDesc *_PSM_InvalidDesc(void) +{ + PR_ASSERT(!"I/O method is invalid"); + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return NULL; +} + +static PRStatus PR_CALLBACK PSMGetsockname(PRFileDesc *fd, PRNetAddr *addr) +{ + nsNSSShutDownPreventionLock locker; + if (!fd || !fd->lower) { return PR_FAILURE; } - PRInt32 bytesWritten = fd->lower->methods->write(fd->lower, buf, amount); -#ifdef DEBUG_SSL_VERBOSE - PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p] wrote %d bytes\n", (void*)fd, bytesWritten)); -#endif + nsNSSSocketInfo *socketInfo = (nsNSSSocketInfo*)fd->secret; + NS_ASSERTION(socketInfo,"nsNSSSocketInfo was null for an fd"); - return checkHandshake(bytesWritten, fd, socketInfo); + return nsSSLThread::requestGetsockname(socketInfo, addr); } -static void InitNSSMethods() +static PRStatus PR_CALLBACK PSMGetpeername(PRFileDesc *fd, PRNetAddr *addr) +{ + nsNSSShutDownPreventionLock locker; + if (!fd || !fd->lower) { + return PR_FAILURE; + } + + nsNSSSocketInfo *socketInfo = (nsNSSSocketInfo*)fd->secret; + NS_ASSERTION(socketInfo,"nsNSSSocketInfo was null for an fd"); + + return nsSSLThread::requestGetpeername(socketInfo, addr); +} + +static PRStatus PR_CALLBACK PSMGetsocketoption(PRFileDesc *fd, + PRSocketOptionData *data) +{ + nsNSSShutDownPreventionLock locker; + if (!fd || !fd->lower) { + return PR_FAILURE; + } + + nsNSSSocketInfo *socketInfo = (nsNSSSocketInfo*)fd->secret; + NS_ASSERTION(socketInfo,"nsNSSSocketInfo was null for an fd"); + + return nsSSLThread::requestGetsocketoption(socketInfo, data); +} + +static PRStatus PR_CALLBACK PSMSetsocketoption(PRFileDesc *fd, + const PRSocketOptionData *data) +{ + nsNSSShutDownPreventionLock locker; + if (!fd || !fd->lower) { + return PR_FAILURE; + } + + nsNSSSocketInfo *socketInfo = (nsNSSSocketInfo*)fd->secret; + NS_ASSERTION(socketInfo,"nsNSSSocketInfo was null for an fd"); + + return nsSSLThread::requestSetsocketoption(socketInfo, data); +} + +static PRInt32 PR_CALLBACK PSMRecv(PRFileDesc *fd, void *buf, PRInt32 amount, + PRIntn flags, PRIntervalTime timeout) +{ + nsNSSShutDownPreventionLock locker; + if (!fd || !fd->lower) { + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0); + return -1; + } + + if (flags != PR_MSG_PEEK) + { + PR_SetError(PR_UNKNOWN_ERROR, 0); + return -1; + } + + nsNSSSocketInfo *socketInfo = (nsNSSSocketInfo*)fd->secret; + NS_ASSERTION(socketInfo,"nsNSSSocketInfo was null for an fd"); + + return nsSSLThread::requestRecvMsgPeek(socketInfo, buf, amount, flags, timeout); +} + +static PRInt32 PR_CALLBACK PSMSend(PRFileDesc *fd, const void *buf, PRInt32 amount, + PRIntn flags, PRIntervalTime timeout) +{ + nsNSSShutDownPreventionLock locker; + if (!fd || !fd->lower) { + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0); + return -1; + } + + PR_SetError(PR_UNKNOWN_ERROR, 0); + return -1; +} + +static PRStatus PR_CALLBACK PSMConnectcontinue(PRFileDesc *fd, PRInt16 out_flags) +{ + nsNSSShutDownPreventionLock locker; + if (!fd || !fd->lower) { + return PR_FAILURE; + } + + nsNSSSocketInfo *socketInfo = (nsNSSSocketInfo*)fd->secret; + NS_ASSERTION(socketInfo,"nsNSSSocketInfo was null for an fd"); + + return nsSSLThread::requestConnectcontinue(socketInfo, out_flags); +} + +nsresult nsSSLIOLayerHelpers::Init() { nsSSLIOLayerIdentity = PR_GetUniqueIdentity("NSS layer"); nsSSLIOLayerMethods = *PR_GetDefaultIOMethods(); - + + nsSSLIOLayerMethods.available = (PRAvailableFN)_PSM_InvalidInt; + nsSSLIOLayerMethods.available64 = (PRAvailable64FN)_PSM_InvalidInt64; + nsSSLIOLayerMethods.fsync = (PRFsyncFN)_PSM_InvalidStatus; + nsSSLIOLayerMethods.seek = (PRSeekFN)_PSM_InvalidInt; + nsSSLIOLayerMethods.seek64 = (PRSeek64FN)_PSM_InvalidInt64; + nsSSLIOLayerMethods.fileInfo = (PRFileInfoFN)_PSM_InvalidStatus; + nsSSLIOLayerMethods.fileInfo64 = (PRFileInfo64FN)_PSM_InvalidStatus; + nsSSLIOLayerMethods.writev = (PRWritevFN)_PSM_InvalidInt; + nsSSLIOLayerMethods.accept = (PRAcceptFN)_PSM_InvalidDesc; + nsSSLIOLayerMethods.bind = (PRBindFN)_PSM_InvalidStatus; + nsSSLIOLayerMethods.listen = (PRListenFN)_PSM_InvalidStatus; + nsSSLIOLayerMethods.shutdown = (PRShutdownFN)_PSM_InvalidStatus; + nsSSLIOLayerMethods.recvfrom = (PRRecvfromFN)_PSM_InvalidInt; + nsSSLIOLayerMethods.sendto = (PRSendtoFN)_PSM_InvalidInt; + nsSSLIOLayerMethods.acceptread = (PRAcceptreadFN)_PSM_InvalidInt; + nsSSLIOLayerMethods.transmitfile = (PRTransmitfileFN)_PSM_InvalidInt; + nsSSLIOLayerMethods.sendfile = (PRSendfileFN)_PSM_InvalidInt; + + nsSSLIOLayerMethods.getsockname = PSMGetsockname; + nsSSLIOLayerMethods.getpeername = PSMGetpeername; + nsSSLIOLayerMethods.getsocketoption = PSMGetsocketoption; + nsSSLIOLayerMethods.setsocketoption = PSMSetsocketoption; + nsSSLIOLayerMethods.recv = PSMRecv; + nsSSLIOLayerMethods.send = PSMSend; + nsSSLIOLayerMethods.connectcontinue = PSMConnectcontinue; + nsSSLIOLayerMethods.connect = nsSSLIOLayerConnect; nsSSLIOLayerMethods.close = nsSSLIOLayerClose; - nsSSLIOLayerMethods.available = nsSSLIOLayerAvailable; nsSSLIOLayerMethods.write = nsSSLIOLayerWrite; nsSSLIOLayerMethods.read = nsSSLIOLayerRead; + nsSSLIOLayerMethods.poll = nsSSLIOLayerPoll; + + mutex = PR_NewLock(); + if (!mutex) + return NS_ERROR_OUT_OF_MEMORY; + + mSharedPollableEvent = PR_NewPollableEvent(); + if (!mSharedPollableEvent) + return NS_ERROR_OUT_OF_MEMORY; + + mTLSIntolerantSites = new nsCStringHashSet(); + if (!mTLSIntolerantSites) + return NS_ERROR_OUT_OF_MEMORY; + + mTLSIntolerantSites->Init(1); + + return NS_OK; +} + +void nsSSLIOLayerHelpers::addIntolerantSite(const nsCString &str) +{ + nsAutoLock lock(mutex); + nsSSLIOLayerHelpers::mTLSIntolerantSites->Put(str); +} + +PRBool nsSSLIOLayerHelpers::isKnownAsIntolerantSite(const nsCString &str) +{ + nsAutoLock lock(mutex); + return mTLSIntolerantSites->Contains(str); } nsresult @@ -1171,15 +1391,6 @@ nsSSLIOLayerNewSocket(PRInt32 family, nsISupports** info, PRBool forSTARTTLS) { - // XXX - this code is duplicated in nsSSLIOLayerAddToSocket - if (firstTime) { - InitNSSMethods(); - gTLSIntolerantSites = new nsCStringHashSet(); - if (!gTLSIntolerantSites) - return NS_ERROR_OUT_OF_MEMORY; - gTLSIntolerantSites->Init(1); - firstTime = PR_FALSE; - } PRFileDesc* sock = PR_OpenTCPSocket(family); if (!sock) return NS_ERROR_OUT_OF_MEMORY; @@ -2280,7 +2491,8 @@ nsSSLIOLayerSetOptions(PRFileDesc *fd, PRBool forSTARTTLS, // TLS intolerant. nsCAutoString key; key = nsDependentCString(host) + NS_LITERAL_CSTRING(":") + nsPrintfCString("%d", port); - if (gTLSIntolerantSites->Contains(key) && + + if (nsSSLIOLayerHelpers::isKnownAsIntolerantSite(key) && SECSuccess != SSL_OptionSet(fd, SSL_ENABLE_TLS, PR_FALSE)) { return NS_ERROR_FAILURE; } @@ -2318,16 +2530,6 @@ nsSSLIOLayerAddToSocket(PRInt32 family, PRFileDesc* layer = nsnull; nsresult rv; - // XXX - this code is duplicated in nsSSLIONewSocket - if (firstTime) { - InitNSSMethods(); - gTLSIntolerantSites = new nsCStringHashSet(); - if (!gTLSIntolerantSites) - return NS_ERROR_OUT_OF_MEMORY; - gTLSIntolerantSites->Init(1); - firstTime = PR_FALSE; - } - nsNSSSocketInfo* infoObject = new nsNSSSocketInfo(); if (!infoObject) return NS_ERROR_FAILURE; @@ -2351,8 +2553,8 @@ nsSSLIOLayerAddToSocket(PRInt32 family, goto loser; /* Now, layer ourselves on top of the SSL socket... */ - layer = PR_CreateIOLayerStub(nsSSLIOLayerIdentity, - &nsSSLIOLayerMethods); + layer = PR_CreateIOLayerStub(nsSSLIOLayerHelpers::nsSSLIOLayerIdentity, + &nsSSLIOLayerHelpers::nsSSLIOLayerMethods); if (!layer) goto loser; diff --git a/security/manager/ssl/src/nsNSSIOLayer.h b/security/manager/ssl/src/nsNSSIOLayer.h index ffb153456a41..f7a75d7c1908 100644 --- a/security/manager/ssl/src/nsNSSIOLayer.h +++ b/security/manager/ssl/src/nsNSSIOLayer.h @@ -22,6 +22,7 @@ * * Contributor(s): * Brian Ryner + * Kai Engert * * 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 @@ -54,6 +55,65 @@ #include "nsNSSShutDown.h" class nsIChannel; +class nsSSLThread; + +/* + * This class is used to store SSL socket I/O state information, + * that is not being executed directly, but defered to + * the separate SSL thread. + */ +class nsSSLSocketThreadData +{ +public: + nsSSLSocketThreadData(); + ~nsSSLSocketThreadData(); + + PRBool ensure_buffer_size(PRInt32 amount); + + enum ssl_state { + ssl_idle, // not in use by SSL thread, no activity pending + ssl_pending_write, // waiting for SSL thread to complete writing + ssl_pending_read, // waiting for SSL thread to complete reading + ssl_writing_done, // SSL write completed, results are ready + ssl_reading_done // SSL read completed, results are ready + }; + + ssl_state mSSLState; + + // Used to transport I/O error codes between SSL thread + // and initial caller thread. + PRErrorCode mPRErrorCode; + + // A buffer used to transfer I/O data between threads + char *mSSLDataBuffer; + PRInt32 mSSLDataBufferAllocatedSize; + + // The amount requested to read or write by the caller. + PRInt32 mSSLRequestedTransferAmount; + + // A pointer into our buffer, to the first byte + // that has not yet been delivered to the caller. + // Necessary, as the caller of the read function + // might request smaller chunks. + const char *mSSLRemainingReadResultData; + + // The caller previously requested to read or write. + // As the initial request to read or write is defered, + // the caller might (in theory) request smaller chunks + // in subsequent calls. + // This variable stores the amount of bytes successfully + // transfered, that have not yet been reported to the caller. + PRInt32 mSSLResultRemainingBytes; + + // When defering SSL read/write activity to another thread, + // we switch the SSL level file descriptor of the original + // layered file descriptor to a pollable event, + // so we can wake up the original caller of the I/O function + // as soon as data is ready. + // This variable is used to save the SSL level file descriptor, + // to allow us to restore the original file descriptor layering. + PRFileDesc *mReplacedSSLFileDesc; +}; class nsNSSSocketInfo : public nsITransportSecurityInfo, public nsISSLSocketControl, @@ -103,7 +163,9 @@ public: /* Set SSL Status values */ nsresult SetSSLStatus(nsISSLStatus *aSSLStatus); - + + PRStatus CloseSocketAndDestroy(); + protected: nsCOMPtr mCallbacks; PRFileDesc* mFd; @@ -122,9 +184,39 @@ protected: nsCOMPtr mSSLStatus; nsresult ActivateSSL(); + + nsSSLSocketThreadData *mThreadData; + private: virtual void virtualDestroyNSSReference(); void destructorSafeDestroyNSSReference(); + +friend class nsSSLThread; +}; + +class nsCStringHashSet; + +class nsSSLIOLayerHelpers +{ +public: + static nsresult Init(); + static void Cleanup(); + + static PRDescIdentity nsSSLIOLayerIdentity; + static PRIOMethods nsSSLIOLayerMethods; + + static PRLock *mutex; + static nsCStringHashSet *mTLSIntolerantSites; + + static PRBool rememberPossibleTLSProblemSite(PRFileDesc* fd, nsNSSSocketInfo *socketInfo); + + static void addIntolerantSite(const nsCString &str); + static PRBool isKnownAsIntolerantSite(const nsCString &str); + + static PRFileDesc *mSharedPollableEvent; + static nsNSSSocketInfo *mSocketOwningPollableEvent; + + static PRBool mPollableEventCurrentlySet; }; nsresult nsSSLIOLayerNewSocket(PRInt32 family,