• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
StartUsing()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 
used() const70   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.
ReuseForTesting()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 
120 base::LazyInstance<OCSPIOLoop>::Leaky
121     g_ocsp_io_loop = LAZY_INSTANCE_INITIALIZER;
122 
123 const 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 
129 SECStatus OCSPCreateSession(const char* host, PRUint16 portnum,
130                             SEC_HTTP_SERVER_SESSION* pSession);
131 SECStatus OCSPKeepAliveSession(SEC_HTTP_SERVER_SESSION session,
132                                PRPollDesc **pPollDesc);
133 SECStatus OCSPFreeSession(SEC_HTTP_SERVER_SESSION session);
134 
135 SECStatus 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);
141 SECStatus OCSPSetPostData(SEC_HTTP_REQUEST_SESSION request,
142                           const char* http_data,
143                           const PRUint32 http_data_len,
144                           const char* http_content_type);
145 SECStatus OCSPAddHeader(SEC_HTTP_REQUEST_SESSION request,
146                         const char* http_header_name,
147                         const char* http_header_value);
148 SECStatus 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);
155 SECStatus OCSPFree(SEC_HTTP_REQUEST_SESSION request);
156 
157 char* GetAlternateOCSPAIAInfo(CERTCertificate *cert);
158 
159 class 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 
171 base::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.
179 class OCSPRequestSession
180     : public base::RefCountedThreadSafe<OCSPRequestSession>,
181       public URLRequest::Delegate {
182  public:
OCSPRequestSession(const GURL & url,const char * http_request_method,base::TimeDelta timeout)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 
SetPostData(const char * http_data,PRUint32 http_data_len,const char * http_content_type)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 
AddHeader(const char * http_header_name,const char * http_header_value)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 
Start()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 
Started() const219   bool Started() const {
220     return request_ != NULL;
221   }
222 
Cancel()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 
Finished() const229   bool Finished() const {
230     base::AutoLock autolock(lock_);
231     return finished_;
232   }
233 
Wait()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 
url() const253   const GURL& url() const {
254     return url_;
255   }
256 
http_request_method() const257   const std::string& http_request_method() const {
258     return http_request_method_;
259   }
260 
timeout() const261   base::TimeDelta timeout() const {
262     return timeout_;
263   }
264 
http_response_code() const265   PRUint16 http_response_code() const {
266     DCHECK(finished_);
267     return response_code_;
268   }
269 
http_response_content_type() const270   const std::string& http_response_content_type() const {
271     DCHECK(finished_);
272     return response_content_type_;
273   }
274 
http_response_headers() const275   const std::string& http_response_headers() const {
276     DCHECK(finished_);
277     return response_headers_->raw_headers();
278   }
279 
http_response_data() const280   const std::string& http_response_data() const {
281     DCHECK(finished_);
282     return data_;
283   }
284 
OnReceivedRedirect(URLRequest * request,const GURL & new_url,bool * defer_redirect)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 
OnResponseStarted(URLRequest * request)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 
OnReadCompleted(URLRequest * request,int bytes_read)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.
CancelURLRequest()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 
~OCSPRequestSession()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_|.
CancelLocked()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.
StartURLRequest()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.
453 class OCSPServerSession {
454  public:
OCSPServerSession(const char * host,PRUint16 port)455   OCSPServerSession(const char* host, PRUint16 port)
456       : host_and_port_(host, port) {}
~OCSPServerSession()457   ~OCSPServerSession() {}
458 
CreateRequest(const char * http_protocol_variant,const char * path_and_query_string,const char * http_request_method,const PRIntervalTime timeout)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 
OCSPIOLoop()495 OCSPIOLoop::OCSPIOLoop()
496     : shutdown_(false),
497       used_(false),
498       io_loop_(NULL) {
499 }
500 
~OCSPIOLoop()501 OCSPIOLoop::~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 
Shutdown()516 void 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 
PostTaskToIOLoop(const tracked_objects::Location & from_here,const base::Closure & task)535 void 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 
EnsureIOLoop()542 void OCSPIOLoop::EnsureIOLoop() {
543   base::AutoLock autolock(lock_);
544   DCHECK_EQ(base::MessageLoopForIO::current(), io_loop_);
545 }
546 
AddRequest(OCSPRequestSession * request)547 void OCSPIOLoop::AddRequest(OCSPRequestSession* request) {
548   DCHECK(!ContainsKey(requests_, request));
549   requests_.insert(request);
550 }
551 
RemoveRequest(OCSPRequestSession * request)552 void OCSPIOLoop::RemoveRequest(OCSPRequestSession* request) {
553   DCHECK(ContainsKey(requests_, request));
554   requests_.erase(request);
555 }
556 
CancelAllRequests()557 void OCSPIOLoop::CancelAllRequests() {
558   // CancelURLRequest() always removes the request from the requests_
559   // set synchronously.
560   while (!requests_.empty())
561     (*requests_.begin())->CancelURLRequest();
562 }
563 
OCSPNSSInitialization()564 OCSPNSSInitialization::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 
~OCSPNSSInitialization()600 OCSPNSSInitialization::~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.
OCSPCreateSession(const char * host,PRUint16 portnum,SEC_HTTP_SERVER_SESSION * pSession)610 SECStatus 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 
OCSPKeepAliveSession(SEC_HTTP_SERVER_SESSION session,PRPollDesc ** pPollDesc)629 SECStatus 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 
OCSPFreeSession(SEC_HTTP_SERVER_SESSION session)637 SECStatus OCSPFreeSession(SEC_HTTP_SERVER_SESSION session) {
638   VLOG(1) << "OCSP free session";
639   delete reinterpret_cast<OCSPServerSession*>(session);
640   return SECSuccess;
641 }
642 
OCSPCreate(SEC_HTTP_SERVER_SESSION session,const char * http_protocol_variant,const char * path_and_query_string,const char * http_request_method,const PRIntervalTime timeout,SEC_HTTP_REQUEST_SESSION * pRequest)643 SECStatus 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 
OCSPSetPostData(SEC_HTTP_REQUEST_SESSION request,const char * http_data,const PRUint32 http_data_len,const char * http_content_type)669 SECStatus 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 
OCSPAddHeader(SEC_HTTP_REQUEST_SESSION request,const char * http_header_name,const char * http_header_value)680 SECStatus 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|.
OCSPSetResponse(OCSPRequestSession * req,PRUint16 * http_response_code,const char ** http_response_content_type,const char ** http_response_headers,const char ** http_response_data,PRUint32 * http_response_data_len)695 SECStatus 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 
OCSPTrySendAndReceive(SEC_HTTP_REQUEST_SESSION request,PRPollDesc ** pPollDesc,PRUint16 * http_response_code,const char ** http_response_content_type,const char ** http_response_headers,const char ** http_response_data,PRUint32 * http_response_data_len)730 SECStatus 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 
OCSPFree(SEC_HTTP_REQUEST_SESSION request)850 SECStatus 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.
863 const 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 };
875 const unsigned int network_solutions_ca_name_len = 100;
876 
877 // This CA is an intermediate CA, subordinate to UTN-USERFirst-Hardware.
878 const 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 };
882 const 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.
886 const 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 };
890 const 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.
894 struct OCSPResponderTableEntry {
895   SECItem issuer;
896   SECItem issuer_key_id;
897   const char *ocsp_url;
898 };
899 
900 const 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 
GetAlternateOCSPAIAInfo(CERTCertificate * cert)929 char* 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 
SetMessageLoopForNSSHttpIO()946 void 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 
EnsureNSSHttpIOInit()956 void EnsureNSSHttpIOInit() {
957   g_ocsp_io_loop.Get().StartUsing();
958   g_ocsp_nss_initialization.Get();
959 }
960 
ShutdownNSSHttpIO()961 void ShutdownNSSHttpIO() {
962   g_ocsp_io_loop.Get().Shutdown();
963 }
964 
ResetNSSHttpIOForTesting()965 void ResetNSSHttpIOForTesting() {
966   g_ocsp_io_loop.Get().ReuseForTesting();
967 }
968 
969 // This function would be called before NSS initialization.
SetURLRequestContextForNSSHttpIO(URLRequestContext * request_context)970 void 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