Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / net / cert_net / nss_ocsp.cc
blob835993f8a9caabfb9a245c94b3d768ec5dbdc917
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/location.h"
23 #include "base/logging.h"
24 #include "base/memory/scoped_ptr.h"
25 #include "base/metrics/histogram_macros.h"
26 #include "base/single_thread_task_runner.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 DCHECK_EQ(request_.get(), request);
302 DCHECK_EQ(base::MessageLoopForIO::current(), io_loop_);
304 int bytes_read = 0;
305 if (request->status().is_success()) {
306 response_code_ = request_->GetResponseCode();
307 response_headers_ = request_->response_headers();
308 response_headers_->GetMimeType(&response_content_type_);
309 request_->Read(buffer_.get(), kRecvBufferSize, &bytes_read);
311 OnReadCompleted(request_.get(), bytes_read);
314 void OnReadCompleted(URLRequest* request, int bytes_read) override {
315 DCHECK_EQ(request_.get(), 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 request_.reset();
326 g_ocsp_io_loop.Get().RemoveRequest(this);
328 base::AutoLock autolock(lock_);
329 finished_ = true;
330 io_loop_ = NULL;
332 cv_.Signal();
333 Release(); // Balanced with StartURLRequest().
337 // Must be called on the IO loop thread.
338 void CancelURLRequest() {
339 #ifndef NDEBUG
341 base::AutoLock autolock(lock_);
342 if (io_loop_)
343 DCHECK_EQ(base::MessageLoopForIO::current(), io_loop_);
345 #endif
346 if (request_) {
347 request_.reset();
348 g_ocsp_io_loop.Get().RemoveRequest(this);
350 base::AutoLock autolock(lock_);
351 finished_ = true;
352 io_loop_ = NULL;
354 cv_.Signal();
355 Release(); // Balanced with StartURLRequest().
359 private:
360 friend class base::RefCountedThreadSafe<OCSPRequestSession>;
362 ~OCSPRequestSession() override {
363 // When this destructor is called, there should be only one thread that has
364 // a reference to this object, and so that thread doesn't need to lock
365 // |lock_| here.
366 DCHECK(!request_);
367 DCHECK(!io_loop_);
370 // Must call this method while holding |lock_|.
371 void CancelLocked() {
372 lock_.AssertAcquired();
373 if (io_loop_) {
374 io_loop_->task_runner()->PostTask(
375 FROM_HERE, 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_->task_runner()->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