• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
38 namespace {
39 
40 // Protects |g_request_context|.
41 pthread_mutex_t g_request_context_lock = PTHREAD_MUTEX_INITIALIZER;
42 static net::URLRequestContext* g_request_context = NULL;
43 
44 class OCSPRequestSession;
45 
46 class OCSPIOLoop {
47  public:
StartUsing()48   void StartUsing() {
49     base::AutoLock autolock(lock_);
50     used_ = true;
51   }
52 
53   // Called on IO loop.
54   void Shutdown();
55 
used() const56   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 
88 base::LazyInstance<OCSPIOLoop, base::LeakyLazyInstanceTraits<OCSPIOLoop> >
89     g_ocsp_io_loop(base::LINKER_INITIALIZED);
90 
91 const 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 
97 SECStatus OCSPCreateSession(const char* host, PRUint16 portnum,
98                             SEC_HTTP_SERVER_SESSION* pSession);
99 SECStatus OCSPKeepAliveSession(SEC_HTTP_SERVER_SESSION session,
100                                PRPollDesc **pPollDesc);
101 SECStatus OCSPFreeSession(SEC_HTTP_SERVER_SESSION session);
102 
103 SECStatus 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);
109 SECStatus OCSPSetPostData(SEC_HTTP_REQUEST_SESSION request,
110                           const char* http_data,
111                           const PRUint32 http_data_len,
112                           const char* http_content_type);
113 SECStatus OCSPAddHeader(SEC_HTTP_REQUEST_SESSION request,
114                         const char* http_header_name,
115                         const char* http_header_value);
116 SECStatus 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);
123 SECStatus OCSPFree(SEC_HTTP_REQUEST_SESSION request);
124 
125 char* GetAlternateOCSPAIAInfo(CERTCertificate *cert);
126 
127 class 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 
139 base::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.
147 class OCSPRequestSession
148     : public base::RefCountedThreadSafe<OCSPRequestSession>,
149       public net::URLRequest::Delegate {
150  public:
OCSPRequestSession(const GURL & url,const char * http_request_method,base::TimeDelta timeout)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 
SetPostData(const char * http_data,PRUint32 http_data_len,const char * http_content_type)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 
AddHeader(const char * http_header_name,const char * http_header_value)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 
Start()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 
Started() const185   bool Started() const {
186     return request_ != NULL;
187   }
188 
Cancel()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 
Finished() const195   bool Finished() const {
196     base::AutoLock autolock(lock_);
197     return finished_;
198   }
199 
Wait()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 
url() const219   const GURL& url() const {
220     return url_;
221   }
222 
http_request_method() const223   const std::string& http_request_method() const {
224     return http_request_method_;
225   }
226 
timeout() const227   base::TimeDelta timeout() const {
228     return timeout_;
229   }
230 
http_response_code() const231   PRUint16 http_response_code() const {
232     DCHECK(finished_);
233     return response_code_;
234   }
235 
http_response_content_type() const236   const std::string& http_response_content_type() const {
237     DCHECK(finished_);
238     return response_content_type_;
239   }
240 
http_response_headers() const241   const std::string& http_response_headers() const {
242     DCHECK(finished_);
243     return response_headers_->raw_headers();
244   }
245 
http_response_data() const246   const std::string& http_response_data() const {
247     DCHECK(finished_);
248     return data_;
249   }
250 
OnReceivedRedirect(net::URLRequest * request,const GURL & new_url,bool * defer_redirect)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 
OnResponseStarted(net::URLRequest * request)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 
OnReadCompleted(net::URLRequest * request,int bytes_read)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.
CancelURLRequest()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 
~OCSPRequestSession()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_|.
CancelLocked()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.
StartURLRequest()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.
414 class OCSPServerSession {
415  public:
OCSPServerSession(const char * host,PRUint16 port)416   OCSPServerSession(const char* host, PRUint16 port)
417       : host_(host), port_(port) {}
~OCSPServerSession()418   ~OCSPServerSession() {}
419 
CreateRequest(const char * http_protocol_variant,const char * path_and_query_string,const char * http_request_method,const PRIntervalTime timeout)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 
OCSPIOLoop()454 OCSPIOLoop::OCSPIOLoop()
455     : shutdown_(false),
456       used_(false),
457       io_loop_(MessageLoopForIO::current()) {
458   DCHECK(io_loop_);
459 }
460 
~OCSPIOLoop()461 OCSPIOLoop::~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 
Shutdown()476 void 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 
PostTaskToIOLoop(const tracked_objects::Location & from_here,Task * task)495 void 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 
EnsureIOLoop()502 void OCSPIOLoop::EnsureIOLoop() {
503   base::AutoLock autolock(lock_);
504   DCHECK_EQ(MessageLoopForIO::current(), io_loop_);
505 }
506 
AddRequest(OCSPRequestSession * request)507 void OCSPIOLoop::AddRequest(OCSPRequestSession* request) {
508   DCHECK(!ContainsKey(requests_, request));
509   requests_.insert(request);
510 }
511 
RemoveRequest(OCSPRequestSession * request)512 void 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 
CancelAllRequests()524 void 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 
OCSPNSSInitialization()533 OCSPNSSInitialization::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 
~OCSPNSSInitialization()569 OCSPNSSInitialization::~OCSPNSSInitialization() {}
570 
571 
572 // OCSP Http Client functions.
573 // Our Http Client functions operate in blocking mode.
OCSPCreateSession(const char * host,PRUint16 portnum,SEC_HTTP_SERVER_SESSION * pSession)574 SECStatus 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 
OCSPKeepAliveSession(SEC_HTTP_SERVER_SESSION session,PRPollDesc ** pPollDesc)592 SECStatus 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 
OCSPFreeSession(SEC_HTTP_SERVER_SESSION session)600 SECStatus OCSPFreeSession(SEC_HTTP_SERVER_SESSION session) {
601   VLOG(1) << "OCSP free session";
602   delete reinterpret_cast<OCSPServerSession*>(session);
603   return SECSuccess;
604 }
605 
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)606 SECStatus 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 
OCSPSetPostData(SEC_HTTP_REQUEST_SESSION request,const char * http_data,const PRUint32 http_data_len,const char * http_content_type)632 SECStatus 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 
OCSPAddHeader(SEC_HTTP_REQUEST_SESSION request,const char * http_header_name,const char * http_header_value)643 SECStatus 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|.
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)658 SECStatus 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 
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)693 SECStatus 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 
OCSPFree(SEC_HTTP_REQUEST_SESSION request)782 SECStatus 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.
795 const 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 };
807 const unsigned int network_solutions_ca_name_len = 100;
808 
809 // This CA is an intermediate CA, subordinate to UTN-USERFirst-Hardware.
810 const 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 };
814 const 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.
818 const 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 };
822 const 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.
826 struct OCSPResponderTableEntry {
827   SECItem issuer;
828   SECItem issuer_key_id;
829   const char *ocsp_url;
830 };
831 
832 const 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 
GetAlternateOCSPAIAInfo(CERTCertificate * cert)861 char* 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 
878 namespace net {
879 
SetMessageLoopForOCSP()880 void 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 
EnsureOCSPInit()890 void EnsureOCSPInit() {
891   g_ocsp_io_loop.Get().StartUsing();
892   g_ocsp_nss_initialization.Get();
893 }
894 
ShutdownOCSP()895 void ShutdownOCSP() {
896   g_ocsp_io_loop.Get().Shutdown();
897 }
898 
899 // This function would be called before NSS initialization.
SetURLRequestContextForOCSP(URLRequestContext * request_context)900 void 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 
GetURLRequestContextForOCSP()910 URLRequestContext* 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