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