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