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