| Index: net/ocsp/nss_ocsp.cc
|
| diff --git a/net/ocsp/nss_ocsp.cc b/net/ocsp/nss_ocsp.cc
|
| index bd6a70bf94190e1749d49ca9d72ef1ee6bb089ec..4fd51a972db863bfe020b0e30c81f5ee9f1409f5 100644
|
| --- a/net/ocsp/nss_ocsp.cc
|
| +++ b/net/ocsp/nss_ocsp.cc
|
| @@ -9,16 +9,19 @@
|
| #include <ocsp.h>
|
| #include <nspr.h>
|
| #include <nss.h>
|
| +#include <pthread.h>
|
| #include <secerr.h>
|
|
|
| #include <string>
|
|
|
| +#include "base/basictypes.h"
|
| #include "base/compiler_specific.h"
|
| #include "base/condition_variable.h"
|
| #include "base/histogram.h"
|
| +#include "base/lazy_instance.h"
|
| +#include "base/lock.h"
|
| #include "base/logging.h"
|
| #include "base/message_loop.h"
|
| -#include "base/singleton.h"
|
| #include "base/string_util.h"
|
| #include "base/thread.h"
|
| #include "base/time.h"
|
| @@ -32,6 +35,93 @@
|
|
|
| namespace {
|
|
|
| +// Protects |g_request_context|.
|
| +pthread_mutex_t g_request_context_lock = PTHREAD_MUTEX_INITIALIZER;
|
| +static URLRequestContext* g_request_context = NULL;
|
| +
|
| +class OCSPIOLoop : public MessageLoop::DestructionObserver {
|
| + public:
|
| + // MessageLoop::DestructionObserver:
|
| + virtual void WillDestroyCurrentMessageLoop();
|
| +
|
| + void StartUsing() {
|
| + AutoLock autolock(lock_);
|
| + used_ = true;
|
| + }
|
| +
|
| + bool used() const {
|
| + AutoLock autolock(lock_);
|
| + return used_;
|
| + }
|
| +
|
| + // Called from worker thread.
|
| + void PostTaskToIOLoop(const tracked_objects::Location& from_here, Task* task);
|
| +
|
| + void EnsureIOLoop();
|
| +
|
| + private:
|
| + friend struct base::DefaultLazyInstanceTraits<OCSPIOLoop>;
|
| +
|
| + OCSPIOLoop();
|
| + ~OCSPIOLoop();
|
| +
|
| + mutable Lock lock_;
|
| + bool used_; // Protected by |lock_|.
|
| + // This should not be modified after |used_|.
|
| + MessageLoopForIO* io_loop_; // Protected by |lock_|.
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(OCSPIOLoop);
|
| +};
|
| +
|
| +OCSPIOLoop::OCSPIOLoop()
|
| + : used_(false),
|
| + io_loop_(MessageLoopForIO::current()) {
|
| + DCHECK(io_loop_);
|
| + io_loop_->AddDestructionObserver(this);
|
| +}
|
| +
|
| +OCSPIOLoop::~OCSPIOLoop() {
|
| + // IO thread was already deleted before the singleton is deleted
|
| + // in AtExitManager.
|
| + {
|
| + AutoLock autolock(lock_);
|
| + DCHECK(!io_loop_);
|
| + DCHECK(!used_);
|
| + }
|
| +
|
| + pthread_mutex_lock(&g_request_context_lock);
|
| + DCHECK(!g_request_context);
|
| + pthread_mutex_unlock(&g_request_context_lock);
|
| +}
|
| +
|
| +void OCSPIOLoop::WillDestroyCurrentMessageLoop() {
|
| + // Prevent the worker thread from trying to access |io_loop_|.
|
| + {
|
| + AutoLock autolock(lock_);
|
| + DCHECK_EQ(MessageLoopForIO::current(), io_loop_);
|
| + io_loop_ = NULL;
|
| + used_ = false;
|
| + }
|
| +
|
| + pthread_mutex_lock(&g_request_context_lock);
|
| + g_request_context = NULL;
|
| + pthread_mutex_unlock(&g_request_context_lock);
|
| +}
|
| +
|
| +void OCSPIOLoop::PostTaskToIOLoop(
|
| + const tracked_objects::Location& from_here, Task* task) {
|
| + AutoLock autolock(lock_);
|
| + if (io_loop_)
|
| + io_loop_->PostTask(from_here, task);
|
| +}
|
| +
|
| +void OCSPIOLoop::EnsureIOLoop() {
|
| + AutoLock autolock(lock_);
|
| + DCHECK_EQ(MessageLoopForIO::current(), io_loop_);
|
| +}
|
| +
|
| +base::LazyInstance<OCSPIOLoop> g_ocsp_io_loop(base::LINKER_INITIALIZED);
|
| +
|
| const int kRecvBufferSize = 4096;
|
|
|
| // All OCSP handlers should be called in the context of
|
| @@ -68,97 +158,58 @@ SECStatus OCSPFree(SEC_HTTP_REQUEST_SESSION request);
|
|
|
| char* GetAlternateOCSPAIAInfo(CERTCertificate *cert);
|
|
|
| -class OCSPInitSingleton : public MessageLoop::DestructionObserver {
|
| - public:
|
| - // Called on IO thread.
|
| - virtual void WillDestroyCurrentMessageLoop() {
|
| - AutoLock autolock(lock_);
|
| - DCHECK_EQ(MessageLoopForIO::current(), io_loop_);
|
| - io_loop_ = NULL;
|
| - request_context_ = NULL;
|
| - };
|
| -
|
| - // Called from worker thread.
|
| - void PostTaskToIOLoop(
|
| - const tracked_objects::Location& from_here, Task* task) {
|
| - AutoLock autolock(lock_);
|
| - if (io_loop_)
|
| - io_loop_->PostTask(from_here, task);
|
| - }
|
| -
|
| - // This is static method because it is called before NSS initialization,
|
| - // that is, before OCSPInitSingleton is initialized.
|
| - static void set_url_request_context(URLRequestContext* request_context) {
|
| - request_context_ = request_context;
|
| - }
|
| - static URLRequestContext* url_request_context() {
|
| - return request_context_;
|
| - }
|
| -
|
| +class OCSPNSSInitialization {
|
| private:
|
| - friend struct DefaultSingletonTraits<OCSPInitSingleton>;
|
| -
|
| - OCSPInitSingleton()
|
| - : io_loop_(MessageLoopForIO::current()) {
|
| - DCHECK(io_loop_);
|
| - io_loop_->AddDestructionObserver(this);
|
| -
|
| - // NSS calls the functions in the function table to download certificates
|
| - // or CRLs or talk to OCSP responders over HTTP. These functions must
|
| - // set an NSS/NSPR error code when they fail. Otherwise NSS will get the
|
| - // residual error code from an earlier failed function call.
|
| - client_fcn_.version = 1;
|
| - SEC_HttpClientFcnV1Struct *ft = &client_fcn_.fcnTable.ftable1;
|
| - ft->createSessionFcn = OCSPCreateSession;
|
| - ft->keepAliveSessionFcn = OCSPKeepAliveSession;
|
| - ft->freeSessionFcn = OCSPFreeSession;
|
| - ft->createFcn = OCSPCreate;
|
| - ft->setPostDataFcn = OCSPSetPostData;
|
| - ft->addHeaderFcn = OCSPAddHeader;
|
| - ft->trySendAndReceiveFcn = OCSPTrySendAndReceive;
|
| - ft->cancelFcn = NULL;
|
| - ft->freeFcn = OCSPFree;
|
| - SECStatus status = SEC_RegisterDefaultHttpClient(&client_fcn_);
|
| - if (status != SECSuccess) {
|
| - NOTREACHED() << "Error initializing OCSP: " << PR_GetError();
|
| - }
|
| + friend struct base::DefaultLazyInstanceTraits<OCSPNSSInitialization>;
|
|
|
| - // Work around NSS bugs 524013 and 564334. NSS incorrectly thinks the
|
| - // CRLs for Network Solutions Certificate Authority have bad signatures,
|
| - // which causes certificates issued by that CA to be reported as revoked.
|
| - // By using OCSP for those certificates, which don't have AIA extensions,
|
| - // we can work around these bugs. See http://crbug.com/41730.
|
| - CERT_StringFromCertFcn old_callback = NULL;
|
| - status = CERT_RegisterAlternateOCSPAIAInfoCallBack(
|
| - GetAlternateOCSPAIAInfo, &old_callback);
|
| - if (status == SECSuccess) {
|
| - DCHECK(!old_callback);
|
| - } else {
|
| - NOTREACHED() << "Error initializing OCSP: " << PR_GetError();
|
| - }
|
| - }
|
| -
|
| - virtual ~OCSPInitSingleton() {
|
| - // IO thread was already deleted before the singleton is deleted
|
| - // in AtExitManager.
|
| - AutoLock autolock(lock_);
|
| - DCHECK(!io_loop_);
|
| - DCHECK(!request_context_);
|
| - }
|
| + OCSPNSSInitialization();
|
| + ~OCSPNSSInitialization();
|
|
|
| SEC_HttpClientFcn client_fcn_;
|
|
|
| - // |lock_| protects |io_loop_|.
|
| - Lock lock_;
|
| - // I/O thread.
|
| - MessageLoop* io_loop_; // I/O thread
|
| - // URLRequestContext for OCSP handlers.
|
| - static URLRequestContext* request_context_;
|
| -
|
| - DISALLOW_COPY_AND_ASSIGN(OCSPInitSingleton);
|
| + DISALLOW_COPY_AND_ASSIGN(OCSPNSSInitialization);
|
| };
|
|
|
| -URLRequestContext* OCSPInitSingleton::request_context_ = NULL;
|
| +OCSPNSSInitialization::OCSPNSSInitialization() {
|
| + // NSS calls the functions in the function table to download certificates
|
| + // or CRLs or talk to OCSP responders over HTTP. These functions must
|
| + // set an NSS/NSPR error code when they fail. Otherwise NSS will get the
|
| + // residual error code from an earlier failed function call.
|
| + client_fcn_.version = 1;
|
| + SEC_HttpClientFcnV1Struct *ft = &client_fcn_.fcnTable.ftable1;
|
| + ft->createSessionFcn = OCSPCreateSession;
|
| + ft->keepAliveSessionFcn = OCSPKeepAliveSession;
|
| + ft->freeSessionFcn = OCSPFreeSession;
|
| + ft->createFcn = OCSPCreate;
|
| + ft->setPostDataFcn = OCSPSetPostData;
|
| + ft->addHeaderFcn = OCSPAddHeader;
|
| + ft->trySendAndReceiveFcn = OCSPTrySendAndReceive;
|
| + ft->cancelFcn = NULL;
|
| + ft->freeFcn = OCSPFree;
|
| + SECStatus status = SEC_RegisterDefaultHttpClient(&client_fcn_);
|
| + if (status != SECSuccess) {
|
| + NOTREACHED() << "Error initializing OCSP: " << PR_GetError();
|
| + }
|
| +
|
| + // Work around NSS bugs 524013 and 564334. NSS incorrectly thinks the
|
| + // CRLs for Network Solutions Certificate Authority have bad signatures,
|
| + // which causes certificates issued by that CA to be reported as revoked.
|
| + // By using OCSP for those certificates, which don't have AIA extensions,
|
| + // we can work around these bugs. See http://crbug.com/41730.
|
| + CERT_StringFromCertFcn old_callback = NULL;
|
| + status = CERT_RegisterAlternateOCSPAIAInfoCallBack(
|
| + GetAlternateOCSPAIAInfo, &old_callback);
|
| + if (status == SECSuccess) {
|
| + DCHECK(!old_callback);
|
| + } else {
|
| + NOTREACHED() << "Error initializing OCSP: " << PR_GetError();
|
| + }
|
| +}
|
| +
|
| +OCSPNSSInitialization::~OCSPNSSInitialization() {}
|
| +
|
| +base::LazyInstance<OCSPNSSInitialization> g_ocsp_nss_initialization(
|
| + base::LINKER_INITIALIZED);
|
|
|
| // Concrete class for SEC_HTTP_REQUEST_SESSION.
|
| // Public methods except virtual methods of URLRequest::Delegate (On* methods)
|
| @@ -199,7 +250,7 @@ class OCSPRequestSession
|
| // |io_loop_| was initialized to be NULL in constructor, and
|
| // set only in StartURLRequest, so no need to lock |lock_| here.
|
| DCHECK(!io_loop_);
|
| - Singleton<OCSPInitSingleton>()->PostTaskToIOLoop(
|
| + g_ocsp_io_loop.Get().PostTaskToIOLoop(
|
| FROM_HERE,
|
| NewRunnableMethod(this, &OCSPRequestSession::StartURLRequest));
|
| }
|
| @@ -338,11 +389,14 @@ class OCSPRequestSession
|
| }
|
| }
|
|
|
| + // Runs on |g_ocsp_io_loop|'s IO loop.
|
| void StartURLRequest() {
|
| DCHECK(!request_);
|
|
|
| - URLRequestContext* url_request_context =
|
| - OCSPInitSingleton::url_request_context();
|
| + pthread_mutex_lock(&g_request_context_lock);
|
| + URLRequestContext* url_request_context = g_request_context;
|
| + pthread_mutex_unlock(&g_request_context_lock);
|
| +
|
| if (url_request_context == NULL)
|
| return;
|
|
|
| @@ -475,7 +529,10 @@ SECStatus OCSPCreateSession(const char* host, PRUint16 portnum,
|
| SEC_HTTP_SERVER_SESSION* pSession) {
|
| LOG(INFO) << "OCSP create session: host=" << host << " port=" << portnum;
|
| DCHECK(!MessageLoop::current());
|
| - if (OCSPInitSingleton::url_request_context() == NULL) {
|
| + pthread_mutex_lock(&g_request_context_lock);
|
| + URLRequestContext* request_context = g_request_context;
|
| + pthread_mutex_unlock(&g_request_context_lock);
|
| + if (request_context == NULL) {
|
| LOG(ERROR) << "No URLRequestContext for OCSP handler.";
|
| // The application failed to call SetURLRequestContextForOCSP, so we
|
| // can't create and use URLRequest. PR_NOT_IMPLEMENTED_ERROR is not an
|
| @@ -782,17 +839,40 @@ char* GetAlternateOCSPAIAInfo(CERTCertificate *cert) {
|
|
|
| namespace net {
|
|
|
| +void SetMessageLoopForOCSP() {
|
| + // Must have a MessageLoopForIO.
|
| + DCHECK(MessageLoopForIO::current());
|
| +
|
| + bool used = g_ocsp_io_loop.Get().used();
|
| +
|
| + // Should not be called when g_ocsp_io_loop has already been used.
|
| + DCHECK(!used);
|
| +}
|
| +
|
| void EnsureOCSPInit() {
|
| - Singleton<OCSPInitSingleton>::get();
|
| + g_ocsp_io_loop.Get().StartUsing();
|
| + g_ocsp_nss_initialization.Get();
|
| }
|
|
|
| // This function would be called before NSS initialization.
|
| void SetURLRequestContextForOCSP(URLRequestContext* request_context) {
|
| - OCSPInitSingleton::set_url_request_context(request_context);
|
| + pthread_mutex_lock(&g_request_context_lock);
|
| + if (request_context) {
|
| + DCHECK(request_context->is_main());
|
| + DCHECK(!g_request_context);
|
| + } else {
|
| + DCHECK(g_request_context);
|
| + }
|
| + g_request_context = request_context;
|
| + pthread_mutex_unlock(&g_request_context_lock);
|
| }
|
|
|
| URLRequestContext* GetURLRequestContextForOCSP() {
|
| - return OCSPInitSingleton::url_request_context();
|
| + pthread_mutex_lock(&g_request_context_lock);
|
| + URLRequestContext* request_context = g_request_context;
|
| + pthread_mutex_unlock(&g_request_context_lock);
|
| + DCHECK(request_context->is_main());
|
| + return request_context;
|
| }
|
|
|
| } // namespace net
|
|
|