bug 111384, Support OCSP requests through a proxy

combined r= by darin / rrelyea
This commit is contained in:
kaie%kuix.de 2006-04-04 06:41:37 +00:00
parent 03038ad819
commit b5aaffdfeb
23 changed files with 3065 additions and 131 deletions

View File

@ -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

View File

@ -22,6 +22,7 @@
* Bob Lord <lord@netscape.com>
* Ian McGreer <mcgreer@netscape.com>
* Javier Delgadillo <javi@netscape.com>
* Kai Engert <kengert@redhat.com>
*
* 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
}

View File

@ -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 \

View File

@ -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 <kengert@redhat.com>
*
* 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);
};

View File

@ -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 <kengert@redhat.com>
*
* 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);
};

View File

@ -57,6 +57,9 @@ LIBXUL_LIBRARY = 1
PACKAGE_FILE = pipnss.pkg
CPPSRCS = \
nsPSMBackgroundThread.cpp \
nsSSLThread.cpp \
nsCertVerificationThread.cpp \
nsCipherInfo.cpp \
nsNSSCallbacks.cpp \
nsNSSComponent.cpp \

View File

@ -20,6 +20,7 @@
* the Initial Developer. All Rights Reserved.
*
* Contributor(s): David Drinan <ddrinan@netscape.com>
* Kai Engert <kengert@redhat.com>
*
* 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:

View File

@ -20,6 +20,7 @@
* the Initial Developer. All Rights Reserved.
*
* Contributor(s): David Drinan <ddrinan@netscape.com>
* Kai Engert <kengert@redhat.com>
*
* 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

View File

@ -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 <kengert@redhat.com>
*
* 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<nsICertVerificationResult> ires;
nsRefPtr<nsCertVerificationResult> 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<nsIX509Cert3> 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<nsICMSMessage2> 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;
}

View File

@ -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 <kengert@redhat.com>
*
* 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

View File

@ -23,6 +23,7 @@
* Contributor(s):
* Brian Ryner <bryner@brianryner.com>
* Terry Hayes <thayes@netscape.com>
* Kai Engert <kengert@redhat.com>
*
* 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<nsHTTPListener> 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<nsIIOService> ios = do_GetIOService(&rv);
if (NS_FAILED(rv))
return;
nsCOMPtr<nsIChannel> 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<nsILoadGroup> loadGroup =
do_CreateInstance(NS_LOADGROUP_CONTRACTID);
chan->SetLoadGroup(loadGroup);
if (aEvent->mRequestSession->mHasPostData)
{
nsCOMPtr<nsIInputStream> uploadStream;
rv = NS_NewPostDataStream(getter_AddRefs(uploadStream),
PR_FALSE,
aEvent->mRequestSession->mPostData,
0, ios);
if (NS_FAILED(rv))
return;
nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(chan, &rv));
if (NS_FAILED(rv))
return;
rv = uploadChannel->SetUploadStream(uploadStream,
aEvent->mRequestSession->mPostContentType,
-1);
if (NS_FAILED(rv))
return;
}
nsCOMPtr<nsIHttpChannel> 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<nsINSSComponent> nssComponent(do_GetService(kNSSComponentCID, &rv));
if (NS_FAILED(rv))
return SECFailure;
nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
if (wwatch){
nsCOMPtr<nsIPrompt> 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<nsIEventQueue> 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<nsIRequest> req;
nsCOMPtr<nsIHttpChannel> 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

View File

@ -22,6 +22,7 @@
*
* Contributor(s):
* Brian Ryner <bryner@brianryner.com>
* Kai Engert <kengert@redhat.com>
*
* 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<nsIStreamLoader> 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<nsHTTPListener> 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_

View File

@ -21,6 +21,7 @@
* Contributor(s):
* Ian McGreer <mcgreer@netscape.com>
* Javier Delgadillo <javi@netscape.com>
* Kai Engert <kengert@redhat.com>
*
* 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,

View File

@ -21,6 +21,7 @@
* Contributor(s):
* Ian McGreer <mcgreer@netscape.com>
* Javier Delgadillo <javi@netscape.com>
* Kai Engert <kengert@redhat.com>
*
* 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);

View File

@ -26,6 +26,7 @@
* Mitch Stoltz <mstoltz@netscape.com>
* Brian Ryner <bryner@brianryner.com>
* Kai Engert <kaie@netscape.com>
* Kai Engert <kengert@redhat.com>
*
* 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<nsIPrefBranch2> 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;

View File

@ -25,6 +25,7 @@
* Doug Turner <dougt@netscape.com>
* Brian Ryner <bryner@brianryner.com>
* Kai Engert <kaie@netscape.com>
* Kai Engert <kengert@redhat.com>
*
* 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,

View File

@ -23,6 +23,7 @@
* Contributor(s):
* Brian Ryner <bryner@brianryner.com>
* Javier Delgadillo <javi@netscape.com>
* Kai Engert <kengert@redhat.com>
*
* 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, &currentlyUsesTLS);
SSL_OptionGet(ssl_layer_fd, SSL_ENABLE_TLS, &currentlyUsesTLS);
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;

View File

@ -22,6 +22,7 @@
*
* Contributor(s):
* Brian Ryner <bryner@brianryner.com>
* Kai Engert <kengert@redhat.com>
*
* 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<nsIInterfaceRequestor> mCallbacks;
PRFileDesc* mFd;
@ -122,9 +184,39 @@ protected:
nsCOMPtr<nsISSLStatus> 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,

View File

@ -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 <kengert@redhat.com>
*
* 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;
}

View File

@ -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 <kengert@redhat.com>
*
* 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

View File

@ -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 <kengert@redhat.com>
*
* 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;

View File

@ -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 <kengert@redhat.com>
*
* 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_

View File

@ -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 <kengert@redhat.com>
*
* 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<nsIX509Cert> mCert;
nsCOMPtr<nsICertVerificationListener> 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<nsICMSMessage> mMessage;
nsCOMPtr<nsISMimeVerificationListener> mListener;
unsigned char *digest_data;
PRUint32 digest_len;
void Run();
};
#endif