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