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