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