Home | History | Annotate | Download | only in ocsp
      1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "net/ocsp/nss_ocsp.h"
      6 
      7 #include <certt.h>
      8 #include <certdb.h>
      9 #include <ocsp.h>
     10 #include <nspr.h>
     11 #include <nss.h>
     12 #include <pthread.h>
     13 #include <secerr.h>
     14 
     15 #include <string>
     16 
     17 #include "base/basictypes.h"
     18 #include "base/compiler_specific.h"
     19 #include "base/lazy_instance.h"
     20 #include "base/logging.h"
     21 #include "base/message_loop.h"
     22 #include "base/metrics/histogram.h"
     23 #include "base/stl_util-inl.h"
     24 #include "base/string_util.h"
     25 #include "base/stringprintf.h"
     26 #include "base/synchronization/condition_variable.h"
     27 #include "base/synchronization/lock.h"
     28 #include "base/threading/thread_checker.h"
     29 #include "base/time.h"
     30 #include "googleurl/src/gurl.h"
     31 #include "net/base/io_buffer.h"
     32 #include "net/base/load_flags.h"
     33 #include "net/http/http_request_headers.h"
     34 #include "net/http/http_response_headers.h"
     35 #include "net/url_request/url_request.h"
     36 #include "net/url_request/url_request_context.h"
     37 
     38 namespace {
     39 
     40 // Protects |g_request_context|.
     41 pthread_mutex_t g_request_context_lock = PTHREAD_MUTEX_INITIALIZER;
     42 static net::URLRequestContext* g_request_context = NULL;
     43 
     44 class OCSPRequestSession;
     45 
     46 class OCSPIOLoop {
     47  public:
     48   void StartUsing() {
     49     base::AutoLock autolock(lock_);
     50     used_ = true;
     51   }
     52 
     53   // Called on IO loop.
     54   void Shutdown();
     55 
     56   bool used() const {
     57     base::AutoLock autolock(lock_);
     58     return used_;
     59   }
     60 
     61   // Called from worker thread.
     62   void PostTaskToIOLoop(const tracked_objects::Location& from_here, Task* task);
     63 
     64   void EnsureIOLoop();
     65 
     66   void AddRequest(OCSPRequestSession* request);
     67   void RemoveRequest(OCSPRequestSession* request);
     68 
     69  private:
     70   friend struct base::DefaultLazyInstanceTraits<OCSPIOLoop>;
     71 
     72   OCSPIOLoop();
     73   ~OCSPIOLoop();
     74 
     75   void CancelAllRequests();
     76 
     77   mutable base::Lock lock_;
     78   bool shutdown_;  // Protected by |lock_|.
     79   std::set<OCSPRequestSession*> requests_;  // Protected by |lock_|.
     80   bool used_;  // Protected by |lock_|.
     81   // This should not be modified after |used_|.
     82   MessageLoopForIO* io_loop_;  // Protected by |lock_|.
     83   base::ThreadChecker thread_checker_;
     84 
     85   DISALLOW_COPY_AND_ASSIGN(OCSPIOLoop);
     86 };
     87 
     88 base::LazyInstance<OCSPIOLoop, base::LeakyLazyInstanceTraits<OCSPIOLoop> >
     89     g_ocsp_io_loop(base::LINKER_INITIALIZED);
     90 
     91 const int kRecvBufferSize = 4096;
     92 
     93 // All OCSP handlers should be called in the context of
     94 // CertVerifier's thread (i.e. worker pool, not on the I/O thread).
     95 // It supports blocking mode only.
     96 
     97 SECStatus OCSPCreateSession(const char* host, PRUint16 portnum,
     98                             SEC_HTTP_SERVER_SESSION* pSession);
     99 SECStatus OCSPKeepAliveSession(SEC_HTTP_SERVER_SESSION session,
    100                                PRPollDesc **pPollDesc);
    101 SECStatus OCSPFreeSession(SEC_HTTP_SERVER_SESSION session);
    102 
    103 SECStatus OCSPCreate(SEC_HTTP_SERVER_SESSION session,
    104                      const char* http_protocol_variant,
    105                      const char* path_and_query_string,
    106                      const char* http_request_method,
    107                      const PRIntervalTime timeout,
    108                      SEC_HTTP_REQUEST_SESSION* pRequest);
    109 SECStatus OCSPSetPostData(SEC_HTTP_REQUEST_SESSION request,
    110                           const char* http_data,
    111                           const PRUint32 http_data_len,
    112                           const char* http_content_type);
    113 SECStatus OCSPAddHeader(SEC_HTTP_REQUEST_SESSION request,
    114                         const char* http_header_name,
    115                         const char* http_header_value);
    116 SECStatus OCSPTrySendAndReceive(SEC_HTTP_REQUEST_SESSION request,
    117                                 PRPollDesc** pPollDesc,
    118                                 PRUint16* http_response_code,
    119                                 const char** http_response_content_type,
    120                                 const char** http_response_headers,
    121                                 const char** http_response_data,
    122                                 PRUint32* http_response_data_len);
    123 SECStatus OCSPFree(SEC_HTTP_REQUEST_SESSION request);
    124 
    125 char* GetAlternateOCSPAIAInfo(CERTCertificate *cert);
    126 
    127 class OCSPNSSInitialization {
    128  private:
    129   friend struct base::DefaultLazyInstanceTraits<OCSPNSSInitialization>;
    130 
    131   OCSPNSSInitialization();
    132   ~OCSPNSSInitialization();
    133 
    134   SEC_HttpClientFcn client_fcn_;
    135 
    136   DISALLOW_COPY_AND_ASSIGN(OCSPNSSInitialization);
    137 };
    138 
    139 base::LazyInstance<OCSPNSSInitialization> g_ocsp_nss_initialization(
    140     base::LINKER_INITIALIZED);
    141 
    142 // Concrete class for SEC_HTTP_REQUEST_SESSION.
    143 // Public methods except virtual methods of net::URLRequest::Delegate
    144 // (On* methods) run on certificate verifier thread (worker thread).
    145 // Virtual methods of net::URLRequest::Delegate and private methods run
    146 // on IO thread.
    147 class OCSPRequestSession
    148     : public base::RefCountedThreadSafe<OCSPRequestSession>,
    149       public net::URLRequest::Delegate {
    150  public:
    151   OCSPRequestSession(const GURL& url,
    152                      const char* http_request_method,
    153                      base::TimeDelta timeout)
    154       : url_(url),
    155         http_request_method_(http_request_method),
    156         timeout_(timeout),
    157         request_(NULL),
    158         buffer_(new net::IOBuffer(kRecvBufferSize)),
    159         response_code_(-1),
    160         cv_(&lock_),
    161         io_loop_(NULL),
    162         finished_(false) {}
    163 
    164   void SetPostData(const char* http_data, PRUint32 http_data_len,
    165                    const char* http_content_type) {
    166     upload_content_.assign(http_data, http_data_len);
    167     upload_content_type_.assign(http_content_type);
    168   }
    169 
    170   void AddHeader(const char* http_header_name, const char* http_header_value) {
    171     extra_request_headers_.SetHeader(http_header_name,
    172                                      http_header_value);
    173   }
    174 
    175   void Start() {
    176     // At this point, it runs on worker thread.
    177     // |io_loop_| was initialized to be NULL in constructor, and
    178     // set only in StartURLRequest, so no need to lock |lock_| here.
    179     DCHECK(!io_loop_);
    180     g_ocsp_io_loop.Get().PostTaskToIOLoop(
    181         FROM_HERE,
    182         NewRunnableMethod(this, &OCSPRequestSession::StartURLRequest));
    183   }
    184 
    185   bool Started() const {
    186     return request_ != NULL;
    187   }
    188 
    189   void Cancel() {
    190     // IO thread may set |io_loop_| to NULL, so protect by |lock_|.
    191     base::AutoLock autolock(lock_);
    192     CancelLocked();
    193   }
    194 
    195   bool Finished() const {
    196     base::AutoLock autolock(lock_);
    197     return finished_;
    198   }
    199 
    200   bool Wait() {
    201     base::TimeDelta timeout = timeout_;
    202     base::AutoLock autolock(lock_);
    203     while (!finished_) {
    204       base::TimeTicks last_time = base::TimeTicks::Now();
    205       cv_.TimedWait(timeout);
    206       // Check elapsed time
    207       base::TimeDelta elapsed_time = base::TimeTicks::Now() - last_time;
    208       timeout -= elapsed_time;
    209       if (timeout < base::TimeDelta()) {
    210         VLOG(1) << "OCSP Timed out";
    211         if (!finished_)
    212           CancelLocked();
    213         break;
    214       }
    215     }
    216     return finished_;
    217   }
    218 
    219   const GURL& url() const {
    220     return url_;
    221   }
    222 
    223   const std::string& http_request_method() const {
    224     return http_request_method_;
    225   }
    226 
    227   base::TimeDelta timeout() const {
    228     return timeout_;
    229   }
    230 
    231   PRUint16 http_response_code() const {
    232     DCHECK(finished_);
    233     return response_code_;
    234   }
    235 
    236   const std::string& http_response_content_type() const {
    237     DCHECK(finished_);
    238     return response_content_type_;
    239   }
    240 
    241   const std::string& http_response_headers() const {
    242     DCHECK(finished_);
    243     return response_headers_->raw_headers();
    244   }
    245 
    246   const std::string& http_response_data() const {
    247     DCHECK(finished_);
    248     return data_;
    249   }
    250 
    251   virtual void OnReceivedRedirect(net::URLRequest* request,
    252                                   const GURL& new_url,
    253                                   bool* defer_redirect) {
    254     DCHECK_EQ(request, request_);
    255     DCHECK_EQ(MessageLoopForIO::current(), io_loop_);
    256 
    257     if (!new_url.SchemeIs("http")) {
    258       // Prevent redirects to non-HTTP schemes, including HTTPS. This matches
    259       // the initial check in OCSPServerSession::CreateRequest().
    260       CancelURLRequest();
    261     }
    262   }
    263 
    264   virtual void OnResponseStarted(net::URLRequest* request) {
    265     DCHECK_EQ(request, request_);
    266     DCHECK_EQ(MessageLoopForIO::current(), io_loop_);
    267 
    268     int bytes_read = 0;
    269     if (request->status().is_success()) {
    270       response_code_ = request_->GetResponseCode();
    271       response_headers_ = request_->response_headers();
    272       response_headers_->GetMimeType(&response_content_type_);
    273       request_->Read(buffer_, kRecvBufferSize, &bytes_read);
    274     }
    275     OnReadCompleted(request_, bytes_read);
    276   }
    277 
    278   virtual void OnReadCompleted(net::URLRequest* request, int bytes_read) {
    279     DCHECK_EQ(request, request_);
    280     DCHECK_EQ(MessageLoopForIO::current(), io_loop_);
    281 
    282     do {
    283       if (!request_->status().is_success() || bytes_read <= 0)
    284         break;
    285       data_.append(buffer_->data(), bytes_read);
    286     } while (request_->Read(buffer_, kRecvBufferSize, &bytes_read));
    287 
    288     if (!request_->status().is_io_pending()) {
    289       delete request_;
    290       request_ = NULL;
    291       g_ocsp_io_loop.Get().RemoveRequest(this);
    292       {
    293         base::AutoLock autolock(lock_);
    294         finished_ = true;
    295         io_loop_ = NULL;
    296       }
    297       cv_.Signal();
    298       Release();  // Balanced with StartURLRequest().
    299     }
    300   }
    301 
    302   // Must be called on the IO loop thread.
    303   void CancelURLRequest() {
    304 #ifndef NDEBUG
    305     {
    306       base::AutoLock autolock(lock_);
    307       if (io_loop_)
    308         DCHECK_EQ(MessageLoopForIO::current(), io_loop_);
    309     }
    310 #endif
    311     if (request_) {
    312       request_->Cancel();
    313       delete request_;
    314       request_ = NULL;
    315       g_ocsp_io_loop.Get().RemoveRequest(this);
    316       {
    317         base::AutoLock autolock(lock_);
    318         finished_ = true;
    319         io_loop_ = NULL;
    320       }
    321       cv_.Signal();
    322       Release();  // Balanced with StartURLRequest().
    323     }
    324   }
    325 
    326  private:
    327   friend class base::RefCountedThreadSafe<OCSPRequestSession>;
    328 
    329   virtual ~OCSPRequestSession() {
    330     // When this destructor is called, there should be only one thread that has
    331     // a reference to this object, and so that thread doesn't need to lock
    332     // |lock_| here.
    333     DCHECK(!request_);
    334     DCHECK(!io_loop_);
    335   }
    336 
    337   // Must call this method while holding |lock_|.
    338   void CancelLocked() {
    339     lock_.AssertAcquired();
    340     if (io_loop_) {
    341       io_loop_->PostTask(
    342           FROM_HERE,
    343           NewRunnableMethod(this, &OCSPRequestSession::CancelURLRequest));
    344     }
    345   }
    346 
    347   // Runs on |g_ocsp_io_loop|'s IO loop.
    348   void StartURLRequest() {
    349     DCHECK(!request_);
    350 
    351     pthread_mutex_lock(&g_request_context_lock);
    352     net::URLRequestContext* url_request_context = g_request_context;
    353     pthread_mutex_unlock(&g_request_context_lock);
    354 
    355     if (url_request_context == NULL)
    356       return;
    357 
    358     {
    359       base::AutoLock autolock(lock_);
    360       DCHECK(!io_loop_);
    361       io_loop_ = MessageLoopForIO::current();
    362       g_ocsp_io_loop.Get().AddRequest(this);
    363     }
    364 
    365     request_ = new net::URLRequest(url_, this);
    366     request_->set_context(url_request_context);
    367     // To meet the privacy requirements of incognito mode.
    368     request_->set_load_flags(
    369         net::LOAD_DISABLE_CACHE | net::LOAD_DO_NOT_SAVE_COOKIES |
    370         net::LOAD_DO_NOT_SEND_COOKIES);
    371 
    372     if (http_request_method_ == "POST") {
    373       DCHECK(!upload_content_.empty());
    374       DCHECK(!upload_content_type_.empty());
    375 
    376       request_->set_method("POST");
    377       extra_request_headers_.SetHeader(
    378           net::HttpRequestHeaders::kContentType, upload_content_type_);
    379       request_->AppendBytesToUpload(upload_content_.data(),
    380                                     static_cast<int>(upload_content_.size()));
    381     }
    382     if (!extra_request_headers_.IsEmpty())
    383       request_->SetExtraRequestHeaders(extra_request_headers_);
    384 
    385     request_->Start();
    386     AddRef();  // Release after |request_| deleted.
    387   }
    388 
    389   GURL url_;                      // The URL we eventually wound up at
    390   std::string http_request_method_;
    391   base::TimeDelta timeout_;       // The timeout for OCSP
    392   net::URLRequest* request_;           // The actual request this wraps
    393   scoped_refptr<net::IOBuffer> buffer_;  // Read buffer
    394   net::HttpRequestHeaders extra_request_headers_;
    395   std::string upload_content_;    // HTTP POST payload
    396   std::string upload_content_type_;  // MIME type of POST payload
    397 
    398   int response_code_;             // HTTP status code for the request
    399   std::string response_content_type_;
    400   scoped_refptr<net::HttpResponseHeaders> response_headers_;
    401   std::string data_;              // Results of the requst
    402 
    403   // |lock_| protects |finished_| and |io_loop_|.
    404   mutable base::Lock lock_;
    405   base::ConditionVariable cv_;
    406 
    407   MessageLoop* io_loop_;          // Message loop of the IO thread
    408   bool finished_;
    409 
    410   DISALLOW_COPY_AND_ASSIGN(OCSPRequestSession);
    411 };
    412 
    413 // Concrete class for SEC_HTTP_SERVER_SESSION.
    414 class OCSPServerSession {
    415  public:
    416   OCSPServerSession(const char* host, PRUint16 port)
    417       : host_(host), port_(port) {}
    418   ~OCSPServerSession() {}
    419 
    420   OCSPRequestSession* CreateRequest(const char* http_protocol_variant,
    421                                     const char* path_and_query_string,
    422                                     const char* http_request_method,
    423                                     const PRIntervalTime timeout) {
    424     // We dont' support "https" because we haven't thought about
    425     // whether it's safe to re-enter this code from talking to an OCSP
    426     // responder over SSL.
    427     if (strcmp(http_protocol_variant, "http") != 0) {
    428       PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
    429       return NULL;
    430     }
    431 
    432     // TODO(ukai): If |host| is an IPv6 literal, we need to quote it with
    433     //  square brackets [].
    434     std::string url_string(base::StringPrintf("%s://%s:%d%s",
    435                                               http_protocol_variant,
    436                                               host_.c_str(),
    437                                               port_,
    438                                               path_and_query_string));
    439     VLOG(1) << "URL [" << url_string << "]";
    440     GURL url(url_string);
    441     return new OCSPRequestSession(
    442         url, http_request_method,
    443         base::TimeDelta::FromMilliseconds(PR_IntervalToMilliseconds(timeout)));
    444   }
    445 
    446 
    447  private:
    448   std::string host_;
    449   int port_;
    450 
    451   DISALLOW_COPY_AND_ASSIGN(OCSPServerSession);
    452 };
    453 
    454 OCSPIOLoop::OCSPIOLoop()
    455     : shutdown_(false),
    456       used_(false),
    457       io_loop_(MessageLoopForIO::current()) {
    458   DCHECK(io_loop_);
    459 }
    460 
    461 OCSPIOLoop::~OCSPIOLoop() {
    462   // IO thread was already deleted before the singleton is deleted
    463   // in AtExitManager.
    464   {
    465     base::AutoLock autolock(lock_);
    466     DCHECK(!io_loop_);
    467     DCHECK(!used_);
    468     DCHECK(shutdown_);
    469   }
    470 
    471   pthread_mutex_lock(&g_request_context_lock);
    472   DCHECK(!g_request_context);
    473   pthread_mutex_unlock(&g_request_context_lock);
    474 }
    475 
    476 void OCSPIOLoop::Shutdown() {
    477   // Safe to read outside lock since we only write on IO thread anyway.
    478   DCHECK(thread_checker_.CalledOnValidThread());
    479 
    480   // Prevent the worker thread from trying to access |io_loop_|.
    481   {
    482     base::AutoLock autolock(lock_);
    483     io_loop_ = NULL;
    484     used_ = false;
    485     shutdown_ = true;
    486   }
    487 
    488   CancelAllRequests();
    489 
    490   pthread_mutex_lock(&g_request_context_lock);
    491   g_request_context = NULL;
    492   pthread_mutex_unlock(&g_request_context_lock);
    493 }
    494 
    495 void OCSPIOLoop::PostTaskToIOLoop(
    496     const tracked_objects::Location& from_here, Task* task) {
    497   base::AutoLock autolock(lock_);
    498   if (io_loop_)
    499     io_loop_->PostTask(from_here, task);
    500 }
    501 
    502 void OCSPIOLoop::EnsureIOLoop() {
    503   base::AutoLock autolock(lock_);
    504   DCHECK_EQ(MessageLoopForIO::current(), io_loop_);
    505 }
    506 
    507 void OCSPIOLoop::AddRequest(OCSPRequestSession* request) {
    508   DCHECK(!ContainsKey(requests_, request));
    509   requests_.insert(request);
    510 }
    511 
    512 void OCSPIOLoop::RemoveRequest(OCSPRequestSession* request) {
    513   {
    514     // Ignore if we've already shutdown.
    515     base::AutoLock auto_lock(lock_);
    516     if (shutdown_)
    517       return;
    518   }
    519 
    520   DCHECK(ContainsKey(requests_, request));
    521   requests_.erase(request);
    522 }
    523 
    524 void OCSPIOLoop::CancelAllRequests() {
    525   std::set<OCSPRequestSession*> requests;
    526   requests.swap(requests_);
    527 
    528   for (std::set<OCSPRequestSession*>::iterator it = requests.begin();
    529        it != requests.end(); ++it)
    530     (*it)->CancelURLRequest();
    531 }
    532 
    533 OCSPNSSInitialization::OCSPNSSInitialization() {
    534   // NSS calls the functions in the function table to download certificates
    535   // or CRLs or talk to OCSP responders over HTTP.  These functions must
    536   // set an NSS/NSPR error code when they fail.  Otherwise NSS will get the
    537   // residual error code from an earlier failed function call.
    538   client_fcn_.version = 1;
    539   SEC_HttpClientFcnV1Struct *ft = &client_fcn_.fcnTable.ftable1;
    540   ft->createSessionFcn = OCSPCreateSession;
    541   ft->keepAliveSessionFcn = OCSPKeepAliveSession;
    542   ft->freeSessionFcn = OCSPFreeSession;
    543   ft->createFcn = OCSPCreate;
    544   ft->setPostDataFcn = OCSPSetPostData;
    545   ft->addHeaderFcn = OCSPAddHeader;
    546   ft->trySendAndReceiveFcn = OCSPTrySendAndReceive;
    547   ft->cancelFcn = NULL;
    548   ft->freeFcn = OCSPFree;
    549   SECStatus status = SEC_RegisterDefaultHttpClient(&client_fcn_);
    550   if (status != SECSuccess) {
    551     NOTREACHED() << "Error initializing OCSP: " << PR_GetError();
    552   }
    553 
    554   // Work around NSS bugs 524013 and 564334.  NSS incorrectly thinks the
    555   // CRLs for Network Solutions Certificate Authority have bad signatures,
    556   // which causes certificates issued by that CA to be reported as revoked.
    557   // By using OCSP for those certificates, which don't have AIA extensions,
    558   // we can work around these bugs.  See http://crbug.com/41730.
    559   CERT_StringFromCertFcn old_callback = NULL;
    560   status = CERT_RegisterAlternateOCSPAIAInfoCallBack(
    561       GetAlternateOCSPAIAInfo, &old_callback);
    562   if (status == SECSuccess) {
    563     DCHECK(!old_callback);
    564   } else {
    565     NOTREACHED() << "Error initializing OCSP: " << PR_GetError();
    566   }
    567 }
    568 
    569 OCSPNSSInitialization::~OCSPNSSInitialization() {}
    570 
    571 
    572 // OCSP Http Client functions.
    573 // Our Http Client functions operate in blocking mode.
    574 SECStatus OCSPCreateSession(const char* host, PRUint16 portnum,
    575                             SEC_HTTP_SERVER_SESSION* pSession) {
    576   VLOG(1) << "OCSP create session: host=" << host << " port=" << portnum;
    577   pthread_mutex_lock(&g_request_context_lock);
    578   net::URLRequestContext* request_context = g_request_context;
    579   pthread_mutex_unlock(&g_request_context_lock);
    580   if (request_context == NULL) {
    581     LOG(ERROR) << "No URLRequestContext for OCSP handler.";
    582     // The application failed to call SetURLRequestContextForOCSP, so we
    583     // can't create and use net::URLRequest.  PR_NOT_IMPLEMENTED_ERROR is not an
    584     // accurate error code for this error condition, but is close enough.
    585     PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
    586     return SECFailure;
    587   }
    588   *pSession = new OCSPServerSession(host, portnum);
    589   return SECSuccess;
    590 }
    591 
    592 SECStatus OCSPKeepAliveSession(SEC_HTTP_SERVER_SESSION session,
    593                                PRPollDesc **pPollDesc) {
    594   VLOG(1) << "OCSP keep alive";
    595   if (pPollDesc)
    596     *pPollDesc = NULL;
    597   return SECSuccess;
    598 }
    599 
    600 SECStatus OCSPFreeSession(SEC_HTTP_SERVER_SESSION session) {
    601   VLOG(1) << "OCSP free session";
    602   delete reinterpret_cast<OCSPServerSession*>(session);
    603   return SECSuccess;
    604 }
    605 
    606 SECStatus OCSPCreate(SEC_HTTP_SERVER_SESSION session,
    607                      const char* http_protocol_variant,
    608                      const char* path_and_query_string,
    609                      const char* http_request_method,
    610                      const PRIntervalTime timeout,
    611                      SEC_HTTP_REQUEST_SESSION* pRequest) {
    612   VLOG(1) << "OCSP create protocol=" << http_protocol_variant
    613           << " path_and_query=" << path_and_query_string
    614           << " http_request_method=" << http_request_method
    615           << " timeout=" << timeout;
    616   OCSPServerSession* ocsp_session =
    617       reinterpret_cast<OCSPServerSession*>(session);
    618 
    619   OCSPRequestSession* req = ocsp_session->CreateRequest(http_protocol_variant,
    620                                                         path_and_query_string,
    621                                                         http_request_method,
    622                                                         timeout);
    623   SECStatus rv = SECFailure;
    624   if (req) {
    625     req->AddRef();  // Release in OCSPFree().
    626     rv = SECSuccess;
    627   }
    628   *pRequest = req;
    629   return rv;
    630 }
    631 
    632 SECStatus OCSPSetPostData(SEC_HTTP_REQUEST_SESSION request,
    633                           const char* http_data,
    634                           const PRUint32 http_data_len,
    635                           const char* http_content_type) {
    636   VLOG(1) << "OCSP set post data len=" << http_data_len;
    637   OCSPRequestSession* req = reinterpret_cast<OCSPRequestSession*>(request);
    638 
    639   req->SetPostData(http_data, http_data_len, http_content_type);
    640   return SECSuccess;
    641 }
    642 
    643 SECStatus OCSPAddHeader(SEC_HTTP_REQUEST_SESSION request,
    644                         const char* http_header_name,
    645                         const char* http_header_value) {
    646   VLOG(1) << "OCSP add header name=" << http_header_name
    647           << " value=" << http_header_value;
    648   OCSPRequestSession* req = reinterpret_cast<OCSPRequestSession*>(request);
    649 
    650   req->AddHeader(http_header_name, http_header_value);
    651   return SECSuccess;
    652 }
    653 
    654 // Sets response of |req| in the output parameters.
    655 // It is helper routine for OCSP trySendAndReceiveFcn.
    656 // |http_response_data_len| could be used as input parameter.  If it has
    657 // non-zero value, it is considered as maximum size of |http_response_data|.
    658 SECStatus OCSPSetResponse(OCSPRequestSession* req,
    659                           PRUint16* http_response_code,
    660                           const char** http_response_content_type,
    661                           const char** http_response_headers,
    662                           const char** http_response_data,
    663                           PRUint32* http_response_data_len) {
    664   DCHECK(req->Finished());
    665   const std::string& data = req->http_response_data();
    666   if (http_response_data_len && *http_response_data_len) {
    667     if (*http_response_data_len < data.size()) {
    668       LOG(ERROR) << "response body too large: " << *http_response_data_len
    669                  << " < " << data.size();
    670       *http_response_data_len = data.size();
    671       PORT_SetError(SEC_ERROR_BAD_HTTP_RESPONSE);
    672       return SECFailure;
    673     }
    674   }
    675   VLOG(1) << "OCSP response "
    676           << " response_code=" << req->http_response_code()
    677           << " content_type=" << req->http_response_content_type()
    678           << " header=" << req->http_response_headers()
    679           << " data_len=" << data.size();
    680   if (http_response_code)
    681     *http_response_code = req->http_response_code();
    682   if (http_response_content_type)
    683     *http_response_content_type = req->http_response_content_type().c_str();
    684   if (http_response_headers)
    685     *http_response_headers = req->http_response_headers().c_str();
    686   if (http_response_data)
    687     *http_response_data = data.data();
    688   if (http_response_data_len)
    689     *http_response_data_len = data.size();
    690   return SECSuccess;
    691 }
    692 
    693 SECStatus OCSPTrySendAndReceive(SEC_HTTP_REQUEST_SESSION request,
    694                                 PRPollDesc** pPollDesc,
    695                                 PRUint16* http_response_code,
    696                                 const char** http_response_content_type,
    697                                 const char** http_response_headers,
    698                                 const char** http_response_data,
    699                                 PRUint32* http_response_data_len) {
    700   if (http_response_data_len) {
    701     // We must always set an output value, even on failure.  The output value 0
    702     // means the failure was unrelated to the acceptable response data length.
    703     *http_response_data_len = 0;
    704   }
    705 
    706   VLOG(1) << "OCSP try send and receive";
    707   OCSPRequestSession* req = reinterpret_cast<OCSPRequestSession*>(request);
    708   // We support blocking mode only.
    709   if (pPollDesc)
    710     *pPollDesc = NULL;
    711 
    712   if (req->Started() || req->Finished()) {
    713     // We support blocking mode only, so this function shouldn't be called
    714     // again when req has stareted or finished.
    715     NOTREACHED();
    716     PORT_SetError(SEC_ERROR_BAD_HTTP_RESPONSE);  // Simple approximation.
    717     return SECFailure;
    718   }
    719 
    720   const base::Time start_time = base::Time::Now();
    721   req->Start();
    722   if (!req->Wait() || req->http_response_code() == static_cast<PRUint16>(-1)) {
    723     // If the response code is -1, the request failed and there is no response.
    724     PORT_SetError(SEC_ERROR_BAD_HTTP_RESPONSE);  // Simple approximation.
    725     return SECFailure;
    726   }
    727   const base::TimeDelta duration = base::Time::Now() - start_time;
    728 
    729   // We want to know if this was:
    730   //   1) An OCSP request
    731   //   2) A CRL request
    732   //   3) A request for a missing intermediate certificate
    733   // There's no sure way to do this, so we use heuristics like MIME type and
    734   // URL.
    735   const char* mime_type = req->http_response_content_type().c_str();
    736   bool is_ocsp_resp =
    737       strcasecmp(mime_type, "application/ocsp-response") == 0;
    738   bool is_crl_resp = strcasecmp(mime_type, "application/x-pkcs7-crl") == 0 ||
    739                      strcasecmp(mime_type, "application/x-x509-crl") == 0 ||
    740                      strcasecmp(mime_type, "application/pkix-crl") == 0;
    741   bool is_crt_resp =
    742       strcasecmp(mime_type, "application/x-x509-ca-cert") == 0 ||
    743       strcasecmp(mime_type, "application/x-x509-server-cert") == 0 ||
    744       strcasecmp(mime_type, "application/pkix-cert") == 0 ||
    745       strcasecmp(mime_type, "application/pkcs7-mime") == 0;
    746   bool known_resp_type = is_crt_resp || is_crt_resp || is_ocsp_resp;
    747 
    748   bool crl_in_url = false, crt_in_url = false, ocsp_in_url = false,
    749        have_url_hint = false;
    750   if (!known_resp_type) {
    751     const std::string path = req->url().path();
    752     const std::string host = req->url().host();
    753     crl_in_url = strcasestr(path.c_str(), ".crl") != NULL;
    754     crt_in_url = strcasestr(path.c_str(), ".crt") != NULL ||
    755                  strcasestr(path.c_str(), ".p7c") != NULL ||
    756                  strcasestr(path.c_str(), ".cer") != NULL;
    757     ocsp_in_url = strcasestr(host.c_str(), "ocsp") != NULL;
    758     have_url_hint = crl_in_url || crt_in_url || ocsp_in_url;
    759   }
    760 
    761   if (is_ocsp_resp ||
    762       (!known_resp_type && (ocsp_in_url ||
    763                             (!have_url_hint &&
    764                              req->http_request_method() == "POST")))) {
    765     UMA_HISTOGRAM_TIMES("Net.OCSPRequestTimeMs", duration);
    766   } else if (is_crl_resp || (!known_resp_type && crl_in_url)) {
    767     UMA_HISTOGRAM_TIMES("Net.CRLRequestTimeMs", duration);
    768   } else if (is_crt_resp || (!known_resp_type && crt_in_url)) {
    769     UMA_HISTOGRAM_TIMES("Net.CRTRequestTimeMs", duration);
    770   } else {
    771     UMA_HISTOGRAM_TIMES("Net.UnknownTypeRequestTimeMs", duration);
    772   }
    773 
    774   return OCSPSetResponse(
    775       req, http_response_code,
    776       http_response_content_type,
    777       http_response_headers,
    778       http_response_data,
    779       http_response_data_len);
    780 }
    781 
    782 SECStatus OCSPFree(SEC_HTTP_REQUEST_SESSION request) {
    783   VLOG(1) << "OCSP free";
    784   OCSPRequestSession* req = reinterpret_cast<OCSPRequestSession*>(request);
    785   req->Cancel();
    786   req->Release();
    787   return SECSuccess;
    788 }
    789 
    790 // Data for GetAlternateOCSPAIAInfo.
    791 
    792 // CN=Network Solutions Certificate Authority,O=Network Solutions L.L.C.,C=US
    793 //
    794 // There are two CAs with this name.  Their key IDs are listed next.
    795 const unsigned char network_solutions_ca_name[] = {
    796   0x30, 0x62, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04,
    797   0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x21, 0x30, 0x1f, 0x06,
    798   0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x4e, 0x65, 0x74, 0x77,
    799   0x6f, 0x72, 0x6b, 0x20, 0x53, 0x6f, 0x6c, 0x75, 0x74, 0x69,
    800   0x6f, 0x6e, 0x73, 0x20, 0x4c, 0x2e, 0x4c, 0x2e, 0x43, 0x2e,
    801   0x31, 0x30, 0x30, 0x2e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
    802   0x27, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x53,
    803   0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x43,
    804   0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65,
    805   0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79
    806 };
    807 const unsigned int network_solutions_ca_name_len = 100;
    808 
    809 // This CA is an intermediate CA, subordinate to UTN-USERFirst-Hardware.
    810 const unsigned char network_solutions_ca_key_id[] = {
    811   0x3c, 0x41, 0xe2, 0x8f, 0x08, 0x08, 0xa9, 0x4c, 0x25, 0x89,
    812   0x8d, 0x6d, 0xc5, 0x38, 0xd0, 0xfc, 0x85, 0x8c, 0x62, 0x17
    813 };
    814 const unsigned int network_solutions_ca_key_id_len = 20;
    815 
    816 // This CA is a root CA.  It is also cross-certified by
    817 // UTN-USERFirst-Hardware.
    818 const unsigned char network_solutions_ca_key_id2[] = {
    819   0x21, 0x30, 0xc9, 0xfb, 0x00, 0xd7, 0x4e, 0x98, 0xda, 0x87,
    820   0xaa, 0x2a, 0xd0, 0xa7, 0x2e, 0xb1, 0x40, 0x31, 0xa7, 0x4c
    821 };
    822 const unsigned int network_solutions_ca_key_id2_len = 20;
    823 
    824 // An entry in our OCSP responder table.  |issuer| and |issuer_key_id| are
    825 // the key.  |ocsp_url| is the value.
    826 struct OCSPResponderTableEntry {
    827   SECItem issuer;
    828   SECItem issuer_key_id;
    829   const char *ocsp_url;
    830 };
    831 
    832 const OCSPResponderTableEntry g_ocsp_responder_table[] = {
    833   {
    834     {
    835       siBuffer,
    836       const_cast<unsigned char*>(network_solutions_ca_name),
    837       network_solutions_ca_name_len
    838     },
    839     {
    840       siBuffer,
    841       const_cast<unsigned char*>(network_solutions_ca_key_id),
    842       network_solutions_ca_key_id_len
    843     },
    844     "http://ocsp.netsolssl.com"
    845   },
    846   {
    847     {
    848       siBuffer,
    849       const_cast<unsigned char*>(network_solutions_ca_name),
    850       network_solutions_ca_name_len
    851     },
    852     {
    853       siBuffer,
    854       const_cast<unsigned char*>(network_solutions_ca_key_id2),
    855       network_solutions_ca_key_id2_len
    856     },
    857     "http://ocsp.netsolssl.com"
    858   }
    859 };
    860 
    861 char* GetAlternateOCSPAIAInfo(CERTCertificate *cert) {
    862   if (cert && !cert->isRoot && cert->authKeyID) {
    863     for (unsigned int i=0; i < arraysize(g_ocsp_responder_table); i++) {
    864       if (SECITEM_CompareItem(&g_ocsp_responder_table[i].issuer,
    865                               &cert->derIssuer) == SECEqual &&
    866           SECITEM_CompareItem(&g_ocsp_responder_table[i].issuer_key_id,
    867                               &cert->authKeyID->keyID) == SECEqual) {
    868         return PORT_Strdup(g_ocsp_responder_table[i].ocsp_url);
    869       }
    870     }
    871   }
    872 
    873   return NULL;
    874 }
    875 
    876 }  // anonymous namespace
    877 
    878 namespace net {
    879 
    880 void SetMessageLoopForOCSP() {
    881   // Must have a MessageLoopForIO.
    882   DCHECK(MessageLoopForIO::current());
    883 
    884   bool used = g_ocsp_io_loop.Get().used();
    885 
    886   // Should not be called when g_ocsp_io_loop has already been used.
    887   DCHECK(!used);
    888 }
    889 
    890 void EnsureOCSPInit() {
    891   g_ocsp_io_loop.Get().StartUsing();
    892   g_ocsp_nss_initialization.Get();
    893 }
    894 
    895 void ShutdownOCSP() {
    896   g_ocsp_io_loop.Get().Shutdown();
    897 }
    898 
    899 // This function would be called before NSS initialization.
    900 void SetURLRequestContextForOCSP(URLRequestContext* request_context) {
    901   pthread_mutex_lock(&g_request_context_lock);
    902   if (request_context) {
    903     DCHECK(request_context->is_main());
    904     DCHECK(!g_request_context);
    905   }
    906   g_request_context = request_context;
    907   pthread_mutex_unlock(&g_request_context_lock);
    908 }
    909 
    910 URLRequestContext* GetURLRequestContextForOCSP() {
    911   pthread_mutex_lock(&g_request_context_lock);
    912   URLRequestContext* request_context = g_request_context;
    913   pthread_mutex_unlock(&g_request_context_lock);
    914   DCHECK(!request_context || request_context->is_main());
    915   return request_context;
    916 }
    917 
    918 }  // namespace net
    919