Bug 674147 (Remove the SSL Thread) Part 2: Everything else, r=honzab

This commit is contained in:
Brian Smith 2011-12-01 14:37:57 -08:00
parent c17a7182ed
commit 20c388403c
12 changed files with 1029 additions and 1951 deletions

View File

@ -54,6 +54,14 @@
#include "mozilla/FunctionTimer.h"
// XXX: There is no good header file to put these in. :(
namespace mozilla { namespace psm {
void InitializeSSLServerCertVerificationThreads();
void StopSSLServerCertVerificationThreads();
} } // namespace mozilla::psm
using namespace mozilla;
#if defined(PR_LOGGING)
@ -609,6 +617,8 @@ nsSocketTransportService::Run()
{
SOCKET_LOG(("STS thread init\n"));
psm::InitializeSSLServerCertVerificationThreads();
gSocketThread = PR_GetCurrentThread();
// add thread event to poll list (mThreadEvent may be NULL)
@ -665,6 +675,8 @@ nsSocketTransportService::Run()
gSocketThread = nsnull;
psm::StopSSLServerCertVerificationThreads();
SOCKET_LOG(("STS thread exit\n"));
return NS_OK;
}

View File

@ -60,7 +60,6 @@ CPPSRCS = \
nsRecentBadCerts.cpp \
nsClientAuthRemember.cpp \
nsPSMBackgroundThread.cpp \
nsSSLThread.cpp \
nsCertVerificationThread.cpp \
nsProtectedAuthThread.cpp \
nsNSSCallbacks.cpp \

View File

@ -40,44 +40,213 @@
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
/*
* All I/O is done on the socket transport thread, including all calls into
* libssl. That is, all SSL_* functions must be called on the socket transport
* thread. This also means that all SSL callback functions will be called on
* the socket transport thread, including in particular the auth certificate
* hook.
*
* During certificate authentication, we call CERT_PKIXVerifyCert or
* CERT_VerifyCert. These functions may make zero or more HTTP requests
* for OCSP responses, CRLs, intermediate certificates, etc.
*
* If our cert auth hook were to call the CERT_*Verify* functions directly,
* there would be a deadlock: The CERT_*Verify* function would cause an event
* to be asynchronously posted to the socket transport thread, and then it
* would block the socket transport thread waiting to be notified of the HTTP
* response. However, the HTTP request would never actually be processed
* because the socket transport thread would be blocked and so it wouldn't be
* able process HTTP requests. (i.e. Deadlock.)
*
* Consequently, we must always call the CERT_*Verify* cert functions off the
* socket transport thread. To accomplish this, our auth cert hook dispatches a
* SSLServerCertVerificationJob to a pool of background threads, and then
* immediatley return SECWouldBlock to libssl. These jobs are where the
* CERT_*Verify* functions are actually called.
*
* When our auth cert hook returns SECWouldBlock, libssl will carry on the
* handshake while we validate the certificate. This will free up the socket
* transport thread so that HTTP requests--in particular, the OCSP/CRL/cert
* requests needed for cert verification as mentioned above--can be processed.
*
* Once the CERT_*Verify* function returns, the cert verification job
* dispatches a SSLServerCertVerificationResult to the socket transport thread;
* the SSLServerCertVerificationResult will notify libssl that the certificate
* authentication is complete. Once libssl is notified that the authentication
* is complete, it will continue the SSL handshake (if it hasn't already
* finished) and it will begin allowing us to send/receive data on the
* connection.
*
* Timeline of events:
*
* * libssl calls SSLServerCertVerificationJob::Dispatch on the socket
* transport thread.
* * SSLServerCertVerificationJob::Dispatch queues a job
* (instance of SSLServerCertVerificationJob) to its background thread
* pool and returns.
* * One of the background threads calls CERT_*Verify*, which may enqueue
* some HTTP request(s) onto the socket transport thread, and then
* blocks that background thread waiting for the responses and/or timeouts
* or errors for those requests.
* * Once those HTTP responses have all come back or failed, the
* CERT_*Verify* function returns a result indicating that the validation
* succeeded or failed.
* * If the validation succeeded, then a SSLServerCertVerificationResult
* event is posted to the socket transport thread, and the cert
* verification thread becomes free to verify other certificates.
* * Otherwise, a CertErrorRunnable is posted to the socket transport thread
* and then to the main thread (blocking both, see CertErrorRunnable) to
* do cert override processing and bad cert listener notification. Then
* the cert verification thread becomes free to verify other certificates.
* * After processing cert overrides, the CertErrorRunnable will dispatch a
* SSLServerCertVerificationResult event to the socket transport thread to
* notify it of the result of the override processing; then it returns,
* freeing up the main thread.
* * The SSLServerCertVerificationResult event will either wake up the
* socket (using SSL_RestartHandshakeAfterServerCert) if validation
* succeeded or there was an error override, or it will set an error flag
* so that the next I/O operation on the socket will fail, causing the
* socket transport thread to close the connection.
*
* Cert override processing must happen on the main thread because it accesses
* the nsICertOverrideService, and that service must be accessed on the main
* thread because some extensions (Selenium, in particular) replace it with a
* Javascript implementation, and chrome JS must always be run on the main
* thread.
*
* SSLServerCertVerificationResult must be dispatched to the socket transport
* thread because we must only call SSL_* functions on the socket transport
* thread since they may do I/O, because many parts of nsNSSSocketInfo and
* the PSM NSS I/O layer are not thread-safe, and because we need the event to
* interrupt the PR_Poll that may waiting for I/O on the socket for which we
* are validating the cert.
*/
#include "SSLServerCertVerification.h"
#include "nsNSSComponent.h"
#include "nsNSSCertificate.h"
#include "nsNSSCleaner.h"
#include "nsNSSIOLayer.h"
#include "nsIThreadPool.h"
#include "nsXPCOMCIDInternal.h"
#include "nsComponentManagerUtils.h"
#include "nsServiceManagerUtils.h"
#include "ssl.h"
#include "cert.h"
#include "secerr.h"
#include "sslerr.h"
using namespace mozilla;
static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID);
NSSCleanupAutoPtrClass(CERTCertificate, CERT_DestroyCertificate)
#ifdef PR_LOGGING
extern PRLogModuleInfo* gPIPNSSLog;
#endif
namespace mozilla { namespace psm {
namespace {
// do not use a nsCOMPtr to avoid static initializer/destructor
nsIThreadPool * gCertVerificationThreadPool = nsnull;
} // unnamed namespace
// Called when the socket transport thread starts, to initialize the SSL cert
// verification thread pool. By tying the thread pool startup/shutdown directly
// to the STS thread's lifetime, we ensure that they are *always* available for
// SSL connections and that there are no races during startup and especially
// shutdown. (Previously, we have had multiple problems with races in PSM
// background threads, and the race-prevention/shutdown logic used there is
// brittle. Since this service is critical to things like downloading updates,
// we take no chances.) Also, by doing things this way, we avoid the need for
// locks, since gCertVerificationThreadPool is only ever accessed on the socket
// transport thread.
void
InitializeSSLServerCertVerificationThreads()
{
// TODO: tuning, make parameters preferences
// XXX: instantiate nsThreadPool directly, to make this more bulletproof.
// Currently, the nsThreadPool.h header isn't exported for us to do so.
nsresult rv = CallCreateInstance(NS_THREADPOOL_CONTRACTID,
&gCertVerificationThreadPool);
if (NS_FAILED(rv)) {
NS_WARNING("Failed to create SSL cert verification threads.");
return;
}
(void) gCertVerificationThreadPool->SetIdleThreadLimit(5);
(void) gCertVerificationThreadPool->SetIdleThreadTimeout(30 * 1000);
(void) gCertVerificationThreadPool->SetThreadLimit(5);
}
// Called when the socket transport thread finishes, to destroy the thread
// pool. Since the socket transport service has stopped processing events, it
// will not attempt any more SSL I/O operations, so it is clearly safe to shut
// down the SSL cert verification infrastructure. Also, the STS will not
// dispatch many SSL verification result events at this point, so any pending
// cert verifications will (correctly) fail at the point they are dispatched.
//
// The other shutdown race condition that is possible is a race condition with
// shutdown of the nsNSSComponent service. We use the
// nsNSSShutdownPreventionLock where needed (not here) to prevent that.
void StopSSLServerCertVerificationThreads()
{
if (gCertVerificationThreadPool) {
gCertVerificationThreadPool->Shutdown();
NS_RELEASE(gCertVerificationThreadPool);
}
}
namespace {
class SSLServerCertVerificationJob : public nsRunnable
{
public:
// Must be called only on the socket transport thread
static SECStatus Dispatch(const void * fdForLogging,
nsNSSSocketInfo * infoObject,
CERTCertificate * serverCert);
private:
NS_DECL_NSIRUNNABLE
// Must be called only on the socket transport thread
SSLServerCertVerificationJob(const void * fdForLogging,
nsNSSSocketInfo & socketInfo,
CERTCertificate & cert);
~SSLServerCertVerificationJob();
// Runs on one of the background threads
SECStatus AuthCertificate(const nsNSSShutDownPreventionLock & proofOfLock);
const void * const mFdForLogging;
const nsRefPtr<nsNSSSocketInfo> mSocketInfo;
CERTCertificate * const mCert;
};
SSLServerCertVerificationJob::SSLServerCertVerificationJob(
const void * fdForLogging, nsNSSSocketInfo & socketInfo,
CERTCertificate & cert)
: mFdForLogging(fdForLogging)
, mSocketInfo(&socketInfo)
, mCert(CERT_DupCertificate(&cert))
{
}
SSLServerCertVerificationJob::~SSLServerCertVerificationJob()
{
CERT_DestroyCertificate(mCert);
}
static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID);
SECStatus
PSM_SSL_PKIX_AuthCertificate(PRFileDesc *fd, CERTCertificate *peerCert, bool checksig, bool isServer)
PSM_SSL_PKIX_AuthCertificate(CERTCertificate *peerCert, void * pinarg,
const char * hostname,
const nsNSSShutDownPreventionLock & /*proofOfLock*/)
{
SECStatus rv;
SECCertUsage certUsage;
SECCertificateUsage certificateusage;
void * pinarg;
char * hostname;
pinarg = SSL_RevealPinArg(fd);
hostname = SSL_RevealURL(fd);
/* this may seem backwards, but isn't. */
certUsage = isServer ? certUsageSSLClient : certUsageSSLServer;
certificateusage = isServer ? certificateUsageSSLClient : certificateUsageSSLServer;
if (!nsNSSComponent::globalConstFlagUsePKIXVerification) {
rv = CERT_VerifyCertNow(CERT_GetDefaultCertDB(), peerCert, checksig, certUsage,
pinarg);
rv = CERT_VerifyCertNow(CERT_GetDefaultCertDB(), peerCert, true,
certUsageSSLServer, pinarg);
}
else {
nsresult nsrv;
@ -91,12 +260,12 @@ PSM_SSL_PKIX_AuthCertificate(PRFileDesc *fd, CERTCertificate *peerCert, bool che
CERTValOutParam cvout[1];
cvout[0].type = cert_po_end;
rv = CERT_PKIXVerifyCert(peerCert, certificateusage,
rv = CERT_PKIXVerifyCert(peerCert, certificateUsageSSLServer,
survivingParams->GetRawPointerForNSS(),
cvout, pinarg);
}
if ( rv == SECSuccess && !isServer ) {
if (rv == SECSuccess) {
/* cert is OK. This is the client side of an SSL connection.
* Now check the name field in the cert against the desired hostname.
* NB: This is our only defense against Man-In-The-Middle (MITM) attacks!
@ -109,7 +278,6 @@ PSM_SSL_PKIX_AuthCertificate(PRFileDesc *fd, CERTCertificate *peerCert, bool che
PORT_SetError(SSL_ERROR_BAD_CERT_DOMAIN);
}
PORT_Free(hostname);
return rv;
}
@ -197,22 +365,17 @@ PSM_SSL_BlacklistDigiNotar(CERTCertificate * serverCert,
return 0;
}
SECStatus PR_CALLBACK AuthCertificateCallback(void* client_data, PRFileDesc* fd,
PRBool checksig, PRBool isServer) {
nsNSSShutDownPreventionLock locker;
CERTCertificate *serverCert = SSL_PeerCertificate(fd);
CERTCertificateCleaner serverCertCleaner(serverCert);
if (serverCert &&
serverCert->serialNumber.data &&
serverCert->issuerName &&
!strcmp(serverCert->issuerName,
SECStatus
SSLServerCertVerificationJob::AuthCertificate(
nsNSSShutDownPreventionLock const & nssShutdownPreventionLock)
{
if (mCert->serialNumber.data &&
mCert->issuerName &&
!strcmp(mCert->issuerName,
"CN=UTN-USERFirst-Hardware,OU=http://www.usertrust.com,O=The USERTRUST Network,L=Salt Lake City,ST=UT,C=US")) {
unsigned char *server_cert_comparison_start = (unsigned char*)serverCert->serialNumber.data;
unsigned int server_cert_comparison_len = serverCert->serialNumber.len;
unsigned char *server_cert_comparison_start = mCert->serialNumber.data;
unsigned int server_cert_comparison_len = mCert->serialNumber.len;
while (server_cert_comparison_len) {
if (*server_cert_comparison_start != 0)
@ -244,87 +407,85 @@ SECStatus PR_CALLBACK AuthCertificateCallback(void* client_data, PRFileDesc* fd,
}
}
SECStatus rv = PSM_SSL_PKIX_AuthCertificate(fd, serverCert, checksig, isServer);
SECStatus rv = PSM_SSL_PKIX_AuthCertificate(mCert, mSocketInfo,
mSocketInfo->GetHostName(),
nssShutdownPreventionLock);
// We want to remember the CA certs in the temp db, so that the application can find the
// complete chain at any time it might need it.
// But we keep only those CA certs in the temp db, that we didn't already know.
if (serverCert) {
nsNSSSocketInfo* infoObject = (nsNSSSocketInfo*) fd->higher->secret;
nsRefPtr<nsSSLStatus> status = infoObject->SSLStatus();
nsRefPtr<nsNSSCertificate> nsc;
nsRefPtr<nsSSLStatus> status = mSocketInfo->SSLStatus();
nsRefPtr<nsNSSCertificate> nsc;
if (!status || !status->mServerCert) {
nsc = nsNSSCertificate::Create(serverCert);
if (!status || !status->mServerCert) {
nsc = nsNSSCertificate::Create(mCert);
}
CERTCertList *certList = nsnull;
certList = CERT_GetCertChainFromCert(mCert, PR_Now(), certUsageSSLCA);
if (!certList) {
rv = SECFailure;
} else {
PRErrorCode blacklistErrorCode;
if (rv == SECSuccess) { // PSM_SSL_PKIX_AuthCertificate said "valid cert"
blacklistErrorCode = PSM_SSL_BlacklistDigiNotar(mCert, certList);
} else { // PSM_SSL_PKIX_AuthCertificate said "invalid cert"
PRErrorCode savedErrorCode = PORT_GetError();
// Check if we want to worsen the error code to "revoked".
blacklistErrorCode = PSM_SSL_DigiNotarTreatAsRevoked(mCert, certList);
if (blacklistErrorCode == 0) {
// we don't worsen the code, let's keep the original error code from NSS
PORT_SetError(savedErrorCode);
}
}
CERTCertList *certList = nsnull;
certList = CERT_GetCertChainFromCert(serverCert, PR_Now(), certUsageSSLCA);
if (!certList) {
if (blacklistErrorCode != 0) {
mSocketInfo->SetCertIssuerBlacklisted();
PORT_SetError(blacklistErrorCode);
rv = SECFailure;
} else {
PRErrorCode blacklistErrorCode;
if (rv == SECSuccess) { // PSM_SSL_PKIX_AuthCertificate said "valid cert"
blacklistErrorCode = PSM_SSL_BlacklistDigiNotar(serverCert, certList);
} else { // PSM_SSL_PKIX_AuthCertificate said "invalid cert"
PRErrorCode savedErrorCode = PORT_GetError();
// Check if we want to worsen the error code to "revoked".
blacklistErrorCode = PSM_SSL_DigiNotarTreatAsRevoked(serverCert, certList);
if (blacklistErrorCode == 0) {
// we don't worsen the code, let's keep the original error code from NSS
PORT_SetError(savedErrorCode);
}
}
if (blacklistErrorCode != 0) {
infoObject->SetCertIssuerBlacklisted();
PORT_SetError(blacklistErrorCode);
rv = SECFailure;
}
}
}
if (rv == SECSuccess) {
if (nsc) {
bool dummyIsEV;
nsc->GetIsExtendedValidation(&dummyIsEV); // the nsc object will cache the status
}
if (rv == SECSuccess) {
if (nsc) {
bool dummyIsEV;
nsc->GetIsExtendedValidation(&dummyIsEV); // the nsc object will cache the status
}
nsCOMPtr<nsINSSComponent> nssComponent;
nsCOMPtr<nsINSSComponent> nssComponent;
for (CERTCertListNode *node = CERT_LIST_HEAD(certList);
!CERT_LIST_END(node, certList);
node = CERT_LIST_NEXT(node)) {
for (CERTCertListNode *node = CERT_LIST_HEAD(certList);
!CERT_LIST_END(node, certList);
node = CERT_LIST_NEXT(node)) {
if (node->cert->slot) {
// This cert was found on a token, no need to remember it in the temp db.
continue;
}
if (node->cert->isperm) {
// We don't need to remember certs already stored in perm db.
continue;
}
if (node->cert == serverCert) {
// We don't want to remember the server cert,
// the code that cares for displaying page info does this already.
continue;
}
// We have found a signer cert that we want to remember.
char* nickname = nsNSSCertificate::defaultServerNickname(node->cert);
if (nickname && *nickname) {
PK11SlotInfo *slot = PK11_GetInternalKeySlot();
if (slot) {
PK11_ImportCert(slot, node->cert, CK_INVALID_HANDLE,
nickname, false);
PK11_FreeSlot(slot);
}
}
PR_FREEIF(nickname);
if (node->cert->slot) {
// This cert was found on a token, no need to remember it in the temp db.
continue;
}
if (node->cert->isperm) {
// We don't need to remember certs already stored in perm db.
continue;
}
if (node->cert == mCert) {
// We don't want to remember the server cert,
// the code that cares for displaying page info does this already.
continue;
}
// We have found a signer cert that we want to remember.
char* nickname = nsNSSCertificate::defaultServerNickname(node->cert);
if (nickname && *nickname) {
PK11SlotInfo *slot = PK11_GetInternalKeySlot();
if (slot) {
PK11_ImportCert(slot, node->cert, CK_INVALID_HANDLE,
nickname, false);
PK11_FreeSlot(slot);
}
}
PR_FREEIF(nickname);
}
if (certList) {
@ -336,27 +497,186 @@ SECStatus PR_CALLBACK AuthCertificateCallback(void* client_data, PRFileDesc* fd,
// to the caller that contains at least the cert and its status.
if (!status) {
status = new nsSSLStatus();
infoObject->SetSSLStatus(status);
mSocketInfo->SetSSLStatus(status);
}
if (rv == SECSuccess) {
// Certificate verification succeeded delete any potential record
// of certificate error bits.
nsSSLIOLayerHelpers::mHostsWithCertErrors->RememberCertHasError(
infoObject, nsnull, rv);
mSocketInfo, nsnull, rv);
}
else {
// Certificate verification failed, update the status' bits.
nsSSLIOLayerHelpers::mHostsWithCertErrors->LookupCertErrorBits(
infoObject, status);
mSocketInfo, status);
}
if (status && !status->mServerCert) {
status->mServerCert = nsc;
PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
("AuthCertificateCallback setting NEW cert %p\n", status->mServerCert.get()));
("AuthCertificate setting NEW cert %p\n", status->mServerCert.get()));
}
}
return rv;
}
/*static*/ SECStatus
SSLServerCertVerificationJob::Dispatch(const void * fdForLogging,
nsNSSSocketInfo * socketInfo,
CERTCertificate * serverCert)
{
// Runs on the socket transport thread
if (!socketInfo || !serverCert) {
NS_ERROR("Invalid parameters for SSL server cert validation");
socketInfo->SetCertVerificationResult(PR_INVALID_STATE_ERROR,
PlainErrorMessage);
PR_SetError(PR_INVALID_STATE_ERROR, 0);
return SECFailure;
}
nsRefPtr<SSLServerCertVerificationJob> job
= new SSLServerCertVerificationJob(fdForLogging, *socketInfo, *serverCert);
socketInfo->SetCertVerificationWaiting();
nsresult nrv;
if (!gCertVerificationThreadPool) {
nrv = NS_ERROR_NOT_INITIALIZED;
} else {
nrv = gCertVerificationThreadPool->Dispatch(job, NS_DISPATCH_NORMAL);
}
if (NS_FAILED(nrv)) {
PRErrorCode error = nrv == NS_ERROR_OUT_OF_MEMORY
? SEC_ERROR_NO_MEMORY
: PR_INVALID_STATE_ERROR;
socketInfo->SetCertVerificationResult(error, PlainErrorMessage);
PORT_SetError(error);
return SECFailure;
}
PORT_SetError(PR_WOULD_BLOCK_ERROR);
return SECWouldBlock;
}
NS_IMETHODIMP
SSLServerCertVerificationJob::Run()
{
// Runs on a cert verification thread
PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
("[%p] SSLServerCertVerificationJob::Run\n", mSocketInfo.get()));
PRErrorCode error;
nsNSSShutDownPreventionLock nssShutdownPrevention;
if (mSocketInfo->isAlreadyShutDown()) {
error = SEC_ERROR_USER_CANCELLED;
} else {
// Reset the error code here so we can detect if AuthCertificate fails to
// set the error code if/when it fails.
PR_SetError(0, 0);
SECStatus rv = AuthCertificate(nssShutdownPrevention);
if (rv == SECSuccess) {
nsRefPtr<SSLServerCertVerificationResult> restart
= new SSLServerCertVerificationResult(*mSocketInfo, 0);
restart->Dispatch();
return NS_OK;
}
error = PR_GetError();
if (error != 0) {
rv = HandleBadCertificate(error, mSocketInfo, *mCert, mFdForLogging,
nssShutdownPrevention);
if (rv == SECSuccess) {
// The CertErrorRunnable will run on the main thread and it will dispatch
// the cert verification result to the socket transport thread, so we
// don't have to. This way, this verification thread doesn't need to
// wait for the CertErrorRunnable to complete.
return NS_OK;
}
// DispatchCertErrorRunnable set a new error code.
error = PR_GetError();
}
}
if (error == 0) {
NS_NOTREACHED("no error set during certificate validation failure");
error = PR_INVALID_STATE_ERROR;
}
nsRefPtr<SSLServerCertVerificationResult> failure
= new SSLServerCertVerificationResult(*mSocketInfo, error);
failure->Dispatch();
return NS_OK;
}
} // unnamed namespace
// Extracts whatever information we need out of fd (using SSL_*) and passes it
// to SSLServerCertVerificationJob::Dispatch. SSLServerCertVerificationJob should
// never do anything with fd except logging.
SECStatus
AuthCertificateHook(void *arg, PRFileDesc *fd, PRBool checkSig, PRBool isServer)
{
// Runs on the socket transport thread
PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
("[%p] starting AuthCertificateHook\n", fd));
// Modern libssl always passes PR_TRUE for checkSig, and we have no means of
// doing verification without checking signatures.
NS_ASSERTION(checkSig, "AuthCertificateHook: checkSig unexpectedly false");
// PSM never causes libssl to call this function with PR_TRUE for isServer,
// and many things in PSM assume that we are a client.
NS_ASSERTION(!isServer, "AuthCertificateHook: isServer unexpectedly true");
if (!checkSig || isServer) {
PR_SetError(PR_INVALID_STATE_ERROR, 0);
return SECFailure;
}
CERTCertificate *serverCert = SSL_PeerCertificate(fd);
nsNSSSocketInfo *socketInfo = static_cast<nsNSSSocketInfo*>(arg);
SECStatus rv = SSLServerCertVerificationJob::Dispatch(
static_cast<const void *>(fd), socketInfo, serverCert);
CERT_DestroyCertificate(serverCert);
return rv;
}
SSLServerCertVerificationResult::SSLServerCertVerificationResult(
nsNSSSocketInfo & socketInfo, PRErrorCode errorCode,
SSLErrorMessageType errorMessageType)
: mSocketInfo(&socketInfo)
, mErrorCode(errorCode)
, mErrorMessageType(errorMessageType)
{
}
void
SSLServerCertVerificationResult::Dispatch()
{
nsresult rv;
nsCOMPtr<nsIEventTarget> stsTarget
= do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
NS_ASSERTION(stsTarget,
"Failed to get socket transport service event target");
rv = stsTarget->Dispatch(this, NS_DISPATCH_NORMAL);
NS_ASSERTION(NS_SUCCEEDED(rv),
"Failed to dispatch SSLServerCertVerificationResult");
}
NS_IMETHODIMP
SSLServerCertVerificationResult::Run()
{
// TODO: Assert that we're on the socket transport thread
mSocketInfo->SetCertVerificationResult(mErrorCode, mErrorMessageType);
return NS_OK;
}
} } // namespace mozilla::psm

View File

@ -0,0 +1,89 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* 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 _SSLSERVERCERTVERIFICATION_H
#define _SSLSERVERCERTVERIFICATION_H
#include "seccomon.h"
#include "nsAutoPtr.h"
#include "nsThreadUtils.h"
#include "nsIRunnable.h"
#include "prerror.h"
#include "nsNSSIOLayer.h"
typedef struct PRFileDesc PRFileDesc;
typedef struct CERTCertificateStr CERTCertificate;
class nsNSSSocketInfo;
class nsNSSShutDownPreventionLock;
namespace mozilla { namespace psm {
SECStatus AuthCertificateHook(void *arg, PRFileDesc *fd,
PRBool checkSig, PRBool isServer);
SECStatus HandleBadCertificate(PRErrorCode defaultErrorCodeToReport,
nsNSSSocketInfo * socketInfo,
CERTCertificate & cert,
const void * fdForLogging,
const nsNSSShutDownPreventionLock &);
// Dispatched from a cert verification thread to the STS thread to notify the
// socketInfo of the verification result.
//
// This will cause the PR_Poll in the STS thread to return, so things work
// correctly even if the STS thread is blocked polling (only) on the file
// descriptor that is waiting for this result.
class SSLServerCertVerificationResult : public nsRunnable
{
public:
NS_DECL_NSIRUNNABLE
SSLServerCertVerificationResult(nsNSSSocketInfo & socketInfo,
PRErrorCode errorCode,
SSLErrorMessageType errorMessageType =
PlainErrorMessage);
void Dispatch();
private:
const nsRefPtr<nsNSSSocketInfo> mSocketInfo;
const PRErrorCode mErrorCode;
const SSLErrorMessageType mErrorMessageType;
};
} } // namespace mozilla::psm
#endif

View File

@ -48,7 +48,6 @@
#include "nsITokenDialogs.h"
#include "nsNSSShutDown.h"
#include "nsIUploadChannel.h"
#include "nsSSLThread.h"
#include "nsThreadUtils.h"
#include "nsIPrompt.h"
#include "nsProxyRelease.h"
@ -420,11 +419,10 @@ nsNSSHttpRequestSession::internal_send_receive_attempt(bool &retryable_error,
if (!request_canceled)
{
bool wantExit = nsSSLThread::stoppedOrStopping();
bool timeout =
(PRIntervalTime)(PR_IntervalNow() - start_time) > mTimeoutInterval;
if (wantExit || timeout)
if (timeout)
{
request_canceled = true;
@ -821,6 +819,12 @@ void PR_CALLBACK HandshakeCallback(PRFileDesc* fd, void* client_data) {
nsresult rv;
PRInt32 encryptBits;
nsNSSSocketInfo* infoObject = (nsNSSSocketInfo*) fd->higher->secret;
// If the handshake completed, then we know the site is TLS tolerant (if this
// was a TLS connection).
nsSSLIOLayerHelpers::rememberTolerantSite(fd, infoObject);
if (SECSuccess != SSL_SecurityStatus(fd, &sslStatus, &cipherName, &keyLength,
&encryptBits, &signer, nsnull)) {
return;
@ -842,7 +846,6 @@ void PR_CALLBACK HandshakeCallback(PRFileDesc* fd, void* client_data) {
bool wantWarning = (nsSSLIOLayerHelpers::getWarnLevelMissingRFC5746() > 0);
nsNSSSocketInfo* infoObject = (nsNSSSocketInfo*) fd->higher->secret;
nsCOMPtr<nsIConsoleService> console;
if (infoObject && wantWarning) {
console = do_GetService(NS_CONSOLESERVICE_CONTRACTID);

View File

@ -52,11 +52,6 @@ char* PR_CALLBACK
PK11PasswordPrompt(PK11SlotInfo *slot, PRBool retry, void* arg);
void PR_CALLBACK HandshakeCallback(PRFileDesc *fd, void *client_data);
SECStatus PR_CALLBACK AuthCertificateCallback(void* client_data, PRFileDesc* fd,
PRBool checksig, PRBool isServer);
PRErrorCode PSM_SSL_BlacklistDigiNotar(CERTCertificate * serverCert,
CERTCertList * serverCertChain);
SECStatus RegisterMyOCSPAIAInfoCallback();
SECStatus UnregisterMyOCSPAIAInfoCallback();

View File

@ -47,7 +47,6 @@
#include "nsNSSComponent.h"
#include "nsNSSCallbacks.h"
#include "nsNSSIOLayer.h"
#include "nsSSLThread.h"
#include "nsCertVerificationThread.h"
#include "nsNetUtil.h"
@ -364,7 +363,7 @@ nsNSSComponent::nsNSSComponent()
mNSSInitialized(false),
mCrlTimerLock("nsNSSComponent.mCrlTimerLock"),
mThreadList(nsnull),
mSSLThread(NULL), mCertVerificationThread(NULL)
mCertVerificationThread(NULL)
{
#ifdef PR_LOGGING
if (!gPIPNSSLog)
@ -391,12 +390,6 @@ nsNSSComponent::nsNSSComponent()
void
nsNSSComponent::deleteBackgroundThreads()
{
if (mSSLThread)
{
mSSLThread->requestExit();
delete mSSLThread;
mSSLThread = nsnull;
}
if (mCertVerificationThread)
{
mCertVerificationThread->requestExit();
@ -408,20 +401,11 @@ nsNSSComponent::deleteBackgroundThreads()
void
nsNSSComponent::createBackgroundThreads()
{
NS_ASSERTION(mSSLThread == nsnull, "SSL thread already created.");
NS_ASSERTION(mCertVerificationThread == nsnull,
"Cert verification thread already created.");
mSSLThread = new nsSSLThread;
nsresult rv = mSSLThread->startThread();
if (NS_FAILED(rv)) {
delete mSSLThread;
mSSLThread = nsnull;
return;
}
mCertVerificationThread = new nsCertVerificationThread;
rv = mCertVerificationThread->startThread();
nsresult rv = mCertVerificationThread->startThread();
if (NS_FAILED(rv)) {
delete mCertVerificationThread;
mCertVerificationThread = nsnull;
@ -1999,7 +1983,7 @@ nsNSSComponent::Init()
mClientAuthRememberService->Init();
createBackgroundThreads();
if (!mSSLThread || !mCertVerificationThread)
if (!mCertVerificationThread)
{
PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("NSS init, could not create threads\n"));
@ -2549,8 +2533,6 @@ nsNSSComponent::DoProfileApproveChange(nsISupports* aSubject)
void
nsNSSComponent::DoProfileChangeNetTeardown()
{
if (mSSLThread)
mSSLThread->requestExit();
if (mCertVerificationThread)
mCertVerificationThread->requestExit();
mIsNetworkDown = true;

View File

@ -237,7 +237,6 @@ private:
};
class nsNSSShutDownList;
class nsSSLThread;
class nsCertVerificationThread;
// Implementation of the PSM component interface.
@ -357,7 +356,6 @@ private:
void deleteBackgroundThreads();
void createBackgroundThreads();
nsSSLThread *mSSLThread;
nsCertVerificationThread *mCertVerificationThread;
nsNSSHttpInterface mHttpForNSS;

File diff suppressed because it is too large Load Diff

View File

@ -60,73 +60,21 @@
#include "nsNSSCertificate.h"
#include "nsDataHashtable.h"
class nsIChannel;
class nsSSLThread;
class ::mozilla::MutexAutoLock;
namespace mozilla {
/*
* 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();
class MutexAutoLock;
bool ensure_buffer_size(PRInt32 amount);
enum ssl_state {
ssl_invalid, // used for initializating, should never occur
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;
namespace psm {
// 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;
bool mOneBytePendingFromEarlierWrite;
unsigned char mThePendingByte;
PRInt32 mOriginalRequestedTransferAmount;
enum SSLErrorMessageType {
OverridableCertErrorMessage = 1, // for *overridable* certificate errors
PlainErrorMessage = 2 // all other errors (or "no error")
};
} // namespace psm
} // namespace mozilla
class nsNSSSocketInfo : public nsITransportSecurityInfo,
public nsISSLSocketControl,
public nsIInterfaceRequestor,
@ -164,6 +112,9 @@ public:
nsresult GetHandshakePending(bool *aHandshakePending);
nsresult SetHandshakePending(bool aHandshakePending);
const char * GetHostName() const {
return mHostName.get();
}
nsresult GetHostName(char **aHostName);
nsresult SetHostName(const char *aHostName);
@ -172,12 +123,9 @@ public:
void GetPreviousCert(nsIX509Cert** _result);
enum ErrorMessageType {
OverridableCertErrorMessage = 1, // for *overridable* certificate errors
PlainErrorMessage = 2, // all other errors
};
void SetCanceled(PRErrorCode errorCode, ErrorMessageType errorMessageType);
PRErrorCode GetErrorCode() const;
void SetCanceled(PRErrorCode errorCode,
::mozilla::psm::SSLErrorMessageType errorMessageType);
void SetHasCleartextPhase(bool aHasCleartextPhase);
bool GetHasCleartextPhase();
@ -193,8 +141,10 @@ public:
/* Set SSL Status values */
nsresult SetSSLStatus(nsSSLStatus *aSSLStatus);
nsSSLStatus* SSLStatus() { return mSSLStatus; }
PRStatus CloseSocketAndDestroy();
void SetStatusErrorBits(nsIX509Cert & cert, PRUint32 collected_errors);
PRStatus CloseSocketAndDestroy(
const nsNSSShutDownPreventionLock & proofOfLock);
bool IsCertIssuerBlacklisted() const {
return mIsCertIssuerBlacklisted;
@ -202,14 +152,32 @@ public:
void SetCertIssuerBlacklisted() {
mIsCertIssuerBlacklisted = true;
}
// XXX: These are only used on for diagnostic purposes
enum CertVerificationState {
before_cert_verification,
waiting_for_cert_verification,
after_cert_verification
};
void SetCertVerificationWaiting();
// Use errorCode == 0 to indicate success; in that case, errorMessageType is
// ignored.
void SetCertVerificationResult(PRErrorCode errorCode,
::mozilla::psm::SSLErrorMessageType errorMessageType);
// for logging only
PRBool IsWaitingForCertVerification() const
{
return mCertVerificationState == waiting_for_cert_verification;
}
protected:
mutable ::mozilla::Mutex mMutex;
nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
PRFileDesc* mFd;
enum {
blocking_state_unknown, is_nonblocking_socket, is_blocking_socket
} mBlockingState;
CertVerificationState mCertVerificationState;
PRUint32 mSecurityState;
PRInt32 mSubRequestsHighSecurity;
PRInt32 mSubRequestsLowSecurity;
@ -218,7 +186,7 @@ protected:
nsString mShortDesc;
PRErrorCode mErrorCode;
ErrorMessageType mErrorMessageType;
::mozilla::psm::SSLErrorMessageType mErrorMessageType;
nsString mErrorMessageCached;
nsresult formatErrorMessage(::mozilla::MutexAutoLock const & proofOfLock);
@ -240,13 +208,9 @@ protected:
nsresult ActivateSSL();
nsSSLSocketThreadData *mThreadData;
private:
virtual void virtualDestroyNSSReference();
void destructorSafeDestroyNSSReference();
friend class nsSSLThread;
};
class nsCStringHashSet;
@ -311,11 +275,6 @@ public:
static void setRenegoUnrestrictedSites(const nsCString &str);
static bool isRenegoUnrestrictedSite(const nsCString &str);
static PRFileDesc *mSharedPollableEvent;
static nsNSSSocketInfo *mSocketOwningPollableEvent;
static bool mPollableEventCurrentlySet;
};
nsresult nsSSLIOLayerNewSocket(PRInt32 family,

File diff suppressed because it is too large Load Diff

View File

@ -1,158 +0,0 @@
/* ***** 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.
nsCOMPtr<nsIRequest> mPendingHTTPRequest;
virtual void Run(void);
// Called from SSL thread only
static PRInt32 checkHandshake(PRInt32 bytesTransfered,
bool wasReading,
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);
// Support of blocking sockets is very rudimentary.
// We only support it because Mozilla's LDAP code requires blocking I/O.
// We do not support switching the blocking mode of a socket.
// We require the blocking state has been set prior to the first
// read/write call, and will stay that way for the remainder of the socket's lifetime.
// This function must be called while holding the lock.
// If the socket is a blocking socket, out_fd will contain the real FD,
// on a non-blocking socket out_fd will be nsnull.
// If there is a failure in obtaining the status of the socket,
// the function will return PR_FAILURE.
static PRStatus getRealFDIfBlockingSocket_locked(nsNSSSocketInfo *si,
PRFileDesc *&out_fd);
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,
PRIntervalTime timeout);
static PRInt32 requestWrite(nsNSSSocketInfo *si,
const void *buf,
PRInt32 amount,
PRIntervalTime timeout);
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);
static bool stoppedOrStopping();
};
#endif //_NSSSLTHREAD_H_