Add include.
[chromium-blink-merge.git] / net / ocsp / nss_ocsp.cc
blob61c65eed25034fd1eff1523263a399944b7ad2f1
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/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(
398 url_, DEFAULT_PRIORITY, this, NULL);
399 // To meet the privacy requirements of incognito mode.
400 request_->SetLoadFlags(LOAD_DISABLE_CACHE | LOAD_DO_NOT_SAVE_COOKIES |
401 LOAD_DO_NOT_SEND_COOKIES);
403 if (http_request_method_ == "POST") {
404 DCHECK(!upload_content_.empty());
405 DCHECK(!upload_content_type_.empty());
407 request_->set_method("POST");
408 extra_request_headers_.SetHeader(
409 HttpRequestHeaders::kContentType, upload_content_type_);
411 scoped_ptr<UploadElementReader> reader(new UploadBytesElementReader(
412 upload_content_.data(), upload_content_.size()));
413 request_->set_upload(
414 ElementsUploadDataStream::CreateWithReader(reader.Pass(), 0));
416 if (!extra_request_headers_.IsEmpty())
417 request_->SetExtraRequestHeaders(extra_request_headers_);
419 request_->Start();
420 AddRef(); // Release after |request_| deleted.
423 GURL url_; // The URL we eventually wound up at
424 std::string http_request_method_;
425 base::TimeDelta timeout_; // The timeout for OCSP
426 scoped_ptr<URLRequest> request_; // The actual request this wraps
427 scoped_refptr<IOBuffer> buffer_; // Read buffer
428 HttpRequestHeaders extra_request_headers_;
430 // HTTP POST payload. |request_| reads bytes from this.
431 std::string upload_content_;
432 std::string upload_content_type_; // MIME type of POST payload
434 int response_code_; // HTTP status code for the request
435 std::string response_content_type_;
436 scoped_refptr<HttpResponseHeaders> response_headers_;
437 std::string data_; // Results of the request
439 // |lock_| protects |finished_| and |io_loop_|.
440 mutable base::Lock lock_;
441 base::ConditionVariable cv_;
443 base::MessageLoop* io_loop_; // Message loop of the IO thread
444 bool finished_;
446 DISALLOW_COPY_AND_ASSIGN(OCSPRequestSession);
449 // Concrete class for SEC_HTTP_SERVER_SESSION.
450 class OCSPServerSession {
451 public:
452 OCSPServerSession(const char* host, PRUint16 port)
453 : host_and_port_(host, port) {}
454 ~OCSPServerSession() {}
456 OCSPRequestSession* CreateRequest(const char* http_protocol_variant,
457 const char* path_and_query_string,
458 const char* http_request_method,
459 const PRIntervalTime timeout) {
460 // We dont' support "https" because we haven't thought about
461 // whether it's safe to re-enter this code from talking to an OCSP
462 // responder over SSL.
463 if (strcmp(http_protocol_variant, "http") != 0) {
464 PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
465 return NULL;
468 std::string url_string(base::StringPrintf(
469 "%s://%s%s",
470 http_protocol_variant,
471 host_and_port_.ToString().c_str(),
472 path_and_query_string));
473 VLOG(1) << "URL [" << url_string << "]";
474 GURL url(url_string);
476 // NSS does not expose public functions to adjust the fetch timeout when
477 // using libpkix, so hardcode the upper limit for network fetches.
478 base::TimeDelta actual_timeout = std::min(
479 base::TimeDelta::FromSeconds(kNetworkFetchTimeoutInSecs),
480 base::TimeDelta::FromMilliseconds(PR_IntervalToMilliseconds(timeout)));
482 return new OCSPRequestSession(url, http_request_method, actual_timeout);
486 private:
487 HostPortPair host_and_port_;
489 DISALLOW_COPY_AND_ASSIGN(OCSPServerSession);
492 OCSPIOLoop::OCSPIOLoop()
493 : shutdown_(false),
494 used_(false),
495 io_loop_(NULL) {
498 OCSPIOLoop::~OCSPIOLoop() {
499 // IO thread was already deleted before the singleton is deleted
500 // in AtExitManager.
502 base::AutoLock autolock(lock_);
503 DCHECK(!io_loop_);
504 DCHECK(!used_);
505 DCHECK(shutdown_);
508 pthread_mutex_lock(&g_request_context_lock);
509 DCHECK(!g_request_context);
510 pthread_mutex_unlock(&g_request_context_lock);
513 void OCSPIOLoop::Shutdown() {
514 // Safe to read outside lock since we only write on IO thread anyway.
515 DCHECK(thread_checker_.CalledOnValidThread());
517 // Prevent the worker thread from trying to access |io_loop_|.
519 base::AutoLock autolock(lock_);
520 io_loop_ = NULL;
521 used_ = false;
522 shutdown_ = true;
525 CancelAllRequests();
527 pthread_mutex_lock(&g_request_context_lock);
528 g_request_context = NULL;
529 pthread_mutex_unlock(&g_request_context_lock);
532 void OCSPIOLoop::PostTaskToIOLoop(
533 const tracked_objects::Location& from_here, const base::Closure& task) {
534 base::AutoLock autolock(lock_);
535 if (io_loop_)
536 io_loop_->PostTask(from_here, task);
539 void OCSPIOLoop::EnsureIOLoop() {
540 base::AutoLock autolock(lock_);
541 DCHECK_EQ(base::MessageLoopForIO::current(), io_loop_);
544 void OCSPIOLoop::AddRequest(OCSPRequestSession* request) {
545 DCHECK(!ContainsKey(requests_, request));
546 requests_.insert(request);
549 void OCSPIOLoop::RemoveRequest(OCSPRequestSession* request) {
550 DCHECK(ContainsKey(requests_, request));
551 requests_.erase(request);
554 void OCSPIOLoop::CancelAllRequests() {
555 // CancelURLRequest() always removes the request from the requests_
556 // set synchronously.
557 while (!requests_.empty())
558 (*requests_.begin())->CancelURLRequest();
561 OCSPNSSInitialization::OCSPNSSInitialization() {
562 // NSS calls the functions in the function table to download certificates
563 // or CRLs or talk to OCSP responders over HTTP. These functions must
564 // set an NSS/NSPR error code when they fail. Otherwise NSS will get the
565 // residual error code from an earlier failed function call.
566 client_fcn_.version = 1;
567 SEC_HttpClientFcnV1Struct *ft = &client_fcn_.fcnTable.ftable1;
568 ft->createSessionFcn = OCSPCreateSession;
569 ft->keepAliveSessionFcn = OCSPKeepAliveSession;
570 ft->freeSessionFcn = OCSPFreeSession;
571 ft->createFcn = OCSPCreate;
572 ft->setPostDataFcn = OCSPSetPostData;
573 ft->addHeaderFcn = OCSPAddHeader;
574 ft->trySendAndReceiveFcn = OCSPTrySendAndReceive;
575 ft->cancelFcn = NULL;
576 ft->freeFcn = OCSPFree;
577 SECStatus status = SEC_RegisterDefaultHttpClient(&client_fcn_);
578 if (status != SECSuccess) {
579 NOTREACHED() << "Error initializing OCSP: " << PR_GetError();
582 // Work around NSS bugs 524013 and 564334. NSS incorrectly thinks the
583 // CRLs for Network Solutions Certificate Authority have bad signatures,
584 // which causes certificates issued by that CA to be reported as revoked.
585 // By using OCSP for those certificates, which don't have AIA extensions,
586 // we can work around these bugs. See http://crbug.com/41730.
587 CERT_StringFromCertFcn old_callback = NULL;
588 status = CERT_RegisterAlternateOCSPAIAInfoCallBack(
589 GetAlternateOCSPAIAInfo, &old_callback);
590 if (status == SECSuccess) {
591 DCHECK(!old_callback);
592 } else {
593 NOTREACHED() << "Error initializing OCSP: " << PR_GetError();
597 OCSPNSSInitialization::~OCSPNSSInitialization() {
598 SECStatus status = CERT_RegisterAlternateOCSPAIAInfoCallBack(NULL, NULL);
599 if (status != SECSuccess) {
600 LOG(ERROR) << "Error unregistering OCSP: " << PR_GetError();
605 // OCSP Http Client functions.
606 // Our Http Client functions operate in blocking mode.
607 SECStatus OCSPCreateSession(const char* host, PRUint16 portnum,
608 SEC_HTTP_SERVER_SESSION* pSession) {
609 VLOG(1) << "OCSP create session: host=" << host << " port=" << portnum;
610 pthread_mutex_lock(&g_request_context_lock);
611 URLRequestContext* request_context = g_request_context;
612 pthread_mutex_unlock(&g_request_context_lock);
613 if (request_context == NULL) {
614 LOG(ERROR) << "No URLRequestContext for NSS HTTP handler. host: " << host;
615 // The application failed to call SetURLRequestContextForNSSHttpIO or
616 // has already called ShutdownNSSHttpIO, so we can't create and use
617 // URLRequest. PR_NOT_IMPLEMENTED_ERROR is not an accurate error
618 // code for these error conditions, but is close enough.
619 PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
620 return SECFailure;
622 *pSession = new OCSPServerSession(host, portnum);
623 return SECSuccess;
626 SECStatus OCSPKeepAliveSession(SEC_HTTP_SERVER_SESSION session,
627 PRPollDesc **pPollDesc) {
628 VLOG(1) << "OCSP keep alive";
629 if (pPollDesc)
630 *pPollDesc = NULL;
631 return SECSuccess;
634 SECStatus OCSPFreeSession(SEC_HTTP_SERVER_SESSION session) {
635 VLOG(1) << "OCSP free session";
636 delete reinterpret_cast<OCSPServerSession*>(session);
637 return SECSuccess;
640 SECStatus OCSPCreate(SEC_HTTP_SERVER_SESSION session,
641 const char* http_protocol_variant,
642 const char* path_and_query_string,
643 const char* http_request_method,
644 const PRIntervalTime timeout,
645 SEC_HTTP_REQUEST_SESSION* pRequest) {
646 VLOG(1) << "OCSP create protocol=" << http_protocol_variant
647 << " path_and_query=" << path_and_query_string
648 << " http_request_method=" << http_request_method
649 << " timeout=" << timeout;
650 OCSPServerSession* ocsp_session =
651 reinterpret_cast<OCSPServerSession*>(session);
653 OCSPRequestSession* req = ocsp_session->CreateRequest(http_protocol_variant,
654 path_and_query_string,
655 http_request_method,
656 timeout);
657 SECStatus rv = SECFailure;
658 if (req) {
659 req->AddRef(); // Release in OCSPFree().
660 rv = SECSuccess;
662 *pRequest = req;
663 return rv;
666 SECStatus OCSPSetPostData(SEC_HTTP_REQUEST_SESSION request,
667 const char* http_data,
668 const PRUint32 http_data_len,
669 const char* http_content_type) {
670 VLOG(1) << "OCSP set post data len=" << http_data_len;
671 OCSPRequestSession* req = reinterpret_cast<OCSPRequestSession*>(request);
673 req->SetPostData(http_data, http_data_len, http_content_type);
674 return SECSuccess;
677 SECStatus OCSPAddHeader(SEC_HTTP_REQUEST_SESSION request,
678 const char* http_header_name,
679 const char* http_header_value) {
680 VLOG(1) << "OCSP add header name=" << http_header_name
681 << " value=" << http_header_value;
682 OCSPRequestSession* req = reinterpret_cast<OCSPRequestSession*>(request);
684 req->AddHeader(http_header_name, http_header_value);
685 return SECSuccess;
688 // Sets response of |req| in the output parameters.
689 // It is helper routine for OCSP trySendAndReceiveFcn.
690 // |http_response_data_len| could be used as input parameter. If it has
691 // non-zero value, it is considered as maximum size of |http_response_data|.
692 SECStatus OCSPSetResponse(OCSPRequestSession* req,
693 PRUint16* http_response_code,
694 const char** http_response_content_type,
695 const char** http_response_headers,
696 const char** http_response_data,
697 PRUint32* http_response_data_len) {
698 DCHECK(req->Finished());
699 const std::string& data = req->http_response_data();
700 if (http_response_data_len && *http_response_data_len) {
701 if (*http_response_data_len < data.size()) {
702 LOG(ERROR) << "response body too large: " << *http_response_data_len
703 << " < " << data.size();
704 *http_response_data_len = data.size();
705 PORT_SetError(SEC_ERROR_BAD_HTTP_RESPONSE);
706 return SECFailure;
709 VLOG(1) << "OCSP response "
710 << " response_code=" << req->http_response_code()
711 << " content_type=" << req->http_response_content_type()
712 << " header=" << req->http_response_headers()
713 << " data_len=" << data.size();
714 if (http_response_code)
715 *http_response_code = req->http_response_code();
716 if (http_response_content_type)
717 *http_response_content_type = req->http_response_content_type().c_str();
718 if (http_response_headers)
719 *http_response_headers = req->http_response_headers().c_str();
720 if (http_response_data)
721 *http_response_data = data.data();
722 if (http_response_data_len)
723 *http_response_data_len = data.size();
724 return SECSuccess;
727 SECStatus OCSPTrySendAndReceive(SEC_HTTP_REQUEST_SESSION request,
728 PRPollDesc** pPollDesc,
729 PRUint16* http_response_code,
730 const char** http_response_content_type,
731 const char** http_response_headers,
732 const char** http_response_data,
733 PRUint32* http_response_data_len) {
734 if (http_response_data_len) {
735 // We must always set an output value, even on failure. The output value 0
736 // means the failure was unrelated to the acceptable response data length.
737 *http_response_data_len = 0;
740 VLOG(1) << "OCSP try send and receive";
741 OCSPRequestSession* req = reinterpret_cast<OCSPRequestSession*>(request);
742 // We support blocking mode only.
743 if (pPollDesc)
744 *pPollDesc = NULL;
746 if (req->Started() || req->Finished()) {
747 // We support blocking mode only, so this function shouldn't be called
748 // again when req has stareted or finished.
749 NOTREACHED();
750 PORT_SetError(SEC_ERROR_BAD_HTTP_RESPONSE); // Simple approximation.
751 return SECFailure;
754 const base::Time start_time = base::Time::Now();
755 bool request_ok = true;
756 req->Start();
757 if (!req->Wait() || req->http_response_code() == static_cast<PRUint16>(-1)) {
758 // If the response code is -1, the request failed and there is no response.
759 request_ok = false;
761 const base::TimeDelta duration = base::Time::Now() - start_time;
763 // For metrics, we want to know if the request was 'successful' or not.
764 // |request_ok| determines if we'll pass the response back to NSS and |ok|
765 // keep track of if we think the response was good.
766 bool ok = true;
767 if (!request_ok ||
768 (req->http_response_code() >= 400 && req->http_response_code() < 600) ||
769 req->http_response_data().size() == 0 ||
770 // 0x30 is the ASN.1 DER encoding of a SEQUENCE. All valid OCSP/CRL/CRT
771 // responses must start with this. If we didn't check for this then a
772 // captive portal could provide an HTML reply that we would count as a
773 // 'success' (although it wouldn't count in NSS, of course).
774 req->http_response_data().data()[0] != 0x30) {
775 ok = false;
778 // We want to know if this was:
779 // 1) An OCSP request
780 // 2) A CRL request
781 // 3) A request for a missing intermediate certificate
782 // There's no sure way to do this, so we use heuristics like MIME type and
783 // URL.
784 const char* mime_type = "";
785 if (ok)
786 mime_type = req->http_response_content_type().c_str();
787 bool is_ocsp =
788 strcasecmp(mime_type, "application/ocsp-response") == 0;
789 bool is_crl = strcasecmp(mime_type, "application/x-pkcs7-crl") == 0 ||
790 strcasecmp(mime_type, "application/x-x509-crl") == 0 ||
791 strcasecmp(mime_type, "application/pkix-crl") == 0;
792 bool is_cert =
793 strcasecmp(mime_type, "application/x-x509-ca-cert") == 0 ||
794 strcasecmp(mime_type, "application/x-x509-server-cert") == 0 ||
795 strcasecmp(mime_type, "application/pkix-cert") == 0 ||
796 strcasecmp(mime_type, "application/pkcs7-mime") == 0;
798 if (!is_cert && !is_crl && !is_ocsp) {
799 // We didn't get a hint from the MIME type, so do the best that we can.
800 const std::string path = req->url().path();
801 const std::string host = req->url().host();
802 is_crl = strcasestr(path.c_str(), ".crl") != NULL;
803 is_cert = strcasestr(path.c_str(), ".crt") != NULL ||
804 strcasestr(path.c_str(), ".p7c") != NULL ||
805 strcasestr(path.c_str(), ".cer") != NULL;
806 is_ocsp = strcasestr(host.c_str(), "ocsp") != NULL ||
807 req->http_request_method() == "POST";
810 if (is_ocsp) {
811 if (ok) {
812 UMA_HISTOGRAM_TIMES("Net.OCSPRequestTimeMs", duration);
813 UMA_HISTOGRAM_BOOLEAN("Net.OCSPRequestSuccess", true);
814 } else {
815 UMA_HISTOGRAM_TIMES("Net.OCSPRequestFailedTimeMs", duration);
816 UMA_HISTOGRAM_BOOLEAN("Net.OCSPRequestSuccess", false);
818 } else if (is_crl) {
819 if (ok) {
820 UMA_HISTOGRAM_TIMES("Net.CRLRequestTimeMs", duration);
821 UMA_HISTOGRAM_BOOLEAN("Net.CRLRequestSuccess", true);
822 } else {
823 UMA_HISTOGRAM_TIMES("Net.CRLRequestFailedTimeMs", duration);
824 UMA_HISTOGRAM_BOOLEAN("Net.CRLRequestSuccess", false);
826 } else if (is_cert) {
827 if (ok)
828 UMA_HISTOGRAM_TIMES("Net.CRTRequestTimeMs", duration);
829 } else {
830 if (ok)
831 UMA_HISTOGRAM_TIMES("Net.UnknownTypeRequestTimeMs", duration);
834 if (!request_ok) {
835 PORT_SetError(SEC_ERROR_BAD_HTTP_RESPONSE); // Simple approximation.
836 return SECFailure;
839 return OCSPSetResponse(
840 req, http_response_code,
841 http_response_content_type,
842 http_response_headers,
843 http_response_data,
844 http_response_data_len);
847 SECStatus OCSPFree(SEC_HTTP_REQUEST_SESSION request) {
848 VLOG(1) << "OCSP free";
849 OCSPRequestSession* req = reinterpret_cast<OCSPRequestSession*>(request);
850 req->Cancel();
851 req->Release();
852 return SECSuccess;
855 // Data for GetAlternateOCSPAIAInfo.
857 // CN=Network Solutions Certificate Authority,O=Network Solutions L.L.C.,C=US
859 // There are two CAs with this name. Their key IDs are listed next.
860 const unsigned char network_solutions_ca_name[] = {
861 0x30, 0x62, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04,
862 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x21, 0x30, 0x1f, 0x06,
863 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x4e, 0x65, 0x74, 0x77,
864 0x6f, 0x72, 0x6b, 0x20, 0x53, 0x6f, 0x6c, 0x75, 0x74, 0x69,
865 0x6f, 0x6e, 0x73, 0x20, 0x4c, 0x2e, 0x4c, 0x2e, 0x43, 0x2e,
866 0x31, 0x30, 0x30, 0x2e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
867 0x27, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x53,
868 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x43,
869 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65,
870 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79
872 const unsigned int network_solutions_ca_name_len = 100;
874 // This CA is an intermediate CA, subordinate to UTN-USERFirst-Hardware.
875 const unsigned char network_solutions_ca_key_id[] = {
876 0x3c, 0x41, 0xe2, 0x8f, 0x08, 0x08, 0xa9, 0x4c, 0x25, 0x89,
877 0x8d, 0x6d, 0xc5, 0x38, 0xd0, 0xfc, 0x85, 0x8c, 0x62, 0x17
879 const unsigned int network_solutions_ca_key_id_len = 20;
881 // This CA is a root CA. It is also cross-certified by
882 // UTN-USERFirst-Hardware.
883 const unsigned char network_solutions_ca_key_id2[] = {
884 0x21, 0x30, 0xc9, 0xfb, 0x00, 0xd7, 0x4e, 0x98, 0xda, 0x87,
885 0xaa, 0x2a, 0xd0, 0xa7, 0x2e, 0xb1, 0x40, 0x31, 0xa7, 0x4c
887 const unsigned int network_solutions_ca_key_id2_len = 20;
889 // An entry in our OCSP responder table. |issuer| and |issuer_key_id| are
890 // the key. |ocsp_url| is the value.
891 struct OCSPResponderTableEntry {
892 SECItem issuer;
893 SECItem issuer_key_id;
894 const char *ocsp_url;
897 const OCSPResponderTableEntry g_ocsp_responder_table[] = {
900 siBuffer,
901 const_cast<unsigned char*>(network_solutions_ca_name),
902 network_solutions_ca_name_len
905 siBuffer,
906 const_cast<unsigned char*>(network_solutions_ca_key_id),
907 network_solutions_ca_key_id_len
909 "http://ocsp.netsolssl.com"
913 siBuffer,
914 const_cast<unsigned char*>(network_solutions_ca_name),
915 network_solutions_ca_name_len
918 siBuffer,
919 const_cast<unsigned char*>(network_solutions_ca_key_id2),
920 network_solutions_ca_key_id2_len
922 "http://ocsp.netsolssl.com"
926 char* GetAlternateOCSPAIAInfo(CERTCertificate *cert) {
927 if (cert && !cert->isRoot && cert->authKeyID) {
928 for (unsigned int i=0; i < arraysize(g_ocsp_responder_table); i++) {
929 if (SECITEM_CompareItem(&g_ocsp_responder_table[i].issuer,
930 &cert->derIssuer) == SECEqual &&
931 SECITEM_CompareItem(&g_ocsp_responder_table[i].issuer_key_id,
932 &cert->authKeyID->keyID) == SECEqual) {
933 return PORT_Strdup(g_ocsp_responder_table[i].ocsp_url);
938 return NULL;
941 } // anonymous namespace
943 void SetMessageLoopForNSSHttpIO() {
944 // Must have a MessageLoopForIO.
945 DCHECK(base::MessageLoopForIO::current());
947 bool used = g_ocsp_io_loop.Get().used();
949 // Should not be called when g_ocsp_io_loop has already been used.
950 DCHECK(!used);
953 void EnsureNSSHttpIOInit() {
954 g_ocsp_io_loop.Get().StartUsing();
955 g_ocsp_nss_initialization.Get();
958 void ShutdownNSSHttpIO() {
959 g_ocsp_io_loop.Get().Shutdown();
962 void ResetNSSHttpIOForTesting() {
963 g_ocsp_io_loop.Get().ReuseForTesting();
966 // This function would be called before NSS initialization.
967 void SetURLRequestContextForNSSHttpIO(URLRequestContext* request_context) {
968 pthread_mutex_lock(&g_request_context_lock);
969 if (request_context) {
970 DCHECK(!g_request_context);
972 g_request_context = request_context;
973 pthread_mutex_unlock(&g_request_context_lock);
976 } // namespace net