Disable PDF tests that are failing after combining PDF plugin into chrome.
[chromium-blink-merge.git] / net / ocsp / nss_ocsp.cc
blob18a476d6e6738e014c6c3431d28923d97090c85a
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/memory/scoped_ptr.h"
24 #include "base/message_loop/message_loop.h"
25 #include "base/metrics/histogram.h"
26 #include "base/profiler/scoped_tracker.h"
27 #include "base/stl_util.h"
28 #include "base/strings/string_util.h"
29 #include "base/strings/stringprintf.h"
30 #include "base/synchronization/condition_variable.h"
31 #include "base/synchronization/lock.h"
32 #include "base/threading/thread_checker.h"
33 #include "base/time/time.h"
34 #include "net/base/elements_upload_data_stream.h"
35 #include "net/base/host_port_pair.h"
36 #include "net/base/io_buffer.h"
37 #include "net/base/load_flags.h"
38 #include "net/base/request_priority.h"
39 #include "net/base/upload_bytes_element_reader.h"
40 #include "net/http/http_request_headers.h"
41 #include "net/http/http_response_headers.h"
42 #include "net/url_request/redirect_info.h"
43 #include "net/url_request/url_request.h"
44 #include "net/url_request/url_request_context.h"
45 #include "url/gurl.h"
47 namespace net {
49 namespace {
51 // Protects |g_request_context|.
52 pthread_mutex_t g_request_context_lock = PTHREAD_MUTEX_INITIALIZER;
53 URLRequestContext* g_request_context = NULL;
55 // The default timeout for network fetches in NSS is 60 seconds. Choose a
56 // saner upper limit for OCSP/CRL/AIA fetches.
57 const int kNetworkFetchTimeoutInSecs = 15;
59 class OCSPRequestSession;
61 class OCSPIOLoop {
62 public:
63 void StartUsing() {
64 base::AutoLock autolock(lock_);
65 used_ = true;
66 io_loop_ = base::MessageLoopForIO::current();
67 DCHECK(io_loop_);
70 // Called on IO loop.
71 void Shutdown();
73 bool used() const {
74 base::AutoLock autolock(lock_);
75 return used_;
78 // Called from worker thread.
79 void PostTaskToIOLoop(const tracked_objects::Location& from_here,
80 const base::Closure& task);
82 void EnsureIOLoop();
84 void AddRequest(OCSPRequestSession* request);
85 void RemoveRequest(OCSPRequestSession* request);
87 // Clears internal state and calls |StartUsing()|. Should be called only in
88 // the context of testing.
89 void ReuseForTesting() {
91 base::AutoLock autolock(lock_);
92 DCHECK(base::MessageLoopForIO::current());
93 thread_checker_.DetachFromThread();
95 // CalledOnValidThread is the only available API to reassociate
96 // thread_checker_ with the current thread. Result ignored intentionally.
97 ignore_result(thread_checker_.CalledOnValidThread());
98 shutdown_ = false;
99 used_ = false;
101 StartUsing();
104 private:
105 friend struct base::DefaultLazyInstanceTraits<OCSPIOLoop>;
107 OCSPIOLoop();
108 ~OCSPIOLoop();
110 void CancelAllRequests();
112 mutable base::Lock lock_;
113 bool shutdown_; // Protected by |lock_|.
114 std::set<OCSPRequestSession*> requests_; // Protected by |lock_|.
115 bool used_; // Protected by |lock_|.
116 // This should not be modified after |used_|.
117 base::MessageLoopForIO* io_loop_; // Protected by |lock_|.
118 base::ThreadChecker thread_checker_;
120 DISALLOW_COPY_AND_ASSIGN(OCSPIOLoop);
123 base::LazyInstance<OCSPIOLoop>::Leaky
124 g_ocsp_io_loop = LAZY_INSTANCE_INITIALIZER;
126 const int kRecvBufferSize = 4096;
128 // All OCSP handlers should be called in the context of
129 // CertVerifier's thread (i.e. worker pool, not on the I/O thread).
130 // It supports blocking mode only.
132 SECStatus OCSPCreateSession(const char* host, PRUint16 portnum,
133 SEC_HTTP_SERVER_SESSION* pSession);
134 SECStatus OCSPKeepAliveSession(SEC_HTTP_SERVER_SESSION session,
135 PRPollDesc **pPollDesc);
136 SECStatus OCSPFreeSession(SEC_HTTP_SERVER_SESSION session);
138 SECStatus OCSPCreate(SEC_HTTP_SERVER_SESSION session,
139 const char* http_protocol_variant,
140 const char* path_and_query_string,
141 const char* http_request_method,
142 const PRIntervalTime timeout,
143 SEC_HTTP_REQUEST_SESSION* pRequest);
144 SECStatus OCSPSetPostData(SEC_HTTP_REQUEST_SESSION request,
145 const char* http_data,
146 const PRUint32 http_data_len,
147 const char* http_content_type);
148 SECStatus OCSPAddHeader(SEC_HTTP_REQUEST_SESSION request,
149 const char* http_header_name,
150 const char* http_header_value);
151 SECStatus OCSPTrySendAndReceive(SEC_HTTP_REQUEST_SESSION request,
152 PRPollDesc** pPollDesc,
153 PRUint16* http_response_code,
154 const char** http_response_content_type,
155 const char** http_response_headers,
156 const char** http_response_data,
157 PRUint32* http_response_data_len);
158 SECStatus OCSPFree(SEC_HTTP_REQUEST_SESSION request);
160 char* GetAlternateOCSPAIAInfo(CERTCertificate *cert);
162 class OCSPNSSInitialization {
163 private:
164 friend struct base::DefaultLazyInstanceTraits<OCSPNSSInitialization>;
166 OCSPNSSInitialization();
167 ~OCSPNSSInitialization();
169 SEC_HttpClientFcn client_fcn_;
171 DISALLOW_COPY_AND_ASSIGN(OCSPNSSInitialization);
174 base::LazyInstance<OCSPNSSInitialization> g_ocsp_nss_initialization =
175 LAZY_INSTANCE_INITIALIZER;
177 // Concrete class for SEC_HTTP_REQUEST_SESSION.
178 // Public methods except virtual methods of URLRequest::Delegate
179 // (On* methods) run on certificate verifier thread (worker thread).
180 // Virtual methods of URLRequest::Delegate and private methods run
181 // on IO thread.
182 class OCSPRequestSession
183 : public base::RefCountedThreadSafe<OCSPRequestSession>,
184 public URLRequest::Delegate {
185 public:
186 OCSPRequestSession(const GURL& url,
187 const char* http_request_method,
188 base::TimeDelta timeout)
189 : url_(url),
190 http_request_method_(http_request_method),
191 timeout_(timeout),
192 buffer_(new IOBuffer(kRecvBufferSize)),
193 response_code_(-1),
194 cv_(&lock_),
195 io_loop_(NULL),
196 finished_(false) {}
198 void SetPostData(const char* http_data, PRUint32 http_data_len,
199 const char* http_content_type) {
200 // |upload_content_| should not be modified if |request_| is active.
201 DCHECK(!request_);
202 upload_content_.assign(http_data, http_data_len);
203 upload_content_type_.assign(http_content_type);
206 void AddHeader(const char* http_header_name, const char* http_header_value) {
207 extra_request_headers_.SetHeader(http_header_name,
208 http_header_value);
211 void Start() {
212 // At this point, it runs on worker thread.
213 // |io_loop_| was initialized to be NULL in constructor, and
214 // set only in StartURLRequest, so no need to lock |lock_| here.
215 DCHECK(!io_loop_);
216 g_ocsp_io_loop.Get().PostTaskToIOLoop(
217 FROM_HERE,
218 base::Bind(&OCSPRequestSession::StartURLRequest, this));
221 bool Started() const {
222 return request_.get() != NULL;
225 void Cancel() {
226 // IO thread may set |io_loop_| to NULL, so protect by |lock_|.
227 base::AutoLock autolock(lock_);
228 CancelLocked();
231 bool Finished() const {
232 base::AutoLock autolock(lock_);
233 return finished_;
236 bool Wait() {
237 base::TimeDelta timeout = timeout_;
238 base::AutoLock autolock(lock_);
239 while (!finished_) {
240 base::TimeTicks last_time = base::TimeTicks::Now();
241 cv_.TimedWait(timeout);
242 // Check elapsed time
243 base::TimeDelta elapsed_time = base::TimeTicks::Now() - last_time;
244 timeout -= elapsed_time;
245 if (timeout < base::TimeDelta()) {
246 VLOG(1) << "OCSP Timed out";
247 if (!finished_)
248 CancelLocked();
249 break;
252 return finished_;
255 const GURL& url() const {
256 return url_;
259 const std::string& http_request_method() const {
260 return http_request_method_;
263 base::TimeDelta timeout() const {
264 return timeout_;
267 PRUint16 http_response_code() const {
268 DCHECK(finished_);
269 return response_code_;
272 const std::string& http_response_content_type() const {
273 DCHECK(finished_);
274 return response_content_type_;
277 const std::string& http_response_headers() const {
278 DCHECK(finished_);
279 return response_headers_->raw_headers();
282 const std::string& http_response_data() const {
283 DCHECK(finished_);
284 return data_;
287 void OnReceivedRedirect(URLRequest* request,
288 const RedirectInfo& redirect_info,
289 bool* defer_redirect) override {
290 DCHECK_EQ(request_.get(), request);
291 DCHECK_EQ(base::MessageLoopForIO::current(), io_loop_);
293 if (!redirect_info.new_url.SchemeIs("http")) {
294 // Prevent redirects to non-HTTP schemes, including HTTPS. This matches
295 // the initial check in OCSPServerSession::CreateRequest().
296 CancelURLRequest();
300 void OnResponseStarted(URLRequest* request) override {
301 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed.
302 tracked_objects::ScopedTracker tracking_profile(
303 FROM_HERE_WITH_EXPLICIT_FUNCTION(
304 "423948 OCSPRequestSession::OnResponseStarted"));
306 DCHECK_EQ(request_.get(), request);
307 DCHECK_EQ(base::MessageLoopForIO::current(), io_loop_);
309 int bytes_read = 0;
310 if (request->status().is_success()) {
311 response_code_ = request_->GetResponseCode();
312 response_headers_ = request_->response_headers();
313 response_headers_->GetMimeType(&response_content_type_);
314 request_->Read(buffer_.get(), kRecvBufferSize, &bytes_read);
316 OnReadCompleted(request_.get(), bytes_read);
319 void OnReadCompleted(URLRequest* request, int bytes_read) override {
320 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed.
321 tracked_objects::ScopedTracker tracking_profile(
322 FROM_HERE_WITH_EXPLICIT_FUNCTION(
323 "423948 OCSPRequestSession::OnReadCompleted"));
325 DCHECK_EQ(request_.get(), request);
326 DCHECK_EQ(base::MessageLoopForIO::current(), io_loop_);
328 do {
329 if (!request_->status().is_success() || bytes_read <= 0)
330 break;
331 data_.append(buffer_->data(), bytes_read);
332 } while (request_->Read(buffer_.get(), kRecvBufferSize, &bytes_read));
334 if (!request_->status().is_io_pending()) {
335 request_.reset();
336 g_ocsp_io_loop.Get().RemoveRequest(this);
338 base::AutoLock autolock(lock_);
339 finished_ = true;
340 io_loop_ = NULL;
342 cv_.Signal();
343 Release(); // Balanced with StartURLRequest().
347 // Must be called on the IO loop thread.
348 void CancelURLRequest() {
349 #ifndef NDEBUG
351 base::AutoLock autolock(lock_);
352 if (io_loop_)
353 DCHECK_EQ(base::MessageLoopForIO::current(), io_loop_);
355 #endif
356 if (request_) {
357 request_.reset();
358 g_ocsp_io_loop.Get().RemoveRequest(this);
360 base::AutoLock autolock(lock_);
361 finished_ = true;
362 io_loop_ = NULL;
364 cv_.Signal();
365 Release(); // Balanced with StartURLRequest().
369 private:
370 friend class base::RefCountedThreadSafe<OCSPRequestSession>;
372 ~OCSPRequestSession() override {
373 // When this destructor is called, there should be only one thread that has
374 // a reference to this object, and so that thread doesn't need to lock
375 // |lock_| here.
376 DCHECK(!request_);
377 DCHECK(!io_loop_);
380 // Must call this method while holding |lock_|.
381 void CancelLocked() {
382 lock_.AssertAcquired();
383 if (io_loop_) {
384 io_loop_->PostTask(
385 FROM_HERE,
386 base::Bind(&OCSPRequestSession::CancelURLRequest, this));
390 // Runs on |g_ocsp_io_loop|'s IO loop.
391 void StartURLRequest() {
392 DCHECK(!request_);
394 pthread_mutex_lock(&g_request_context_lock);
395 URLRequestContext* url_request_context = g_request_context;
396 pthread_mutex_unlock(&g_request_context_lock);
398 if (url_request_context == NULL)
399 return;
402 base::AutoLock autolock(lock_);
403 DCHECK(!io_loop_);
404 io_loop_ = base::MessageLoopForIO::current();
405 g_ocsp_io_loop.Get().AddRequest(this);
408 request_ = url_request_context->CreateRequest(
409 url_, DEFAULT_PRIORITY, this, NULL);
410 // To meet the privacy requirements of incognito mode.
411 request_->SetLoadFlags(LOAD_DISABLE_CACHE | LOAD_DO_NOT_SAVE_COOKIES |
412 LOAD_DO_NOT_SEND_COOKIES);
414 if (http_request_method_ == "POST") {
415 DCHECK(!upload_content_.empty());
416 DCHECK(!upload_content_type_.empty());
418 request_->set_method("POST");
419 extra_request_headers_.SetHeader(
420 HttpRequestHeaders::kContentType, upload_content_type_);
422 scoped_ptr<UploadElementReader> reader(new UploadBytesElementReader(
423 upload_content_.data(), upload_content_.size()));
424 request_->set_upload(
425 ElementsUploadDataStream::CreateWithReader(reader.Pass(), 0));
427 if (!extra_request_headers_.IsEmpty())
428 request_->SetExtraRequestHeaders(extra_request_headers_);
430 request_->Start();
431 AddRef(); // Release after |request_| deleted.
434 GURL url_; // The URL we eventually wound up at
435 std::string http_request_method_;
436 base::TimeDelta timeout_; // The timeout for OCSP
437 scoped_ptr<URLRequest> request_; // The actual request this wraps
438 scoped_refptr<IOBuffer> buffer_; // Read buffer
439 HttpRequestHeaders extra_request_headers_;
441 // HTTP POST payload. |request_| reads bytes from this.
442 std::string upload_content_;
443 std::string upload_content_type_; // MIME type of POST payload
445 int response_code_; // HTTP status code for the request
446 std::string response_content_type_;
447 scoped_refptr<HttpResponseHeaders> response_headers_;
448 std::string data_; // Results of the request
450 // |lock_| protects |finished_| and |io_loop_|.
451 mutable base::Lock lock_;
452 base::ConditionVariable cv_;
454 base::MessageLoop* io_loop_; // Message loop of the IO thread
455 bool finished_;
457 DISALLOW_COPY_AND_ASSIGN(OCSPRequestSession);
460 // Concrete class for SEC_HTTP_SERVER_SESSION.
461 class OCSPServerSession {
462 public:
463 OCSPServerSession(const char* host, PRUint16 port)
464 : host_and_port_(host, port) {}
465 ~OCSPServerSession() {}
467 OCSPRequestSession* CreateRequest(const char* http_protocol_variant,
468 const char* path_and_query_string,
469 const char* http_request_method,
470 const PRIntervalTime timeout) {
471 // We dont' support "https" because we haven't thought about
472 // whether it's safe to re-enter this code from talking to an OCSP
473 // responder over SSL.
474 if (strcmp(http_protocol_variant, "http") != 0) {
475 PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
476 return NULL;
479 std::string url_string(base::StringPrintf(
480 "%s://%s%s",
481 http_protocol_variant,
482 host_and_port_.ToString().c_str(),
483 path_and_query_string));
484 VLOG(1) << "URL [" << url_string << "]";
485 GURL url(url_string);
487 // NSS does not expose public functions to adjust the fetch timeout when
488 // using libpkix, so hardcode the upper limit for network fetches.
489 base::TimeDelta actual_timeout = std::min(
490 base::TimeDelta::FromSeconds(kNetworkFetchTimeoutInSecs),
491 base::TimeDelta::FromMilliseconds(PR_IntervalToMilliseconds(timeout)));
493 return new OCSPRequestSession(url, http_request_method, actual_timeout);
497 private:
498 HostPortPair host_and_port_;
500 DISALLOW_COPY_AND_ASSIGN(OCSPServerSession);
503 OCSPIOLoop::OCSPIOLoop()
504 : shutdown_(false),
505 used_(false),
506 io_loop_(NULL) {
509 OCSPIOLoop::~OCSPIOLoop() {
510 // IO thread was already deleted before the singleton is deleted
511 // in AtExitManager.
513 base::AutoLock autolock(lock_);
514 DCHECK(!io_loop_);
515 DCHECK(!used_);
516 DCHECK(shutdown_);
519 pthread_mutex_lock(&g_request_context_lock);
520 DCHECK(!g_request_context);
521 pthread_mutex_unlock(&g_request_context_lock);
524 void OCSPIOLoop::Shutdown() {
525 // Safe to read outside lock since we only write on IO thread anyway.
526 DCHECK(thread_checker_.CalledOnValidThread());
528 // Prevent the worker thread from trying to access |io_loop_|.
530 base::AutoLock autolock(lock_);
531 io_loop_ = NULL;
532 used_ = false;
533 shutdown_ = true;
536 CancelAllRequests();
538 pthread_mutex_lock(&g_request_context_lock);
539 g_request_context = NULL;
540 pthread_mutex_unlock(&g_request_context_lock);
543 void OCSPIOLoop::PostTaskToIOLoop(
544 const tracked_objects::Location& from_here, const base::Closure& task) {
545 base::AutoLock autolock(lock_);
546 if (io_loop_)
547 io_loop_->PostTask(from_here, task);
550 void OCSPIOLoop::EnsureIOLoop() {
551 base::AutoLock autolock(lock_);
552 DCHECK_EQ(base::MessageLoopForIO::current(), io_loop_);
555 void OCSPIOLoop::AddRequest(OCSPRequestSession* request) {
556 DCHECK(!ContainsKey(requests_, request));
557 requests_.insert(request);
560 void OCSPIOLoop::RemoveRequest(OCSPRequestSession* request) {
561 DCHECK(ContainsKey(requests_, request));
562 requests_.erase(request);
565 void OCSPIOLoop::CancelAllRequests() {
566 // CancelURLRequest() always removes the request from the requests_
567 // set synchronously.
568 while (!requests_.empty())
569 (*requests_.begin())->CancelURLRequest();
572 OCSPNSSInitialization::OCSPNSSInitialization() {
573 // NSS calls the functions in the function table to download certificates
574 // or CRLs or talk to OCSP responders over HTTP. These functions must
575 // set an NSS/NSPR error code when they fail. Otherwise NSS will get the
576 // residual error code from an earlier failed function call.
577 client_fcn_.version = 1;
578 SEC_HttpClientFcnV1Struct *ft = &client_fcn_.fcnTable.ftable1;
579 ft->createSessionFcn = OCSPCreateSession;
580 ft->keepAliveSessionFcn = OCSPKeepAliveSession;
581 ft->freeSessionFcn = OCSPFreeSession;
582 ft->createFcn = OCSPCreate;
583 ft->setPostDataFcn = OCSPSetPostData;
584 ft->addHeaderFcn = OCSPAddHeader;
585 ft->trySendAndReceiveFcn = OCSPTrySendAndReceive;
586 ft->cancelFcn = NULL;
587 ft->freeFcn = OCSPFree;
588 SECStatus status = SEC_RegisterDefaultHttpClient(&client_fcn_);
589 if (status != SECSuccess) {
590 NOTREACHED() << "Error initializing OCSP: " << PR_GetError();
593 // Work around NSS bugs 524013 and 564334. NSS incorrectly thinks the
594 // CRLs for Network Solutions Certificate Authority have bad signatures,
595 // which causes certificates issued by that CA to be reported as revoked.
596 // By using OCSP for those certificates, which don't have AIA extensions,
597 // we can work around these bugs. See http://crbug.com/41730.
598 CERT_StringFromCertFcn old_callback = NULL;
599 status = CERT_RegisterAlternateOCSPAIAInfoCallBack(
600 GetAlternateOCSPAIAInfo, &old_callback);
601 if (status == SECSuccess) {
602 DCHECK(!old_callback);
603 } else {
604 NOTREACHED() << "Error initializing OCSP: " << PR_GetError();
608 OCSPNSSInitialization::~OCSPNSSInitialization() {
609 SECStatus status = CERT_RegisterAlternateOCSPAIAInfoCallBack(NULL, NULL);
610 if (status != SECSuccess) {
611 LOG(ERROR) << "Error unregistering OCSP: " << PR_GetError();
616 // OCSP Http Client functions.
617 // Our Http Client functions operate in blocking mode.
618 SECStatus OCSPCreateSession(const char* host, PRUint16 portnum,
619 SEC_HTTP_SERVER_SESSION* pSession) {
620 VLOG(1) << "OCSP create session: host=" << host << " port=" << portnum;
621 pthread_mutex_lock(&g_request_context_lock);
622 URLRequestContext* request_context = g_request_context;
623 pthread_mutex_unlock(&g_request_context_lock);
624 if (request_context == NULL) {
625 LOG(ERROR) << "No URLRequestContext for NSS HTTP handler. host: " << host;
626 // The application failed to call SetURLRequestContextForNSSHttpIO or
627 // has already called ShutdownNSSHttpIO, so we can't create and use
628 // URLRequest. PR_NOT_IMPLEMENTED_ERROR is not an accurate error
629 // code for these error conditions, but is close enough.
630 PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
631 return SECFailure;
633 *pSession = new OCSPServerSession(host, portnum);
634 return SECSuccess;
637 SECStatus OCSPKeepAliveSession(SEC_HTTP_SERVER_SESSION session,
638 PRPollDesc **pPollDesc) {
639 VLOG(1) << "OCSP keep alive";
640 if (pPollDesc)
641 *pPollDesc = NULL;
642 return SECSuccess;
645 SECStatus OCSPFreeSession(SEC_HTTP_SERVER_SESSION session) {
646 VLOG(1) << "OCSP free session";
647 delete reinterpret_cast<OCSPServerSession*>(session);
648 return SECSuccess;
651 SECStatus OCSPCreate(SEC_HTTP_SERVER_SESSION session,
652 const char* http_protocol_variant,
653 const char* path_and_query_string,
654 const char* http_request_method,
655 const PRIntervalTime timeout,
656 SEC_HTTP_REQUEST_SESSION* pRequest) {
657 VLOG(1) << "OCSP create protocol=" << http_protocol_variant
658 << " path_and_query=" << path_and_query_string
659 << " http_request_method=" << http_request_method
660 << " timeout=" << timeout;
661 OCSPServerSession* ocsp_session =
662 reinterpret_cast<OCSPServerSession*>(session);
664 OCSPRequestSession* req = ocsp_session->CreateRequest(http_protocol_variant,
665 path_and_query_string,
666 http_request_method,
667 timeout);
668 SECStatus rv = SECFailure;
669 if (req) {
670 req->AddRef(); // Release in OCSPFree().
671 rv = SECSuccess;
673 *pRequest = req;
674 return rv;
677 SECStatus OCSPSetPostData(SEC_HTTP_REQUEST_SESSION request,
678 const char* http_data,
679 const PRUint32 http_data_len,
680 const char* http_content_type) {
681 VLOG(1) << "OCSP set post data len=" << http_data_len;
682 OCSPRequestSession* req = reinterpret_cast<OCSPRequestSession*>(request);
684 req->SetPostData(http_data, http_data_len, http_content_type);
685 return SECSuccess;
688 SECStatus OCSPAddHeader(SEC_HTTP_REQUEST_SESSION request,
689 const char* http_header_name,
690 const char* http_header_value) {
691 VLOG(1) << "OCSP add header name=" << http_header_name
692 << " value=" << http_header_value;
693 OCSPRequestSession* req = reinterpret_cast<OCSPRequestSession*>(request);
695 req->AddHeader(http_header_name, http_header_value);
696 return SECSuccess;
699 // Sets response of |req| in the output parameters.
700 // It is helper routine for OCSP trySendAndReceiveFcn.
701 // |http_response_data_len| could be used as input parameter. If it has
702 // non-zero value, it is considered as maximum size of |http_response_data|.
703 SECStatus OCSPSetResponse(OCSPRequestSession* req,
704 PRUint16* http_response_code,
705 const char** http_response_content_type,
706 const char** http_response_headers,
707 const char** http_response_data,
708 PRUint32* http_response_data_len) {
709 DCHECK(req->Finished());
710 const std::string& data = req->http_response_data();
711 if (http_response_data_len && *http_response_data_len) {
712 if (*http_response_data_len < data.size()) {
713 LOG(ERROR) << "response body too large: " << *http_response_data_len
714 << " < " << data.size();
715 *http_response_data_len = data.size();
716 PORT_SetError(SEC_ERROR_BAD_HTTP_RESPONSE);
717 return SECFailure;
720 VLOG(1) << "OCSP response "
721 << " response_code=" << req->http_response_code()
722 << " content_type=" << req->http_response_content_type()
723 << " header=" << req->http_response_headers()
724 << " data_len=" << data.size();
725 if (http_response_code)
726 *http_response_code = req->http_response_code();
727 if (http_response_content_type)
728 *http_response_content_type = req->http_response_content_type().c_str();
729 if (http_response_headers)
730 *http_response_headers = req->http_response_headers().c_str();
731 if (http_response_data)
732 *http_response_data = data.data();
733 if (http_response_data_len)
734 *http_response_data_len = data.size();
735 return SECSuccess;
738 SECStatus OCSPTrySendAndReceive(SEC_HTTP_REQUEST_SESSION request,
739 PRPollDesc** pPollDesc,
740 PRUint16* http_response_code,
741 const char** http_response_content_type,
742 const char** http_response_headers,
743 const char** http_response_data,
744 PRUint32* http_response_data_len) {
745 if (http_response_data_len) {
746 // We must always set an output value, even on failure. The output value 0
747 // means the failure was unrelated to the acceptable response data length.
748 *http_response_data_len = 0;
751 VLOG(1) << "OCSP try send and receive";
752 OCSPRequestSession* req = reinterpret_cast<OCSPRequestSession*>(request);
753 // We support blocking mode only.
754 if (pPollDesc)
755 *pPollDesc = NULL;
757 if (req->Started() || req->Finished()) {
758 // We support blocking mode only, so this function shouldn't be called
759 // again when req has stareted or finished.
760 NOTREACHED();
761 PORT_SetError(SEC_ERROR_BAD_HTTP_RESPONSE); // Simple approximation.
762 return SECFailure;
765 const base::Time start_time = base::Time::Now();
766 bool request_ok = true;
767 req->Start();
768 if (!req->Wait() || req->http_response_code() == static_cast<PRUint16>(-1)) {
769 // If the response code is -1, the request failed and there is no response.
770 request_ok = false;
772 const base::TimeDelta duration = base::Time::Now() - start_time;
774 // For metrics, we want to know if the request was 'successful' or not.
775 // |request_ok| determines if we'll pass the response back to NSS and |ok|
776 // keep track of if we think the response was good.
777 bool ok = true;
778 if (!request_ok ||
779 (req->http_response_code() >= 400 && req->http_response_code() < 600) ||
780 req->http_response_data().size() == 0 ||
781 // 0x30 is the ASN.1 DER encoding of a SEQUENCE. All valid OCSP/CRL/CRT
782 // responses must start with this. If we didn't check for this then a
783 // captive portal could provide an HTML reply that we would count as a
784 // 'success' (although it wouldn't count in NSS, of course).
785 req->http_response_data().data()[0] != 0x30) {
786 ok = false;
789 // We want to know if this was:
790 // 1) An OCSP request
791 // 2) A CRL request
792 // 3) A request for a missing intermediate certificate
793 // There's no sure way to do this, so we use heuristics like MIME type and
794 // URL.
795 const char* mime_type = "";
796 if (ok)
797 mime_type = req->http_response_content_type().c_str();
798 bool is_ocsp =
799 strcasecmp(mime_type, "application/ocsp-response") == 0;
800 bool is_crl = strcasecmp(mime_type, "application/x-pkcs7-crl") == 0 ||
801 strcasecmp(mime_type, "application/x-x509-crl") == 0 ||
802 strcasecmp(mime_type, "application/pkix-crl") == 0;
803 bool is_cert =
804 strcasecmp(mime_type, "application/x-x509-ca-cert") == 0 ||
805 strcasecmp(mime_type, "application/x-x509-server-cert") == 0 ||
806 strcasecmp(mime_type, "application/pkix-cert") == 0 ||
807 strcasecmp(mime_type, "application/pkcs7-mime") == 0;
809 if (!is_cert && !is_crl && !is_ocsp) {
810 // We didn't get a hint from the MIME type, so do the best that we can.
811 const std::string path = req->url().path();
812 const std::string host = req->url().host();
813 is_crl = strcasestr(path.c_str(), ".crl") != NULL;
814 is_cert = strcasestr(path.c_str(), ".crt") != NULL ||
815 strcasestr(path.c_str(), ".p7c") != NULL ||
816 strcasestr(path.c_str(), ".cer") != NULL;
817 is_ocsp = strcasestr(host.c_str(), "ocsp") != NULL ||
818 req->http_request_method() == "POST";
821 if (is_ocsp) {
822 if (ok) {
823 UMA_HISTOGRAM_TIMES("Net.OCSPRequestTimeMs", duration);
824 UMA_HISTOGRAM_BOOLEAN("Net.OCSPRequestSuccess", true);
825 } else {
826 UMA_HISTOGRAM_TIMES("Net.OCSPRequestFailedTimeMs", duration);
827 UMA_HISTOGRAM_BOOLEAN("Net.OCSPRequestSuccess", false);
829 } else if (is_crl) {
830 if (ok) {
831 UMA_HISTOGRAM_TIMES("Net.CRLRequestTimeMs", duration);
832 UMA_HISTOGRAM_BOOLEAN("Net.CRLRequestSuccess", true);
833 } else {
834 UMA_HISTOGRAM_TIMES("Net.CRLRequestFailedTimeMs", duration);
835 UMA_HISTOGRAM_BOOLEAN("Net.CRLRequestSuccess", false);
837 } else if (is_cert) {
838 if (ok)
839 UMA_HISTOGRAM_TIMES("Net.CRTRequestTimeMs", duration);
840 } else {
841 if (ok)
842 UMA_HISTOGRAM_TIMES("Net.UnknownTypeRequestTimeMs", duration);
845 if (!request_ok) {
846 PORT_SetError(SEC_ERROR_BAD_HTTP_RESPONSE); // Simple approximation.
847 return SECFailure;
850 return OCSPSetResponse(
851 req, http_response_code,
852 http_response_content_type,
853 http_response_headers,
854 http_response_data,
855 http_response_data_len);
858 SECStatus OCSPFree(SEC_HTTP_REQUEST_SESSION request) {
859 VLOG(1) << "OCSP free";
860 OCSPRequestSession* req = reinterpret_cast<OCSPRequestSession*>(request);
861 req->Cancel();
862 req->Release();
863 return SECSuccess;
866 // Data for GetAlternateOCSPAIAInfo.
868 // CN=Network Solutions Certificate Authority,O=Network Solutions L.L.C.,C=US
870 // There are two CAs with this name. Their key IDs are listed next.
871 const unsigned char network_solutions_ca_name[] = {
872 0x30, 0x62, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04,
873 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x21, 0x30, 0x1f, 0x06,
874 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x4e, 0x65, 0x74, 0x77,
875 0x6f, 0x72, 0x6b, 0x20, 0x53, 0x6f, 0x6c, 0x75, 0x74, 0x69,
876 0x6f, 0x6e, 0x73, 0x20, 0x4c, 0x2e, 0x4c, 0x2e, 0x43, 0x2e,
877 0x31, 0x30, 0x30, 0x2e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
878 0x27, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x53,
879 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x43,
880 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65,
881 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79
883 const unsigned int network_solutions_ca_name_len = 100;
885 // This CA is an intermediate CA, subordinate to UTN-USERFirst-Hardware.
886 const unsigned char network_solutions_ca_key_id[] = {
887 0x3c, 0x41, 0xe2, 0x8f, 0x08, 0x08, 0xa9, 0x4c, 0x25, 0x89,
888 0x8d, 0x6d, 0xc5, 0x38, 0xd0, 0xfc, 0x85, 0x8c, 0x62, 0x17
890 const unsigned int network_solutions_ca_key_id_len = 20;
892 // This CA is a root CA. It is also cross-certified by
893 // UTN-USERFirst-Hardware.
894 const unsigned char network_solutions_ca_key_id2[] = {
895 0x21, 0x30, 0xc9, 0xfb, 0x00, 0xd7, 0x4e, 0x98, 0xda, 0x87,
896 0xaa, 0x2a, 0xd0, 0xa7, 0x2e, 0xb1, 0x40, 0x31, 0xa7, 0x4c
898 const unsigned int network_solutions_ca_key_id2_len = 20;
900 // An entry in our OCSP responder table. |issuer| and |issuer_key_id| are
901 // the key. |ocsp_url| is the value.
902 struct OCSPResponderTableEntry {
903 SECItem issuer;
904 SECItem issuer_key_id;
905 const char *ocsp_url;
908 const OCSPResponderTableEntry g_ocsp_responder_table[] = {
911 siBuffer,
912 const_cast<unsigned char*>(network_solutions_ca_name),
913 network_solutions_ca_name_len
916 siBuffer,
917 const_cast<unsigned char*>(network_solutions_ca_key_id),
918 network_solutions_ca_key_id_len
920 "http://ocsp.netsolssl.com"
924 siBuffer,
925 const_cast<unsigned char*>(network_solutions_ca_name),
926 network_solutions_ca_name_len
929 siBuffer,
930 const_cast<unsigned char*>(network_solutions_ca_key_id2),
931 network_solutions_ca_key_id2_len
933 "http://ocsp.netsolssl.com"
937 char* GetAlternateOCSPAIAInfo(CERTCertificate *cert) {
938 if (cert && !cert->isRoot && cert->authKeyID) {
939 for (unsigned int i=0; i < arraysize(g_ocsp_responder_table); i++) {
940 if (SECITEM_CompareItem(&g_ocsp_responder_table[i].issuer,
941 &cert->derIssuer) == SECEqual &&
942 SECITEM_CompareItem(&g_ocsp_responder_table[i].issuer_key_id,
943 &cert->authKeyID->keyID) == SECEqual) {
944 return PORT_Strdup(g_ocsp_responder_table[i].ocsp_url);
949 return NULL;
952 } // anonymous namespace
954 void SetMessageLoopForNSSHttpIO() {
955 // Must have a MessageLoopForIO.
956 DCHECK(base::MessageLoopForIO::current());
958 bool used = g_ocsp_io_loop.Get().used();
960 // Should not be called when g_ocsp_io_loop has already been used.
961 DCHECK(!used);
964 void EnsureNSSHttpIOInit() {
965 g_ocsp_io_loop.Get().StartUsing();
966 g_ocsp_nss_initialization.Get();
969 void ShutdownNSSHttpIO() {
970 g_ocsp_io_loop.Get().Shutdown();
973 void ResetNSSHttpIOForTesting() {
974 g_ocsp_io_loop.Get().ReuseForTesting();
977 // This function would be called before NSS initialization.
978 void SetURLRequestContextForNSSHttpIO(URLRequestContext* request_context) {
979 pthread_mutex_lock(&g_request_context_lock);
980 if (request_context) {
981 DCHECK(!g_request_context);
983 g_request_context = request_context;
984 pthread_mutex_unlock(&g_request_context_lock);
987 } // namespace net