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