bug 111384, Support OCSP requests through a proxy

combined r= by darin / rrelyea
second checkin attempt
This commit is contained in:
kaie%kuix.de 2006-04-04 13:14:40 +00:00
Родитель ef483b51d5
Коммит 9b7392ffd9
14 изменённых файлов: 1296 добавлений и 131 удалений

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

@ -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"
@ -290,14 +293,33 @@ nsNSSComponent::nsNSSComponent()
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;

Просмотреть файл

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

Просмотреть файл

@ -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),
@ -146,10 +183,13 @@ nsNSSSocketInfo::nsNSSSocketInfo()
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;
nsresult rv = nsSSLThread::requestActivateSSL(this);
if (SECSuccess != SSL_ResetHandshake(mFd, PR_FALSE))
return NS_ERROR_FAILURE;
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;
@ -917,23 +948,32 @@ nsSSLIOLayerClose(PRFileDesc *fd)
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;

Просмотреть файл

@ -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,
@ -104,6 +164,8 @@ 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,