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