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