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