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