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