cc: Implement RemoveLast for DisplayItemList.
[chromium-blink-merge.git] / net / cert_net / nss_ocsp.cc
blob1eea3ebe978179857e35745f5cd86b96d1333544
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/cert_net/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/stl_util.h"
27 #include "base/strings/string_util.h"
28 #include "base/strings/stringprintf.h"
29 #include "base/synchronization/condition_variable.h"
30 #include "base/synchronization/lock.h"
31 #include "base/threading/thread_checker.h"
32 #include "base/time/time.h"
33 #include "net/base/elements_upload_data_stream.h"
34 #include "net/base/host_port_pair.h"
35 #include "net/base/io_buffer.h"
36 #include "net/base/load_flags.h"
37 #include "net/base/request_priority.h"
38 #include "net/base/upload_bytes_element_reader.h"
39 #include "net/http/http_request_headers.h"
40 #include "net/http/http_response_headers.h"
41 #include "net/url_request/redirect_info.h"
42 #include "net/url_request/url_request.h"
43 #include "net/url_request/url_request_context.h"
44 #include "url/gurl.h"
46 namespace net {
48 namespace {
50 // Protects |g_request_context|.
51 pthread_mutex_t g_request_context_lock = PTHREAD_MUTEX_INITIALIZER;
52 URLRequestContext* g_request_context = NULL;
54 // The default timeout for network fetches in NSS is 60 seconds. Choose a
55 // saner upper limit for OCSP/CRL/AIA fetches.
56 const int kNetworkFetchTimeoutInSecs = 15;
58 class OCSPRequestSession;
60 class OCSPIOLoop {
61 public:
62 void StartUsing() {
63 base::AutoLock autolock(lock_);
64 used_ = true;
65 io_loop_ = base::MessageLoopForIO::current();
66 DCHECK(io_loop_);
69 // Called on IO loop.
70 void Shutdown();
72 bool used() const {
73 base::AutoLock autolock(lock_);
74 return used_;
77 // Called from worker thread.
78 void PostTaskToIOLoop(const tracked_objects::Location& from_here,
79 const base::Closure& task);
81 void EnsureIOLoop();
83 void AddRequest(OCSPRequestSession* request);
84 void RemoveRequest(OCSPRequestSession* request);
86 // Clears internal state and calls |StartUsing()|. Should be called only in
87 // the context of testing.
88 void ReuseForTesting() {
90 base::AutoLock autolock(lock_);
91 DCHECK(base::MessageLoopForIO::current());
92 thread_checker_.DetachFromThread();
94 // CalledOnValidThread is the only available API to reassociate
95 // thread_checker_ with the current thread. Result ignored intentionally.
96 ignore_result(thread_checker_.CalledOnValidThread());
97 shutdown_ = false;
98 used_ = false;
100 StartUsing();
103 private:
104 friend struct base::DefaultLazyInstanceTraits<OCSPIOLoop>;
106 OCSPIOLoop();
107 ~OCSPIOLoop();
109 void CancelAllRequests();
111 mutable base::Lock lock_;
112 bool shutdown_; // Protected by |lock_|.
113 std::set<OCSPRequestSession*> requests_; // Protected by |lock_|.
114 bool used_; // Protected by |lock_|.
115 // This should not be modified after |used_|.
116 base::MessageLoopForIO* io_loop_; // Protected by |lock_|.
117 base::ThreadChecker thread_checker_;
119 DISALLOW_COPY_AND_ASSIGN(OCSPIOLoop);
122 base::LazyInstance<OCSPIOLoop>::Leaky
123 g_ocsp_io_loop = LAZY_INSTANCE_INITIALIZER;
125 const int kRecvBufferSize = 4096;
127 // All OCSP handlers should be called in the context of
128 // CertVerifier's thread (i.e. worker pool, not on the I/O thread).
129 // It supports blocking mode only.
131 SECStatus OCSPCreateSession(const char* host, PRUint16 portnum,
132 SEC_HTTP_SERVER_SESSION* pSession);
133 SECStatus OCSPKeepAliveSession(SEC_HTTP_SERVER_SESSION session,
134 PRPollDesc **pPollDesc);
135 SECStatus OCSPFreeSession(SEC_HTTP_SERVER_SESSION session);
137 SECStatus OCSPCreate(SEC_HTTP_SERVER_SESSION session,
138 const char* http_protocol_variant,
139 const char* path_and_query_string,
140 const char* http_request_method,
141 const PRIntervalTime timeout,
142 SEC_HTTP_REQUEST_SESSION* pRequest);
143 SECStatus OCSPSetPostData(SEC_HTTP_REQUEST_SESSION request,
144 const char* http_data,
145 const PRUint32 http_data_len,
146 const char* http_content_type);
147 SECStatus OCSPAddHeader(SEC_HTTP_REQUEST_SESSION request,
148 const char* http_header_name,
149 const char* http_header_value);
150 SECStatus OCSPTrySendAndReceive(SEC_HTTP_REQUEST_SESSION request,
151 PRPollDesc** pPollDesc,
152 PRUint16* http_response_code,
153 const char** http_response_content_type,
154 const char** http_response_headers,
155 const char** http_response_data,
156 PRUint32* http_response_data_len);
157 SECStatus OCSPFree(SEC_HTTP_REQUEST_SESSION request);
159 char* GetAlternateOCSPAIAInfo(CERTCertificate *cert);
161 class OCSPNSSInitialization {
162 private:
163 friend struct base::DefaultLazyInstanceTraits<OCSPNSSInitialization>;
165 OCSPNSSInitialization();
166 ~OCSPNSSInitialization();
168 SEC_HttpClientFcn client_fcn_;
170 DISALLOW_COPY_AND_ASSIGN(OCSPNSSInitialization);
173 base::LazyInstance<OCSPNSSInitialization> g_ocsp_nss_initialization =
174 LAZY_INSTANCE_INITIALIZER;
176 // Concrete class for SEC_HTTP_REQUEST_SESSION.
177 // Public methods except virtual methods of URLRequest::Delegate
178 // (On* methods) run on certificate verifier thread (worker thread).
179 // Virtual methods of URLRequest::Delegate and private methods run
180 // on IO thread.
181 class OCSPRequestSession
182 : public base::RefCountedThreadSafe<OCSPRequestSession>,
183 public URLRequest::Delegate {
184 public:
185 OCSPRequestSession(const GURL& url,
186 const char* http_request_method,
187 base::TimeDelta timeout)
188 : url_(url),
189 http_request_method_(http_request_method),
190 timeout_(timeout),
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_.get() != 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 void OnReceivedRedirect(URLRequest* request,
287 const RedirectInfo& redirect_info,
288 bool* defer_redirect) override {
289 DCHECK_EQ(request_.get(), 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 void OnResponseStarted(URLRequest* request) override {
300 DCHECK_EQ(request_.get(), 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_.get(), bytes_read);
313 void OnReadCompleted(URLRequest* request, int bytes_read) override {
314 DCHECK_EQ(request_.get(), request);
315 DCHECK_EQ(base::MessageLoopForIO::current(), io_loop_);
317 do {
318 if (!request_->status().is_success() || bytes_read <= 0)
319 break;
320 data_.append(buffer_->data(), bytes_read);
321 } while (request_->Read(buffer_.get(), kRecvBufferSize, &bytes_read));
323 if (!request_->status().is_io_pending()) {
324 request_.reset();
325 g_ocsp_io_loop.Get().RemoveRequest(this);
327 base::AutoLock autolock(lock_);
328 finished_ = true;
329 io_loop_ = NULL;
331 cv_.Signal();
332 Release(); // Balanced with StartURLRequest().
336 // Must be called on the IO loop thread.
337 void CancelURLRequest() {
338 #ifndef NDEBUG
340 base::AutoLock autolock(lock_);
341 if (io_loop_)
342 DCHECK_EQ(base::MessageLoopForIO::current(), io_loop_);
344 #endif
345 if (request_) {
346 request_.reset();
347 g_ocsp_io_loop.Get().RemoveRequest(this);
349 base::AutoLock autolock(lock_);
350 finished_ = true;
351 io_loop_ = NULL;
353 cv_.Signal();
354 Release(); // Balanced with StartURLRequest().
358 private:
359 friend class base::RefCountedThreadSafe<OCSPRequestSession>;
361 ~OCSPRequestSession() override {
362 // When this destructor is called, there should be only one thread that has
363 // a reference to this object, and so that thread doesn't need to lock
364 // |lock_| here.
365 DCHECK(!request_);
366 DCHECK(!io_loop_);
369 // Must call this method while holding |lock_|.
370 void CancelLocked() {
371 lock_.AssertAcquired();
372 if (io_loop_) {
373 io_loop_->PostTask(
374 FROM_HERE,
375 base::Bind(&OCSPRequestSession::CancelURLRequest, this));
379 // Runs on |g_ocsp_io_loop|'s IO loop.
380 void StartURLRequest() {
381 DCHECK(!request_);
383 pthread_mutex_lock(&g_request_context_lock);
384 URLRequestContext* url_request_context = g_request_context;
385 pthread_mutex_unlock(&g_request_context_lock);
387 if (url_request_context == NULL)
388 return;
391 base::AutoLock autolock(lock_);
392 DCHECK(!io_loop_);
393 io_loop_ = base::MessageLoopForIO::current();
394 g_ocsp_io_loop.Get().AddRequest(this);
397 request_ = url_request_context->CreateRequest(url_, DEFAULT_PRIORITY, this);
398 // To meet the privacy requirements of incognito mode.
399 request_->SetLoadFlags(LOAD_DISABLE_CACHE | LOAD_DO_NOT_SAVE_COOKIES |
400 LOAD_DO_NOT_SEND_COOKIES);
402 if (http_request_method_ == "POST") {
403 DCHECK(!upload_content_.empty());
404 DCHECK(!upload_content_type_.empty());
406 request_->set_method("POST");
407 extra_request_headers_.SetHeader(
408 HttpRequestHeaders::kContentType, upload_content_type_);
410 scoped_ptr<UploadElementReader> reader(new UploadBytesElementReader(
411 upload_content_.data(), upload_content_.size()));
412 request_->set_upload(
413 ElementsUploadDataStream::CreateWithReader(reader.Pass(), 0));
415 if (!extra_request_headers_.IsEmpty())
416 request_->SetExtraRequestHeaders(extra_request_headers_);
418 request_->Start();
419 AddRef(); // Release after |request_| deleted.
422 GURL url_; // The URL we eventually wound up at
423 std::string http_request_method_;
424 base::TimeDelta timeout_; // The timeout for OCSP
425 scoped_ptr<URLRequest> request_; // The actual request this wraps
426 scoped_refptr<IOBuffer> buffer_; // Read buffer
427 HttpRequestHeaders extra_request_headers_;
429 // HTTP POST payload. |request_| reads bytes from this.
430 std::string upload_content_;
431 std::string upload_content_type_; // MIME type of POST payload
433 int response_code_; // HTTP status code for the request
434 std::string response_content_type_;
435 scoped_refptr<HttpResponseHeaders> response_headers_;
436 std::string data_; // Results of the request
438 // |lock_| protects |finished_| and |io_loop_|.
439 mutable base::Lock lock_;
440 base::ConditionVariable cv_;
442 base::MessageLoop* io_loop_; // Message loop of the IO thread
443 bool finished_;
445 DISALLOW_COPY_AND_ASSIGN(OCSPRequestSession);
448 // Concrete class for SEC_HTTP_SERVER_SESSION.
449 class OCSPServerSession {
450 public:
451 OCSPServerSession(const char* host, PRUint16 port)
452 : host_and_port_(host, port) {}
453 ~OCSPServerSession() {}
455 OCSPRequestSession* CreateRequest(const char* http_protocol_variant,
456 const char* path_and_query_string,
457 const char* http_request_method,
458 const PRIntervalTime timeout) {
459 // We dont' support "https" because we haven't thought about
460 // whether it's safe to re-enter this code from talking to an OCSP
461 // responder over SSL.
462 if (strcmp(http_protocol_variant, "http") != 0) {
463 PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
464 return NULL;
467 std::string url_string(base::StringPrintf(
468 "%s://%s%s",
469 http_protocol_variant,
470 host_and_port_.ToString().c_str(),
471 path_and_query_string));
472 VLOG(1) << "URL [" << url_string << "]";
473 GURL url(url_string);
475 // NSS does not expose public functions to adjust the fetch timeout when
476 // using libpkix, so hardcode the upper limit for network fetches.
477 base::TimeDelta actual_timeout = std::min(
478 base::TimeDelta::FromSeconds(kNetworkFetchTimeoutInSecs),
479 base::TimeDelta::FromMilliseconds(PR_IntervalToMilliseconds(timeout)));
481 return new OCSPRequestSession(url, http_request_method, actual_timeout);
485 private:
486 HostPortPair host_and_port_;
488 DISALLOW_COPY_AND_ASSIGN(OCSPServerSession);
491 OCSPIOLoop::OCSPIOLoop()
492 : shutdown_(false),
493 used_(false),
494 io_loop_(NULL) {
497 OCSPIOLoop::~OCSPIOLoop() {
498 // IO thread was already deleted before the singleton is deleted
499 // in AtExitManager.
501 base::AutoLock autolock(lock_);
502 DCHECK(!io_loop_);
503 DCHECK(!used_);
504 DCHECK(shutdown_);
507 pthread_mutex_lock(&g_request_context_lock);
508 DCHECK(!g_request_context);
509 pthread_mutex_unlock(&g_request_context_lock);
512 void OCSPIOLoop::Shutdown() {
513 // Safe to read outside lock since we only write on IO thread anyway.
514 DCHECK(thread_checker_.CalledOnValidThread());
516 // Prevent the worker thread from trying to access |io_loop_|.
518 base::AutoLock autolock(lock_);
519 io_loop_ = NULL;
520 used_ = false;
521 shutdown_ = true;
524 CancelAllRequests();
526 pthread_mutex_lock(&g_request_context_lock);
527 g_request_context = NULL;
528 pthread_mutex_unlock(&g_request_context_lock);
531 void OCSPIOLoop::PostTaskToIOLoop(
532 const tracked_objects::Location& from_here, const base::Closure& task) {
533 base::AutoLock autolock(lock_);
534 if (io_loop_)
535 io_loop_->PostTask(from_here, task);
538 void OCSPIOLoop::EnsureIOLoop() {
539 base::AutoLock autolock(lock_);
540 DCHECK_EQ(base::MessageLoopForIO::current(), io_loop_);
543 void OCSPIOLoop::AddRequest(OCSPRequestSession* request) {
544 DCHECK(!ContainsKey(requests_, request));
545 requests_.insert(request);
548 void OCSPIOLoop::RemoveRequest(OCSPRequestSession* request) {
549 DCHECK(ContainsKey(requests_, request));
550 requests_.erase(request);
553 void OCSPIOLoop::CancelAllRequests() {
554 // CancelURLRequest() always removes the request from the requests_
555 // set synchronously.
556 while (!requests_.empty())
557 (*requests_.begin())->CancelURLRequest();
560 OCSPNSSInitialization::OCSPNSSInitialization() {
561 // NSS calls the functions in the function table to download certificates
562 // or CRLs or talk to OCSP responders over HTTP. These functions must
563 // set an NSS/NSPR error code when they fail. Otherwise NSS will get the
564 // residual error code from an earlier failed function call.
565 client_fcn_.version = 1;
566 SEC_HttpClientFcnV1Struct *ft = &client_fcn_.fcnTable.ftable1;
567 ft->createSessionFcn = OCSPCreateSession;
568 ft->keepAliveSessionFcn = OCSPKeepAliveSession;
569 ft->freeSessionFcn = OCSPFreeSession;
570 ft->createFcn = OCSPCreate;
571 ft->setPostDataFcn = OCSPSetPostData;
572 ft->addHeaderFcn = OCSPAddHeader;
573 ft->trySendAndReceiveFcn = OCSPTrySendAndReceive;
574 ft->cancelFcn = NULL;
575 ft->freeFcn = OCSPFree;
576 SECStatus status = SEC_RegisterDefaultHttpClient(&client_fcn_);
577 if (status != SECSuccess) {
578 NOTREACHED() << "Error initializing OCSP: " << PR_GetError();
581 // Work around NSS bugs 524013 and 564334. NSS incorrectly thinks the
582 // CRLs for Network Solutions Certificate Authority have bad signatures,
583 // which causes certificates issued by that CA to be reported as revoked.
584 // By using OCSP for those certificates, which don't have AIA extensions,
585 // we can work around these bugs. See http://crbug.com/41730.
586 CERT_StringFromCertFcn old_callback = NULL;
587 status = CERT_RegisterAlternateOCSPAIAInfoCallBack(
588 GetAlternateOCSPAIAInfo, &old_callback);
589 if (status == SECSuccess) {
590 DCHECK(!old_callback);
591 } else {
592 NOTREACHED() << "Error initializing OCSP: " << PR_GetError();
596 OCSPNSSInitialization::~OCSPNSSInitialization() {
597 SECStatus status = CERT_RegisterAlternateOCSPAIAInfoCallBack(NULL, NULL);
598 if (status != SECSuccess) {
599 LOG(ERROR) << "Error unregistering OCSP: " << PR_GetError();
604 // OCSP Http Client functions.
605 // Our Http Client functions operate in blocking mode.
606 SECStatus OCSPCreateSession(const char* host, PRUint16 portnum,
607 SEC_HTTP_SERVER_SESSION* pSession) {
608 VLOG(1) << "OCSP create session: host=" << host << " port=" << portnum;
609 pthread_mutex_lock(&g_request_context_lock);
610 URLRequestContext* request_context = g_request_context;
611 pthread_mutex_unlock(&g_request_context_lock);
612 if (request_context == NULL) {
613 LOG(ERROR) << "No URLRequestContext for NSS HTTP handler. host: " << host;
614 // The application failed to call SetURLRequestContextForNSSHttpIO or
615 // has already called ShutdownNSSHttpIO, so we can't create and use
616 // URLRequest. PR_NOT_IMPLEMENTED_ERROR is not an accurate error
617 // code for these error conditions, but is close enough.
618 PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
619 return SECFailure;
621 *pSession = new OCSPServerSession(host, portnum);
622 return SECSuccess;
625 SECStatus OCSPKeepAliveSession(SEC_HTTP_SERVER_SESSION session,
626 PRPollDesc **pPollDesc) {
627 VLOG(1) << "OCSP keep alive";
628 if (pPollDesc)
629 *pPollDesc = NULL;
630 return SECSuccess;
633 SECStatus OCSPFreeSession(SEC_HTTP_SERVER_SESSION session) {
634 VLOG(1) << "OCSP free session";
635 delete reinterpret_cast<OCSPServerSession*>(session);
636 return SECSuccess;
639 SECStatus OCSPCreate(SEC_HTTP_SERVER_SESSION session,
640 const char* http_protocol_variant,
641 const char* path_and_query_string,
642 const char* http_request_method,
643 const PRIntervalTime timeout,
644 SEC_HTTP_REQUEST_SESSION* pRequest) {
645 VLOG(1) << "OCSP create protocol=" << http_protocol_variant
646 << " path_and_query=" << path_and_query_string
647 << " http_request_method=" << http_request_method
648 << " timeout=" << timeout;
649 OCSPServerSession* ocsp_session =
650 reinterpret_cast<OCSPServerSession*>(session);
652 OCSPRequestSession* req = ocsp_session->CreateRequest(http_protocol_variant,
653 path_and_query_string,
654 http_request_method,
655 timeout);
656 SECStatus rv = SECFailure;
657 if (req) {
658 req->AddRef(); // Release in OCSPFree().
659 rv = SECSuccess;
661 *pRequest = req;
662 return rv;
665 SECStatus OCSPSetPostData(SEC_HTTP_REQUEST_SESSION request,
666 const char* http_data,
667 const PRUint32 http_data_len,
668 const char* http_content_type) {
669 VLOG(1) << "OCSP set post data len=" << http_data_len;
670 OCSPRequestSession* req = reinterpret_cast<OCSPRequestSession*>(request);
672 req->SetPostData(http_data, http_data_len, http_content_type);
673 return SECSuccess;
676 SECStatus OCSPAddHeader(SEC_HTTP_REQUEST_SESSION request,
677 const char* http_header_name,
678 const char* http_header_value) {
679 VLOG(1) << "OCSP add header name=" << http_header_name
680 << " value=" << http_header_value;
681 OCSPRequestSession* req = reinterpret_cast<OCSPRequestSession*>(request);
683 req->AddHeader(http_header_name, http_header_value);
684 return SECSuccess;
687 // Sets response of |req| in the output parameters.
688 // It is helper routine for OCSP trySendAndReceiveFcn.
689 // |http_response_data_len| could be used as input parameter. If it has
690 // non-zero value, it is considered as maximum size of |http_response_data|.
691 SECStatus OCSPSetResponse(OCSPRequestSession* req,
692 PRUint16* http_response_code,
693 const char** http_response_content_type,
694 const char** http_response_headers,
695 const char** http_response_data,
696 PRUint32* http_response_data_len) {
697 DCHECK(req->Finished());
698 const std::string& data = req->http_response_data();
699 if (http_response_data_len && *http_response_data_len) {
700 if (*http_response_data_len < data.size()) {
701 LOG(ERROR) << "response body too large: " << *http_response_data_len
702 << " < " << data.size();
703 *http_response_data_len = data.size();
704 PORT_SetError(SEC_ERROR_BAD_HTTP_RESPONSE);
705 return SECFailure;
708 VLOG(1) << "OCSP response "
709 << " response_code=" << req->http_response_code()
710 << " content_type=" << req->http_response_content_type()
711 << " header=" << req->http_response_headers()
712 << " data_len=" << data.size();
713 if (http_response_code)
714 *http_response_code = req->http_response_code();
715 if (http_response_content_type)
716 *http_response_content_type = req->http_response_content_type().c_str();
717 if (http_response_headers)
718 *http_response_headers = req->http_response_headers().c_str();
719 if (http_response_data)
720 *http_response_data = data.data();
721 if (http_response_data_len)
722 *http_response_data_len = data.size();
723 return SECSuccess;
726 SECStatus OCSPTrySendAndReceive(SEC_HTTP_REQUEST_SESSION request,
727 PRPollDesc** pPollDesc,
728 PRUint16* http_response_code,
729 const char** http_response_content_type,
730 const char** http_response_headers,
731 const char** http_response_data,
732 PRUint32* http_response_data_len) {
733 if (http_response_data_len) {
734 // We must always set an output value, even on failure. The output value 0
735 // means the failure was unrelated to the acceptable response data length.
736 *http_response_data_len = 0;
739 VLOG(1) << "OCSP try send and receive";
740 OCSPRequestSession* req = reinterpret_cast<OCSPRequestSession*>(request);
741 // We support blocking mode only.
742 if (pPollDesc)
743 *pPollDesc = NULL;
745 if (req->Started() || req->Finished()) {
746 // We support blocking mode only, so this function shouldn't be called
747 // again when req has stareted or finished.
748 NOTREACHED();
749 PORT_SetError(SEC_ERROR_BAD_HTTP_RESPONSE); // Simple approximation.
750 return SECFailure;
753 const base::Time start_time = base::Time::Now();
754 bool request_ok = true;
755 req->Start();
756 if (!req->Wait() || req->http_response_code() == static_cast<PRUint16>(-1)) {
757 // If the response code is -1, the request failed and there is no response.
758 request_ok = false;
760 const base::TimeDelta duration = base::Time::Now() - start_time;
762 // For metrics, we want to know if the request was 'successful' or not.
763 // |request_ok| determines if we'll pass the response back to NSS and |ok|
764 // keep track of if we think the response was good.
765 bool ok = true;
766 if (!request_ok ||
767 (req->http_response_code() >= 400 && req->http_response_code() < 600) ||
768 req->http_response_data().size() == 0 ||
769 // 0x30 is the ASN.1 DER encoding of a SEQUENCE. All valid OCSP/CRL/CRT
770 // responses must start with this. If we didn't check for this then a
771 // captive portal could provide an HTML reply that we would count as a
772 // 'success' (although it wouldn't count in NSS, of course).
773 req->http_response_data().data()[0] != 0x30) {
774 ok = false;
777 // We want to know if this was:
778 // 1) An OCSP request
779 // 2) A CRL request
780 // 3) A request for a missing intermediate certificate
781 // There's no sure way to do this, so we use heuristics like MIME type and
782 // URL.
783 const char* mime_type = "";
784 if (ok)
785 mime_type = req->http_response_content_type().c_str();
786 bool is_ocsp =
787 strcasecmp(mime_type, "application/ocsp-response") == 0;
788 bool is_crl = strcasecmp(mime_type, "application/x-pkcs7-crl") == 0 ||
789 strcasecmp(mime_type, "application/x-x509-crl") == 0 ||
790 strcasecmp(mime_type, "application/pkix-crl") == 0;
791 bool is_cert =
792 strcasecmp(mime_type, "application/x-x509-ca-cert") == 0 ||
793 strcasecmp(mime_type, "application/x-x509-server-cert") == 0 ||
794 strcasecmp(mime_type, "application/pkix-cert") == 0 ||
795 strcasecmp(mime_type, "application/pkcs7-mime") == 0;
797 if (!is_cert && !is_crl && !is_ocsp) {
798 // We didn't get a hint from the MIME type, so do the best that we can.
799 const std::string path = req->url().path();
800 const std::string host = req->url().host();
801 is_crl = strcasestr(path.c_str(), ".crl") != NULL;
802 is_cert = strcasestr(path.c_str(), ".crt") != NULL ||
803 strcasestr(path.c_str(), ".p7c") != NULL ||
804 strcasestr(path.c_str(), ".cer") != NULL;
805 is_ocsp = strcasestr(host.c_str(), "ocsp") != NULL ||
806 req->http_request_method() == "POST";
809 if (is_ocsp) {
810 if (ok) {
811 UMA_HISTOGRAM_TIMES("Net.OCSPRequestTimeMs", duration);
812 UMA_HISTOGRAM_BOOLEAN("Net.OCSPRequestSuccess", true);
813 } else {
814 UMA_HISTOGRAM_TIMES("Net.OCSPRequestFailedTimeMs", duration);
815 UMA_HISTOGRAM_BOOLEAN("Net.OCSPRequestSuccess", false);
817 } else if (is_crl) {
818 if (ok) {
819 UMA_HISTOGRAM_TIMES("Net.CRLRequestTimeMs", duration);
820 UMA_HISTOGRAM_BOOLEAN("Net.CRLRequestSuccess", true);
821 } else {
822 UMA_HISTOGRAM_TIMES("Net.CRLRequestFailedTimeMs", duration);
823 UMA_HISTOGRAM_BOOLEAN("Net.CRLRequestSuccess", false);
825 } else if (is_cert) {
826 if (ok)
827 UMA_HISTOGRAM_TIMES("Net.CRTRequestTimeMs", duration);
828 } else {
829 if (ok)
830 UMA_HISTOGRAM_TIMES("Net.UnknownTypeRequestTimeMs", duration);
833 if (!request_ok) {
834 PORT_SetError(SEC_ERROR_BAD_HTTP_RESPONSE); // Simple approximation.
835 return SECFailure;
838 return OCSPSetResponse(
839 req, http_response_code,
840 http_response_content_type,
841 http_response_headers,
842 http_response_data,
843 http_response_data_len);
846 SECStatus OCSPFree(SEC_HTTP_REQUEST_SESSION request) {
847 VLOG(1) << "OCSP free";
848 OCSPRequestSession* req = reinterpret_cast<OCSPRequestSession*>(request);
849 req->Cancel();
850 req->Release();
851 return SECSuccess;
854 // Data for GetAlternateOCSPAIAInfo.
856 // CN=Network Solutions Certificate Authority,O=Network Solutions L.L.C.,C=US
858 // There are two CAs with this name. Their key IDs are listed next.
859 const unsigned char network_solutions_ca_name[] = {
860 0x30, 0x62, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04,
861 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x21, 0x30, 0x1f, 0x06,
862 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x4e, 0x65, 0x74, 0x77,
863 0x6f, 0x72, 0x6b, 0x20, 0x53, 0x6f, 0x6c, 0x75, 0x74, 0x69,
864 0x6f, 0x6e, 0x73, 0x20, 0x4c, 0x2e, 0x4c, 0x2e, 0x43, 0x2e,
865 0x31, 0x30, 0x30, 0x2e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
866 0x27, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x53,
867 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x43,
868 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65,
869 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79
871 const unsigned int network_solutions_ca_name_len = 100;
873 // This CA is an intermediate CA, subordinate to UTN-USERFirst-Hardware.
874 const unsigned char network_solutions_ca_key_id[] = {
875 0x3c, 0x41, 0xe2, 0x8f, 0x08, 0x08, 0xa9, 0x4c, 0x25, 0x89,
876 0x8d, 0x6d, 0xc5, 0x38, 0xd0, 0xfc, 0x85, 0x8c, 0x62, 0x17
878 const unsigned int network_solutions_ca_key_id_len = 20;
880 // This CA is a root CA. It is also cross-certified by
881 // UTN-USERFirst-Hardware.
882 const unsigned char network_solutions_ca_key_id2[] = {
883 0x21, 0x30, 0xc9, 0xfb, 0x00, 0xd7, 0x4e, 0x98, 0xda, 0x87,
884 0xaa, 0x2a, 0xd0, 0xa7, 0x2e, 0xb1, 0x40, 0x31, 0xa7, 0x4c
886 const unsigned int network_solutions_ca_key_id2_len = 20;
888 // An entry in our OCSP responder table. |issuer| and |issuer_key_id| are
889 // the key. |ocsp_url| is the value.
890 struct OCSPResponderTableEntry {
891 SECItem issuer;
892 SECItem issuer_key_id;
893 const char *ocsp_url;
896 const OCSPResponderTableEntry g_ocsp_responder_table[] = {
899 siBuffer,
900 const_cast<unsigned char*>(network_solutions_ca_name),
901 network_solutions_ca_name_len
904 siBuffer,
905 const_cast<unsigned char*>(network_solutions_ca_key_id),
906 network_solutions_ca_key_id_len
908 "http://ocsp.netsolssl.com"
912 siBuffer,
913 const_cast<unsigned char*>(network_solutions_ca_name),
914 network_solutions_ca_name_len
917 siBuffer,
918 const_cast<unsigned char*>(network_solutions_ca_key_id2),
919 network_solutions_ca_key_id2_len
921 "http://ocsp.netsolssl.com"
925 char* GetAlternateOCSPAIAInfo(CERTCertificate *cert) {
926 if (cert && !cert->isRoot && cert->authKeyID) {
927 for (unsigned int i=0; i < arraysize(g_ocsp_responder_table); i++) {
928 if (SECITEM_CompareItem(&g_ocsp_responder_table[i].issuer,
929 &cert->derIssuer) == SECEqual &&
930 SECITEM_CompareItem(&g_ocsp_responder_table[i].issuer_key_id,
931 &cert->authKeyID->keyID) == SECEqual) {
932 return PORT_Strdup(g_ocsp_responder_table[i].ocsp_url);
937 return NULL;
940 } // anonymous namespace
942 void SetMessageLoopForNSSHttpIO() {
943 // Must have a MessageLoopForIO.
944 DCHECK(base::MessageLoopForIO::current());
946 bool used = g_ocsp_io_loop.Get().used();
948 // Should not be called when g_ocsp_io_loop has already been used.
949 DCHECK(!used);
952 void EnsureNSSHttpIOInit() {
953 g_ocsp_io_loop.Get().StartUsing();
954 g_ocsp_nss_initialization.Get();
957 void ShutdownNSSHttpIO() {
958 g_ocsp_io_loop.Get().Shutdown();
961 void ResetNSSHttpIOForTesting() {
962 g_ocsp_io_loop.Get().ReuseForTesting();
965 // This function would be called before NSS initialization.
966 void SetURLRequestContextForNSSHttpIO(URLRequestContext* request_context) {
967 pthread_mutex_lock(&g_request_context_lock);
968 if (request_context) {
969 DCHECK(!g_request_context);
971 g_request_context = request_context;
972 pthread_mutex_unlock(&g_request_context_lock);
975 } // namespace net