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