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/public/nsICMSMessage2.idl b/security/manager/ssl/public/nsICMSMessage2.idl new file mode 100644 index 000000000000..888f635479c2 --- /dev/null +++ b/security/manager/ssl/public/nsICMSMessage2.idl @@ -0,0 +1,97 @@ +/* ***** 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 + * Red Hat, Inc. + * Portions created by the Initial Developer are Copyright (C) 2006 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * 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 + * 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 "nsISupports.idl" + +interface nsISMimeVerificationListener; + +[ptr] native UnsignedCharPtr(unsigned char); + +/* + * This interface is currently not marked scriptable, + * because its verification functions are meant to look like those + * in nsICMSMessage. At the time the ptr type is eliminated in both + * interfaces, both should be made scriptable. + */ + +[uuid(a99a3203-39e3-45e1-909c-175b0e471c2b)] +interface nsICMSMessage2 : nsISupports +{ + /** + * Async version of nsICMSMessage::VerifySignature. + * Code will be executed on a background thread and + * availability of results will be notified using a + * call to nsISMimeVerificationListener. + */ + void asyncVerifySignature(in nsISMimeVerificationListener listener); + + /** + * Async version of nsICMSMessage::VerifyDetachedSignature. + * Code will be executed on a background thread and + * availability of results will be notified using a + * call to nsISMimeVerificationListener. + * + * We are using "native unsigned char" ptr, because the function + * signatures of this one and nsICMSMessage::verifyDetachedSignature + * should be the identical. Cleaning up nsICMSMessages needs to be + * postponed, because this async version is needed on MOZILLA_1_8_BRANCH. + * + * Once both interfaces get cleaned up, the function signature should + * look like: + * [array, length_is(aDigestDataLen)] + * in octet aDigestData, + * in unsigned long aDigestDataLen); + */ + void asyncVerifyDetachedSignature(in nsISMimeVerificationListener listener, + in UnsignedCharPtr aDigestData, + in unsigned long aDigestDataLen); +}; + +[uuid(56310af6-dffc-48b4-abca-85eae4059064)] +interface nsISMimeVerificationListener : nsISupports { + + /** + * Notify that results are ready, that have been requested + * using nsICMSMessage2::asyncVerify[Detached]Signature() + * + * verificationResultCode matches synchronous result code from + * nsICMSMessage::verify[Detached]Signature + */ + void notify(in nsICMSMessage2 verifiedMessage, + in nsresult verificationResultCode); +}; + diff --git a/security/manager/ssl/public/nsIX509Cert3.idl b/security/manager/ssl/public/nsIX509Cert3.idl new file mode 100644 index 000000000000..9ce589ac1ca0 --- /dev/null +++ b/security/manager/ssl/public/nsIX509Cert3.idl @@ -0,0 +1,100 @@ +/* ***** 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 + * Red Hat, Inc. + * Portions created by the Initial Developer are Copyright (C) 2006 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * 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 + * 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 "nsISupports.idl" + +interface nsIX509Cert; +interface nsICertVerificationListener; + +/** + * Extending nsIX509Cert + * + * TODO: nsIX509Cert3 should be derived from nsIX509Cert2 + * (and nsIX509Cert2 derived from nsIX509Cert) + */ +[scriptable, uuid(402aee39-653c-403f-8be1-6d1824223bf9)] +interface nsIX509Cert3 : nsISupports { + + /** + * Async version of nsIX509Cert::getUsagesArray() + * + * Will not block, will request results asynchronously, + * availability of results will be notified. + */ + void requestUsagesArrayAsync(in nsICertVerificationListener cvl); +}; + +[scriptable, uuid(2fd0a785-9f2d-4327-8871-8c3e0783891d)] +interface nsICertVerificationResult : nsISupports { + + /** + * This interface reflects a container of + * verification results. Call will not block. + * + * Obtain an array of human readable strings describing + * the certificate's certified usages. + * + * Mirrors the results produced by + * nsIX509Cert::getUsagesArray() + * + * As of today, this function is a one-shot object, + * only the first call will succeed. + * This allows an optimization in the implementation, + * ownership of result data will be transfered to caller. + * + * @param cert The certificate that was verified. + * @param verified The certificate verification result, + * see constants in nsIX509Cert. + * @param count The number of human readable usages returned. + * @param usages The array of human readable usages. + */ + void getUsagesArrayResult(out PRUint32 verified, + out PRUint32 count, + [array, size_is(count)] out wstring usages); +}; + + +[scriptable, uuid(6684bce9-50db-48e1-81b7-98102bf81357)] +interface nsICertVerificationListener : nsISupports { + + /** + * Notify that results are ready, that have been requested + * using nsIX509Cert3::requestUsagesArrayAsync() + */ + void notify(in nsIX509Cert3 verifiedCert, + in nsICertVerificationResult result); +}; 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/nsCertVerificationThread.cpp b/security/manager/ssl/src/nsCertVerificationThread.cpp new file mode 100644 index 000000000000..4df572ff5a21 --- /dev/null +++ b/security/manager/ssl/src/nsCertVerificationThread.cpp @@ -0,0 +1,210 @@ +/* ***** 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 + * Red Hat, Inc. + * Portions created by the Initial Developer are Copyright (C) 2006 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * 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 + * 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 "nsMemory.h" +#include "nsAutoLock.h" +#include "nsAutoPtr.h" +#include "nsCertVerificationThread.h" + +nsCertVerificationThread *nsCertVerificationThread::verification_thread_singleton; + +NS_IMPL_THREADSAFE_ISUPPORTS1(nsCertVerificationResult, nsICertVerificationResult) + +void nsCertVerificationJob::Run() +{ + if (!mListener || !mCert) + return; + + PRUint32 verified; + PRUint32 count; + PRUnichar **usages; + + nsCOMPtr ires; + nsRefPtr vres = new nsCertVerificationResult; + if (vres) + { + nsresult rv = mCert->GetUsagesArray(PR_FALSE, // do not ignore OCSP + &verified, + &count, + &usages); + vres->mRV = rv; + if (NS_SUCCEEDED(rv)) + { + vres->mVerified = verified; + vres->mCount = count; + vres->mUsages = usages; + } + + ires = vres; + } + + nsCOMPtr c3 = do_QueryInterface(mCert); + mListener->Notify(c3, ires); +} + +void nsSMimeVerificationJob::Run() +{ + if (!mMessage || !mListener) + return; + + nsresult rv; + + if (digest_data) + rv = mMessage->VerifyDetachedSignature(digest_data, digest_len); + else + rv = mMessage->VerifySignature(); + + nsCOMPtr m2 = do_QueryInterface(mMessage); + mListener->Notify(m2, rv); +} + +nsCertVerificationThread::nsCertVerificationThread() +: mJobQ(nsnull) +{ + NS_ASSERTION(!verification_thread_singleton, + "nsCertVerificationThread is a singleton, caller attempts" + " to create another instance!"); + + verification_thread_singleton = this; + + NS_ASSERTION(mThreadHandle, "Could not create nsThreadRunner thread\n"); +} + +nsCertVerificationThread::~nsCertVerificationThread() +{ + verification_thread_singleton = nsnull; +} + +nsresult nsCertVerificationThread::addJob(nsBaseVerificationJob *aJob) +{ + if (!aJob || !verification_thread_singleton) + return NS_ERROR_FAILURE; + + if (!verification_thread_singleton->mThreadHandle) + return NS_ERROR_OUT_OF_MEMORY; + + nsAutoLock threadLock(verification_thread_singleton->mMutex); + + verification_thread_singleton->mJobQ.Push(aJob); + PR_NotifyAllCondVar(verification_thread_singleton->mCond); + + return NS_OK; +} + +#define CONDITION_WAIT_TIME PR_TicksPerSecond() / 4 + +void nsCertVerificationThread::Run(void) +{ + const PRIntervalTime wait_time = CONDITION_WAIT_TIME; + + while (PR_TRUE) { + + nsBaseVerificationJob *job = nsnull; + + { + nsAutoLock threadLock(verification_thread_singleton->mMutex); + + while (!mExitRequested && (0 == verification_thread_singleton->mJobQ.GetSize())) { + // no work to do ? let's wait a moment + + PR_WaitCondVar(mCond, wait_time); + } + + if (mExitRequested) + break; + + job = NS_STATIC_CAST(nsBaseVerificationJob*, mJobQ.PopFront()); + } + + if (job) + { + job->Run(); + delete job; + } + } + + { + nsAutoLock threadLock(verification_thread_singleton->mMutex); + + while (verification_thread_singleton->mJobQ.GetSize()) { + nsCertVerificationJob *job = + NS_STATIC_CAST(nsCertVerificationJob*, mJobQ.PopFront()); + delete job; + } + } +} + +nsCertVerificationResult::nsCertVerificationResult() +: mRV(0), + mVerified(0), + mCount(0), + mUsages(0) +{ +} + +nsCertVerificationResult::~nsCertVerificationResult() +{ + if (mUsages) + { + NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(mCount, mUsages); + } +} + +NS_IMETHODIMP +nsCertVerificationResult::GetUsagesArrayResult(PRUint32 *aVerified, + PRUint32 *aCount, + PRUnichar ***aUsages) +{ + if (NS_FAILED(mRV)) + return mRV; + + // transfer ownership + + *aVerified = mVerified; + *aCount = mCount; + *aUsages = mUsages; + + mVerified = 0; + mCount = 0; + mUsages = 0; + + nsresult rv = mRV; + + mRV = NS_ERROR_FAILURE; // this object works only once... + + return rv; +} diff --git a/security/manager/ssl/src/nsCertVerificationThread.h b/security/manager/ssl/src/nsCertVerificationThread.h new file mode 100644 index 000000000000..ba87d2889fd1 --- /dev/null +++ b/security/manager/ssl/src/nsCertVerificationThread.h @@ -0,0 +1,62 @@ +/* ***** 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 + * Red Hat, Inc. + * Portions created by the Initial Developer are Copyright (C) 2006 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * 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 + * 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 ***** */ + +#ifndef _NSCERTVERIFICATIONTHREAD_H_ +#define _NSCERTVERIFICATIONTHREAD_H_ + +#include "nsCOMPtr.h" +#include "nsDeque.h" +#include "nsPSMBackgroundThread.h" +#include "nsVerificationJob.h" + +class nsCertVerificationThread : public nsPSMBackgroundThread +{ +private: + nsDeque mJobQ; + + virtual void Run(void); + +public: + nsCertVerificationThread(); + ~nsCertVerificationThread(); + + static nsCertVerificationThread *verification_thread_singleton; + + static nsresult addJob(nsBaseVerificationJob *aJob); +}; + +#endif 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, diff --git a/security/manager/ssl/src/nsPSMBackgroundThread.cpp b/security/manager/ssl/src/nsPSMBackgroundThread.cpp new file mode 100644 index 000000000000..59a282a5fed9 --- /dev/null +++ b/security/manager/ssl/src/nsPSMBackgroundThread.cpp @@ -0,0 +1,91 @@ +/* ***** 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 + * Red Hat, Inc. + * Portions created by the Initial Developer are Copyright (C) 2006 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * 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 + * 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 "nsPSMBackgroundThread.h" +#include "nsAutoLock.h" + +void PR_CALLBACK nsPSMBackgroundThread::nsThreadRunner(void *arg) +{ + nsPSMBackgroundThread *self = NS_STATIC_CAST(nsPSMBackgroundThread *, arg); + self->Run(); +} + +nsPSMBackgroundThread::nsPSMBackgroundThread() +: mThreadHandle(nsnull), + mMutex(nsnull), + mCond(nsnull), + mExitRequested(PR_FALSE) +{ + mMutex = PR_NewLock(); + mCond = PR_NewCondVar(mMutex); + + if (mMutex && mCond) + { + mThreadHandle = PR_CreateThread(PR_USER_THREAD, nsThreadRunner, NS_STATIC_CAST(void*, this), + PR_PRIORITY_NORMAL, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0); + + NS_ASSERTION(mThreadHandle, "Could not create nsPSMBackgroundThread\n"); + } +} + +nsPSMBackgroundThread::~nsPSMBackgroundThread() +{ + if (mCond) + PR_DestroyCondVar(mCond); + + if (mMutex) + PR_DestroyLock(mMutex); +} + +void nsPSMBackgroundThread::requestExit() +{ + if (!mThreadHandle) + return; + + { + nsAutoLock threadLock(mMutex); + + if (mExitRequested) + return; + + mExitRequested = PR_TRUE; + PR_NotifyAllCondVar(mCond); + } + + PR_JoinThread(mThreadHandle); + mThreadHandle = nsnull; +} diff --git a/security/manager/ssl/src/nsPSMBackgroundThread.h b/security/manager/ssl/src/nsPSMBackgroundThread.h new file mode 100644 index 000000000000..554bf15071f0 --- /dev/null +++ b/security/manager/ssl/src/nsPSMBackgroundThread.h @@ -0,0 +1,72 @@ +/* ***** 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 + * Red Hat, Inc. + * Portions created by the Initial Developer are Copyright (C) 2006 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * 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 + * 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 ***** */ + +#ifndef _NSPSMBACKGROUNDTHREAD_H_ +#define _NSPSMBACKGROUNDTHREAD_H_ + +#include "nspr.h" + +class nsPSMBackgroundThread +{ +protected: + static void PR_CALLBACK nsThreadRunner(void *arg); + virtual void Run(void) = 0; + + // used to join the thread + PRThread *mThreadHandle; + + // Shared mutex used for condition variables, + // and to protect access to mExitRequested. + // Derived classes may use it to protect additional + // resources. + PRLock *mMutex; + + // Used to signal the thread's Run loop + PRCondVar *mCond; + + // Has termination of the SSL thread been requested? + PRBool mExitRequested; + +public: + nsPSMBackgroundThread(); + virtual ~nsPSMBackgroundThread(); + + void requestExit(); +}; + + +#endif diff --git a/security/manager/ssl/src/nsSSLThread.cpp b/security/manager/ssl/src/nsSSLThread.cpp new file mode 100644 index 000000000000..7c4cd3b7767d --- /dev/null +++ b/security/manager/ssl/src/nsSSLThread.cpp @@ -0,0 +1,891 @@ +/* ***** 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 + * Red Hat, Inc. + * Portions created by the Initial Developer are Copyright (C) 2006 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * 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 + * 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 "nsIThread.h" +#include "nsSSLThread.h" +#include "nsAutoLock.h" +#include "nsNSSIOLayer.h" + +#include "ssl.h" + +nsSSLThread::nsSSLThread() +: mBusySocket(nsnull), + mSocketScheduledToBeDestroyed(nsnull), + mPendingHTTPRequest(nsnull) +{ + NS_ASSERTION(!ssl_thread_singleton, "nsSSLThread is a singleton, caller attempts to create another instance!"); + + ssl_thread_singleton = this; + + NS_ASSERTION(mThreadHandle, "Could not create nsSSLThreadRunner thread\n"); +} + +nsSSLThread::~nsSSLThread() +{ + ssl_thread_singleton = nsnull; +} + +PRFileDesc *nsSSLThread::getRealSSLFD(nsNSSSocketInfo *si) +{ + if (!ssl_thread_singleton || !si || !ssl_thread_singleton->mThreadHandle) + return nsnull; + + nsAutoLock threadLock(ssl_thread_singleton->mMutex); + + if (si->mThreadData->mReplacedSSLFileDesc) + { + return si->mThreadData->mReplacedSSLFileDesc; + } + else + { + return si->mFd->lower; + } +} + +PRStatus nsSSLThread::requestGetsockname(nsNSSSocketInfo *si, PRNetAddr *addr) +{ + PRFileDesc *fd = getRealSSLFD(si); + if (!fd) + return PR_FAILURE; + + return fd->methods->getsockname(fd, addr); +} + +PRStatus nsSSLThread::requestGetpeername(nsNSSSocketInfo *si, PRNetAddr *addr) +{ + PRFileDesc *fd = getRealSSLFD(si); + if (!fd) + return PR_FAILURE; + + return fd->methods->getpeername(fd, addr); +} + +PRStatus nsSSLThread::requestGetsocketoption(nsNSSSocketInfo *si, + PRSocketOptionData *data) +{ + PRFileDesc *fd = getRealSSLFD(si); + if (!fd) + return PR_FAILURE; + + return fd->methods->getsocketoption(fd, data); +} + +PRStatus nsSSLThread::requestSetsocketoption(nsNSSSocketInfo *si, + const PRSocketOptionData *data) +{ + PRFileDesc *fd = getRealSSLFD(si); + if (!fd) + return PR_FAILURE; + + return fd->methods->setsocketoption(fd, data); +} + +PRStatus nsSSLThread::requestConnectcontinue(nsNSSSocketInfo *si, + PRInt16 out_flags) +{ + PRFileDesc *fd = getRealSSLFD(si); + if (!fd) + return PR_FAILURE; + + return fd->methods->connectcontinue(fd, out_flags); +} + +PRInt32 nsSSLThread::requestRecvMsgPeek(nsNSSSocketInfo *si, void *buf, PRInt32 amount, + PRIntn flags, PRIntervalTime timeout) +{ + if (!ssl_thread_singleton || !si || !ssl_thread_singleton->mThreadHandle) + { + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0); + return -1; + } + + PRFileDesc *realSSLFD; + + { + nsAutoLock threadLock(ssl_thread_singleton->mMutex); + + if (si == ssl_thread_singleton->mBusySocket) + { + PORT_SetError(PR_WOULD_BLOCK_ERROR); + return -1; + } + + switch (si->mThreadData->mSSLState) + { + case nsSSLSocketThreadData::ssl_idle: + break; + + case nsSSLSocketThreadData::ssl_reading_done: + { + // we have data available that we can return + + // if there was a failure, just return the failure, + // but do not yet clear our state, that should happen + // in the call to "read". + + if (si->mThreadData->mSSLResultRemainingBytes < 0) { + if (si->mThreadData->mPRErrorCode != PR_SUCCESS) { + PR_SetError(si->mThreadData->mPRErrorCode, 0); + } + + return si->mThreadData->mSSLResultRemainingBytes; + } + + PRInt32 return_amount = NS_MIN(amount, si->mThreadData->mSSLResultRemainingBytes); + + memcpy(buf, si->mThreadData->mSSLRemainingReadResultData, return_amount); + + return return_amount; + } + + case nsSSLSocketThreadData::ssl_writing_done: + case nsSSLSocketThreadData::ssl_pending_write: + case nsSSLSocketThreadData::ssl_pending_read: + + // for safety reasons, also return would_block on any other state, + // although this switch statement should be complete and list + // the appropriate behaviour for each state. + default: + { + PORT_SetError(PR_WOULD_BLOCK_ERROR); + return -1; + } + } + + if (si->mThreadData->mReplacedSSLFileDesc) + { + realSSLFD = si->mThreadData->mReplacedSSLFileDesc; + } + else + { + realSSLFD = si->mFd->lower; + } + } + + return realSSLFD->methods->recv(realSSLFD, buf, amount, flags, timeout); +} + +nsresult nsSSLThread::requestActivateSSL(nsNSSSocketInfo *si) +{ + PRFileDesc *fd = getRealSSLFD(si); + if (!fd) + return NS_ERROR_FAILURE; + + if (SECSuccess != SSL_OptionSet(fd, SSL_SECURITY, PR_TRUE)) + return NS_ERROR_FAILURE; + + if (SECSuccess != SSL_ResetHandshake(fd, PR_FALSE)) + return NS_ERROR_FAILURE; + + return NS_OK; +} + +PRInt16 nsSSLThread::requestPoll(nsNSSSocketInfo *si, PRInt16 in_flags, PRInt16 *out_flags) +{ + if (!ssl_thread_singleton || !si || !ssl_thread_singleton->mThreadHandle) + return 0; + + *out_flags = 0; + + { + nsAutoLock threadLock(ssl_thread_singleton->mMutex); + + if (si == ssl_thread_singleton->mBusySocket) + { + switch (si->mThreadData->mSSLState) + { + case nsSSLSocketThreadData::ssl_writing_done: + { + if (in_flags & PR_POLL_WRITE) + { + *out_flags |= PR_POLL_WRITE; + } + + return in_flags; + } + break; + + case nsSSLSocketThreadData::ssl_reading_done: + { + if (in_flags & PR_POLL_READ) + { + *out_flags |= PR_POLL_READ; + } + + return in_flags; + } + break; + + case nsSSLSocketThreadData::ssl_pending_write: + case nsSSLSocketThreadData::ssl_pending_read: + { + // The lower layer of the socket is currently the pollable event, + // which signals the readable state. + + return PR_POLL_READ; + } + + case nsSSLSocketThreadData::ssl_idle: + default: + break; + } + } + } + + return si->mFd->lower->methods->poll(si->mFd->lower, in_flags, out_flags); +} + +PRStatus nsSSLThread::requestClose(nsNSSSocketInfo *si) +{ + if (!ssl_thread_singleton || !si) + return PR_FAILURE; + + PRBool close_later = PR_FALSE; + nsIRequest* requestToCancel = nsnull; + + { + nsAutoLock threadLock(ssl_thread_singleton->mMutex); + + if (ssl_thread_singleton->mBusySocket == si) { + + // That's tricky, SSL thread is currently busy with this socket, + // and might even be blocked on it (UI or OCSP). + // We should not close the socket directly, but rather + // schedule closing it, at the time the SSL thread is done. + // If there is indeed a depending OCSP request pending, + // we should cancel it now. + + if (ssl_thread_singleton->mPendingHTTPRequest) + { + requestToCancel = ssl_thread_singleton->mPendingHTTPRequest; + ssl_thread_singleton->mPendingHTTPRequest = nsnull; + } + + close_later = PR_TRUE; + ssl_thread_singleton->mSocketScheduledToBeDestroyed = si; + } + } + + if (requestToCancel) + { + if (nsIThread::IsMainThread()) + { + requestToCancel->Cancel(NS_ERROR_ABORT); + } + else + { + NS_WARNING("Attempt to close SSL socket from a thread that is not the main thread. Can not cancel pending HTTP request from NSS"); + } + + NS_RELEASE(requestToCancel); + } + + if (!close_later) + { + return si->CloseSocketAndDestroy(); + } + + return PR_SUCCESS; +} + +void nsSSLThread::restoreOriginalSocket_locked(nsNSSSocketInfo *si) +{ + if (si->mThreadData->mReplacedSSLFileDesc) + { + if (nsSSLIOLayerHelpers::mPollableEventCurrentlySet) + { + nsSSLIOLayerHelpers::mPollableEventCurrentlySet = PR_FALSE; + PR_WaitForPollableEvent(nsSSLIOLayerHelpers::mSharedPollableEvent); + } + + // need to restore + si->mFd->lower = si->mThreadData->mReplacedSSLFileDesc; + si->mThreadData->mReplacedSSLFileDesc = nsnull; + nsSSLIOLayerHelpers::mSocketOwningPollableEvent = nsnull; + } +} + +PRInt32 nsSSLThread::requestRead(nsNSSSocketInfo *si, void *buf, PRInt32 amount) +{ + if (!ssl_thread_singleton || !si || !buf || !amount || !ssl_thread_singleton->mThreadHandle) + { + PR_SetError(PR_UNKNOWN_ERROR, 0); + return -1; + } + + PRBool this_socket_is_busy = PR_FALSE; + PRBool some_other_socket_is_busy = PR_FALSE; + nsSSLSocketThreadData::ssl_state my_ssl_state; + + { + nsAutoLock threadLock(ssl_thread_singleton->mMutex); + + if (ssl_thread_singleton->mExitRequested) { + PR_SetError(PR_UNKNOWN_ERROR, 0); + return -1; + } + + my_ssl_state = si->mThreadData->mSSLState; + + if (ssl_thread_singleton->mBusySocket == si) + { + this_socket_is_busy = PR_TRUE; + + if (my_ssl_state == nsSSLSocketThreadData::ssl_reading_done) + { + // we will now care for the data that's ready, + // the socket is no longer busy on the ssl thread + + restoreOriginalSocket_locked(si); + + ssl_thread_singleton->mBusySocket = nsnull; + + // We'll handle the results further down, + // while not holding the lock. + } + } + else if (ssl_thread_singleton->mBusySocket) + { + some_other_socket_is_busy = PR_TRUE; + } + } + + switch (my_ssl_state) + { + case nsSSLSocketThreadData::ssl_idle: + { + NS_ASSERTION(!this_socket_is_busy, "oops, unexpected incosistency"); + + if (some_other_socket_is_busy) + { + PORT_SetError(PR_WOULD_BLOCK_ERROR); + return -1; + } + + // ssl thread is not busy, we'll continue below + } + break; + + case nsSSLSocketThreadData::ssl_reading_done: + // there has been a previous request to read, that is now done! + { + // failure ? + if (si->mThreadData->mSSLResultRemainingBytes < 0) { + if (si->mThreadData->mPRErrorCode != PR_SUCCESS) { + PR_SetError(si->mThreadData->mPRErrorCode, 0); + si->mThreadData->mPRErrorCode = PR_SUCCESS; + } + + si->mThreadData->mSSLState = nsSSLSocketThreadData::ssl_idle; + return si->mThreadData->mSSLResultRemainingBytes; + } + + PRInt32 return_amount = NS_MIN(amount, si->mThreadData->mSSLResultRemainingBytes); + + memcpy(buf, si->mThreadData->mSSLRemainingReadResultData, return_amount); + + si->mThreadData->mSSLResultRemainingBytes -= return_amount; + + if (!si->mThreadData->mSSLResultRemainingBytes) { + si->mThreadData->mSSLRemainingReadResultData = nsnull; + si->mThreadData->mSSLState = nsSSLSocketThreadData::ssl_idle; + } + else { + si->mThreadData->mSSLRemainingReadResultData += return_amount; + } + + return return_amount; + } + // we never arrive here, see return statement above + break; + + + // We should not see the following events here, + // because we have not yet signaled Necko that we are + // readable/writable again, so if we end up here, + // it means that Necko decided to try read/write again, + // for whatever reason. No problem, just return would_block, + case nsSSLSocketThreadData::ssl_pending_write: + case nsSSLSocketThreadData::ssl_pending_read: + + // We should not see this state here, because Necko has previously + // requested us to write, Necko is not yet aware that it's done, + // (although it meanwhile is), but Necko now tries to read? + // If that ever happens, it's confusing, but not a problem, + // just let Necko know we can not do that now and return would_block. + case nsSSLSocketThreadData::ssl_writing_done: + + // for safety reasons, also return would_block on any other state, + // although this switch statement should be complete and list + // the appropriate behaviour for each state. + default: + { + PORT_SetError(PR_WOULD_BLOCK_ERROR); + return -1; + } + // we never arrive here, see return statement above + break; + } + + if (si->isPK11LoggedOut() || si->isAlreadyShutDown()) { + PR_SetError(PR_SOCKET_SHUTDOWN_ERROR, 0); + return -1; + } + + if (si->GetCanceled()) { + return PR_FAILURE; + } + + // si is idle and good, and no other socket is currently busy, + // so it's fine to continue with the request. + + if (!si->mThreadData->ensure_buffer_size(amount)) + { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return -1; + } + + si->mThreadData->mSSLRequestedTransferAmount = amount; + si->mThreadData->mSSLState = nsSSLSocketThreadData::ssl_pending_read; + + // Remember we are operating on a layered file descriptor, that consists of + // a PSM code layer (nsNSSIOLayer), a NSS code layer (SSL protocol logic), + // and the raw socket at the bottommost layer. + // + // We don't want to call the SSL layer read/write directly on this thread, + // because it might block, should a callback to UI (for user confirmation) + // or Necko (for retrieving OCSP verification data) be necessary. + // As Necko is single threaded, it is currently waiting for this + // function to return, and a callback into Necko from NSS couldn't succeed. + // + // Therefore we must defer the request to read/write to a separate SSL thread. + // We will return WOULD_BLOCK to Necko, and will return the real results + // once the I/O operation on the SSL thread is ready. + // + // The tricky part is to wake up Necko, as soon as the I/O operation + // on the SSL thread is done. + // + // In order to achieve that, we manipulate the layering of the file + // descriptor. Usually the PSM layer points to the SSL layer as its lower + // layer. We change that to a pollable event file descriptor. + // + // Once we return from this original read/write function, Necko will + // poll/select on the file descriptor. As result data is not yet ready, we will + // instruct Necko to select on the bottommost file descriptor + // (by using appropriate flags in PSM's layer implementation of the + // poll method), which is the pollable event. + // + // Once the SSL thread is done with the call to the SSL layer, it will + // "set" the pollable event, causing Necko to wake up on the file descriptor + // and call read/write again. Now that the file descriptor is in the done state, + // we'll arrive in this read/write function again. We'll detect the socket is + // in the done state, and restore the original SSL level file descriptor. + // Finally, we return the data obtained on the SSL thread back to our caller. + + { + nsAutoLock threadLock(ssl_thread_singleton->mMutex); + + NS_ASSERTION(!nsSSLIOLayerHelpers::mSocketOwningPollableEvent, + "oops, some other socket still owns our shared pollable event"); + NS_ASSERTION(!si->mThreadData->mReplacedSSLFileDesc, "oops"); + + si->mThreadData->mReplacedSSLFileDesc = si->mFd->lower; + si->mFd->lower = nsSSLIOLayerHelpers::mSharedPollableEvent; + nsSSLIOLayerHelpers::mSocketOwningPollableEvent = si; + ssl_thread_singleton->mBusySocket = si; + + // notify the thread + PR_NotifyAllCondVar(ssl_thread_singleton->mCond); + } + + PORT_SetError(PR_WOULD_BLOCK_ERROR); + return -1; +} + +PRInt32 nsSSLThread::requestWrite(nsNSSSocketInfo *si, const void *buf, PRInt32 amount) +{ + if (!ssl_thread_singleton || !si || !buf || !amount || !ssl_thread_singleton->mThreadHandle) + { + PR_SetError(PR_UNKNOWN_ERROR, 0); + return -1; + } + + PRBool this_socket_is_busy = PR_FALSE; + PRBool some_other_socket_is_busy = PR_FALSE; + nsSSLSocketThreadData::ssl_state my_ssl_state; + + { + nsAutoLock threadLock(ssl_thread_singleton->mMutex); + + if (ssl_thread_singleton->mExitRequested) { + PR_SetError(PR_UNKNOWN_ERROR, 0); + return -1; + } + + my_ssl_state = si->mThreadData->mSSLState; + + if (ssl_thread_singleton->mBusySocket == si) + { + this_socket_is_busy = PR_TRUE; + + if (my_ssl_state == nsSSLSocketThreadData::ssl_writing_done) + { + // we will now care for the data that's ready, + // the socket is no longer busy on the ssl thread + + restoreOriginalSocket_locked(si); + + ssl_thread_singleton->mBusySocket = nsnull; + + // We'll handle the results further down, + // while not holding the lock. + } + } + else if (ssl_thread_singleton->mBusySocket) + { + some_other_socket_is_busy = PR_TRUE; + } + } + + switch (my_ssl_state) + { + case nsSSLSocketThreadData::ssl_idle: + { + NS_ASSERTION(!this_socket_is_busy, "oops, unexpected incosistency"); + + if (some_other_socket_is_busy) + { + PORT_SetError(PR_WOULD_BLOCK_ERROR); + return -1; + } + + // ssl thread is not busy, we'll continue below + } + break; + + case nsSSLSocketThreadData::ssl_writing_done: + // there has been a previous request to write, that is now done! + { + // failure ? + if (si->mThreadData->mSSLResultRemainingBytes < 0) { + if (si->mThreadData->mPRErrorCode != PR_SUCCESS) { + PR_SetError(si->mThreadData->mPRErrorCode, 0); + si->mThreadData->mPRErrorCode = PR_SUCCESS; + } + + si->mThreadData->mSSLState = nsSSLSocketThreadData::ssl_idle; + return si->mThreadData->mSSLResultRemainingBytes; + } + + PRInt32 return_amount = NS_MIN(amount, si->mThreadData->mSSLResultRemainingBytes); + + si->mThreadData->mSSLResultRemainingBytes -= return_amount; + + if (!si->mThreadData->mSSLResultRemainingBytes) { + si->mThreadData->mSSLState = nsSSLSocketThreadData::ssl_idle; + } + + return return_amount; + } + break; + + // We should not see the following events here, + // because we have not yet signaled Necko that we are + // readable/writable again, so if we end up here, + // it means that Necko decided to try read/write again, + // for whatever reason. No problem, just return would_block, + case nsSSLSocketThreadData::ssl_pending_write: + case nsSSLSocketThreadData::ssl_pending_read: + + // We should not see this state here, because Necko has previously + // requested us to read, Necko is not yet aware that it's done, + // (although it meanwhile is), but Necko now tries to write? + // If that ever happens, it's confusing, but not a problem, + // just let Necko know we can not do that now and return would_block. + case nsSSLSocketThreadData::ssl_reading_done: + + // for safety reasons, also return would_block on any other state, + // although this switch statement should be complete and list + // the appropriate behaviour for each state. + default: + { + PORT_SetError(PR_WOULD_BLOCK_ERROR); + return -1; + } + // we never arrive here, see return statement above + break; + } + + if (si->isPK11LoggedOut() || si->isAlreadyShutDown()) { + PR_SetError(PR_SOCKET_SHUTDOWN_ERROR, 0); + return -1; + } + + if (si->GetCanceled()) { + return PR_FAILURE; + } + + // si is idle and good, and no other socket is currently busy, + // so it's fine to continue with the request. + + if (!si->mThreadData->ensure_buffer_size(amount)) + { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return -1; + } + + memcpy(si->mThreadData->mSSLDataBuffer, buf, amount); + si->mThreadData->mSSLRequestedTransferAmount = amount; + si->mThreadData->mSSLState = nsSSLSocketThreadData::ssl_pending_write; + + { + nsAutoLock threadLock(ssl_thread_singleton->mMutex); + + NS_ASSERTION(!nsSSLIOLayerHelpers::mSocketOwningPollableEvent, + "oops, some other socket still owns our shared pollable event"); + NS_ASSERTION(!si->mThreadData->mReplacedSSLFileDesc, "oops"); + + si->mThreadData->mReplacedSSLFileDesc = si->mFd->lower; + si->mFd->lower = nsSSLIOLayerHelpers::mSharedPollableEvent; + nsSSLIOLayerHelpers::mSocketOwningPollableEvent = si; + ssl_thread_singleton->mBusySocket = si; + + PR_NotifyAllCondVar(ssl_thread_singleton->mCond); + } + + PORT_SetError(PR_WOULD_BLOCK_ERROR); + return -1; +} + +void nsSSLThread::Run(void) +{ + // Helper variable, we don't want to call destroy + // while holding the mutex. + nsNSSSocketInfo *socketToDestroy = nsnull; + + while (PR_TRUE) + { + if (socketToDestroy) + { + socketToDestroy->CloseSocketAndDestroy(); + socketToDestroy = nsnull; + } + + // remember whether we'll write or read + nsSSLSocketThreadData::ssl_state busy_socket_ssl_state; + + { + // In this scope we need mutex protection, + // as we find out what needs to be done. + + nsAutoLock threadLock(ssl_thread_singleton->mMutex); + + if (mSocketScheduledToBeDestroyed) + { + if (mBusySocket == mSocketScheduledToBeDestroyed) + { + // That's rare, but it happens. + // We have received a request to close the socket, + // although I/O results have not yet been consumed. + + restoreOriginalSocket_locked(mBusySocket); + + mBusySocket->mThreadData->mSSLState = nsSSLSocketThreadData::ssl_idle; + mBusySocket = nsnull; + } + + socketToDestroy = mSocketScheduledToBeDestroyed; + mSocketScheduledToBeDestroyed = nsnull; + continue; // go back and finally destroy it, before doing anything else + } + + if (mExitRequested) + break; + + PRBool pending_work = PR_FALSE; + + do + { + if (mBusySocket + && + (mBusySocket->mThreadData->mSSLState == nsSSLSocketThreadData::ssl_pending_read + || + mBusySocket->mThreadData->mSSLState == nsSSLSocketThreadData::ssl_pending_write)) + { + pending_work = PR_TRUE; + } + + if (!pending_work) + { + // no work to do ? let's wait a moment + + PRIntervalTime wait_time = PR_TicksPerSecond() / 4; + PR_WaitCondVar(mCond, wait_time); + } + + } while (!pending_work && !mExitRequested && !mSocketScheduledToBeDestroyed); + + if (mSocketScheduledToBeDestroyed) + continue; + + if (mExitRequested) + break; + + if (!pending_work) + continue; + + busy_socket_ssl_state = mBusySocket->mThreadData->mSSLState; + } + + { + // In this scope we need to make sure NSS does not go away + // while we are busy. + + nsNSSShutDownPreventionLock locker; + + if (nsSSLSocketThreadData::ssl_pending_write == busy_socket_ssl_state) + { + PRInt32 bytesWritten = mBusySocket->mThreadData->mReplacedSSLFileDesc->methods + ->write(mBusySocket->mThreadData->mReplacedSSLFileDesc, + mBusySocket->mThreadData->mSSLDataBuffer, + mBusySocket->mThreadData->mSSLRequestedTransferAmount); + +#ifdef DEBUG_SSL_VERBOSE + PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p] wrote %d bytes\n", (void*)fd, bytesWritten)); +#endif + + bytesWritten = checkHandshake(bytesWritten, mBusySocket->mThreadData->mReplacedSSLFileDesc, mBusySocket); + if (bytesWritten < 0) { + // give the error back to caller + mBusySocket->mThreadData->mPRErrorCode = PR_GetError(); + } + + mBusySocket->mThreadData->mSSLResultRemainingBytes = bytesWritten; + busy_socket_ssl_state = nsSSLSocketThreadData::ssl_writing_done; + } + else if (nsSSLSocketThreadData::ssl_pending_read == busy_socket_ssl_state) + { + PRInt32 bytesRead = mBusySocket->mThreadData->mReplacedSSLFileDesc->methods + ->read(mBusySocket->mThreadData->mReplacedSSLFileDesc, + mBusySocket->mThreadData->mSSLDataBuffer, + mBusySocket->mThreadData->mSSLRequestedTransferAmount); + +#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 + bytesRead = checkHandshake(bytesRead, mBusySocket->mThreadData->mReplacedSSLFileDesc, mBusySocket); + if (bytesRead < 0) { + // give the error back to caller + mBusySocket->mThreadData->mPRErrorCode = PR_GetError(); + } + + mBusySocket->mThreadData->mSSLResultRemainingBytes = bytesRead; + mBusySocket->mThreadData->mSSLRemainingReadResultData = + mBusySocket->mThreadData->mSSLDataBuffer; + busy_socket_ssl_state = nsSSLSocketThreadData::ssl_reading_done; + } + } + + // avoid setting event repeatedly + PRBool needToSetPollableEvent = PR_FALSE; + + { + nsAutoLock threadLock(ssl_thread_singleton->mMutex); + + mBusySocket->mThreadData->mSSLState = busy_socket_ssl_state; + + if (!nsSSLIOLayerHelpers::mPollableEventCurrentlySet) + { + needToSetPollableEvent = PR_TRUE; + nsSSLIOLayerHelpers::mPollableEventCurrentlySet = PR_TRUE; + } + } + + if (needToSetPollableEvent) + { + // Wake up the file descriptor on the Necko thread, + // so it can fetch the results from the SSL I/O call + // that we just completed. + PR_SetPollableEvent(nsSSLIOLayerHelpers::mSharedPollableEvent); + } + } + + { + nsAutoLock threadLock(ssl_thread_singleton->mMutex); + if (mBusySocket) + { + restoreOriginalSocket_locked(mBusySocket); + mBusySocket = nsnull; + } + if (!nsSSLIOLayerHelpers::mPollableEventCurrentlySet) + { + nsSSLIOLayerHelpers::mPollableEventCurrentlySet = PR_TRUE; + PR_SetPollableEvent(nsSSLIOLayerHelpers::mSharedPollableEvent); + } + } +} + +void nsSSLThread::rememberPendingHTTPRequest(nsIRequest *aRequest) +{ + if (!ssl_thread_singleton) + return; + + nsAutoLock threadLock(ssl_thread_singleton->mMutex); + + NS_IF_ADDREF(aRequest); + ssl_thread_singleton->mPendingHTTPRequest = aRequest; +} + +void nsSSLThread::cancelPendingHTTPRequest() +{ + if (!ssl_thread_singleton) + return; + + nsAutoLock threadLock(ssl_thread_singleton->mMutex); + + if (ssl_thread_singleton->mPendingHTTPRequest) + { + ssl_thread_singleton->mPendingHTTPRequest->Cancel(NS_ERROR_ABORT); + + NS_RELEASE(ssl_thread_singleton->mPendingHTTPRequest); + + ssl_thread_singleton->mPendingHTTPRequest = nsnull; + } +} + +nsSSLThread *nsSSLThread::ssl_thread_singleton = nsnull; diff --git a/security/manager/ssl/src/nsSSLThread.h b/security/manager/ssl/src/nsSSLThread.h new file mode 100644 index 000000000000..2f6ef91f753f --- /dev/null +++ b/security/manager/ssl/src/nsSSLThread.h @@ -0,0 +1,146 @@ +/* ***** 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 + * Red Hat, Inc. + * Portions created by the Initial Developer are Copyright (C) 2006 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * 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 + * 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 ***** */ + +#ifndef _NSSSLTHREAD_H_ +#define _NSSSLTHREAD_H_ + +#include "nsCOMPtr.h" +#include "nsIRequest.h" +#include "nsPSMBackgroundThread.h" + +class nsNSSSocketInfo; +class nsIHttpChannel; + +class nsSSLThread : public nsPSMBackgroundThread +{ +private: + // We use mMutex contained in our base class + // to protect access to these variables: + // mBusySocket, mSocketScheduledToBeDestroyed + // and to nsSSLSocketThreadData::mSSLState + // while a socket is the busy socket. + + // We use mCond contained in our base class + // to notify the SSL thread that a new SSL I/O + // request has been queued for processing. + // It can be found in the mBusySocket variable, + // containing all details in its member. + + // A socket that is currently owned by the SSL thread + // and has pending SSL I/O activity or I/O results + // not yet fetched by the original caller. + nsNSSSocketInfo *mBusySocket; + + // A socket that should be closed and destroyed + // as soon as possible. The request was initiated by + // Necko, but it happened at a time when the SSL + // thread had ownership of the socket, so the request + // was delayed. It's now the responsibility of the + // SSL thread to close and destroy this socket. + nsNSSSocketInfo *mSocketScheduledToBeDestroyed; + + // Did we receive a request from NSS to fetch HTTP + // data on behalf of NSS? (Most likely this is a OCSP request) + // We track a handle to the HTTP request sent to Necko. + // As this HTTP request depends on some original SSL socket, + // we can use this handle to cancel the dependent HTTP request, + // should we be asked to close the original SSL socket. + nsIRequest* mPendingHTTPRequest; + + virtual void Run(void); + + // Called from SSL thread only + static PRInt32 checkHandshake(PRInt32 bytesTransfered, + PRFileDesc* fd, + nsNSSSocketInfo *socketInfo); + + // Function can be called from either Necko or SSL thread + // Caller must lock mMutex before this call. + static void restoreOriginalSocket_locked(nsNSSSocketInfo *si); + + // Helper for requestSomething functions, + // caled from the Necko thread only. + static PRFileDesc *getRealSSLFD(nsNSSSocketInfo *si); + + +public: + nsSSLThread(); + ~nsSSLThread(); + + static nsSSLThread *ssl_thread_singleton; + + // All requestSomething functions are called from + // the Necko thread only. + + static PRInt32 requestRead(nsNSSSocketInfo *si, + void *buf, + PRInt32 amount); + + static PRInt32 requestWrite(nsNSSSocketInfo *si, + const void *buf, + PRInt32 amount); + + static PRInt16 requestPoll(nsNSSSocketInfo *si, + PRInt16 in_flags, + PRInt16 *out_flags); + + static PRInt32 requestRecvMsgPeek(nsNSSSocketInfo *si, void *buf, PRInt32 amount, + PRIntn flags, PRIntervalTime timeout); + + static PRStatus requestClose(nsNSSSocketInfo *si); + + static PRStatus requestGetsockname(nsNSSSocketInfo *si, PRNetAddr *addr); + + static PRStatus requestGetpeername(nsNSSSocketInfo *si, PRNetAddr *addr); + + static PRStatus requestGetsocketoption(nsNSSSocketInfo *si, + PRSocketOptionData *data); + + static PRStatus requestSetsocketoption(nsNSSSocketInfo *si, + const PRSocketOptionData *data); + + static PRStatus requestConnectcontinue(nsNSSSocketInfo *si, + PRInt16 out_flags); + + static nsresult requestActivateSSL(nsNSSSocketInfo *si); + + // Called from either Necko or SSL thread. + static void rememberPendingHTTPRequest(nsIRequest *aRequest); + static void cancelPendingHTTPRequest(); +}; + +#endif //_NSSSLTHREAD_H_ diff --git a/security/manager/ssl/src/nsVerificationJob.h b/security/manager/ssl/src/nsVerificationJob.h new file mode 100644 index 000000000000..e7e70588eca1 --- /dev/null +++ b/security/manager/ssl/src/nsVerificationJob.h @@ -0,0 +1,100 @@ +/* -*- 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) 2001 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * 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 + * 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 ***** */ + +#ifndef _INC_NSVERIFICATIONJOB_H +#define _INC_NSVERIFICATIONJOB_H + +#include "nspr.h" + +#include "nsIX509Cert.h" +#include "nsIX509Cert3.h" +#include "nsICMSMessage.h" +#include "nsICMSMessage2.h" + +class nsBaseVerificationJob +{ +public: + virtual void Run() = 0; +}; + +class nsCertVerificationJob : public nsBaseVerificationJob +{ +public: + nsCOMPtr mCert; + nsCOMPtr mListener; + + void Run(); +}; + +class nsCertVerificationResult : public nsICertVerificationResult +{ +public: + nsCertVerificationResult(); + virtual ~nsCertVerificationResult(); + + NS_DECL_ISUPPORTS + NS_DECL_NSICERTVERIFICATIONRESULT + +private: + nsresult mRV; + PRUint32 mVerified; + PRUint32 mCount; + PRUnichar **mUsages; + +friend class nsCertVerificationJob; +}; + +class nsSMimeVerificationJob : public nsBaseVerificationJob +{ +public: + nsSMimeVerificationJob() { digest_data = nsnull; digest_len = 0; } + ~nsSMimeVerificationJob() { delete [] digest_data; } + + nsCOMPtr mMessage; + nsCOMPtr mListener; + + unsigned char *digest_data; + PRUint32 digest_len; + + void Run(); +}; + + + +#endif