Dismiss autofill popup on screen orientation change.
[chromium-blink-merge.git] / net / ocsp / nss_ocsp.cc
blobaea8fc2ad4190a4a5e52b29c728674f74f2a8a86
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/message_loop/message_loop.h"
24 #include "base/metrics/histogram.h"
25 #include "base/stl_util.h"
26 #include "base/strings/string_util.h"
27 #include "base/strings/stringprintf.h"
28 #include "base/synchronization/condition_variable.h"
29 #include "base/synchronization/lock.h"
30 #include "base/threading/thread_checker.h"
31 #include "base/time/time.h"
32 #include "net/base/host_port_pair.h"
33 #include "net/base/io_buffer.h"
34 #include "net/base/load_flags.h"
35 #include "net/base/upload_bytes_element_reader.h"
36 #include "net/base/upload_data_stream.h"
37 #include "net/http/http_request_headers.h"
38 #include "net/http/http_response_headers.h"
39 #include "net/url_request/url_request.h"
40 #include "net/url_request/url_request_context.h"
41 #include "url/gurl.h"
43 namespace net {
45 namespace {
47 // Protects |g_request_context|.
48 pthread_mutex_t g_request_context_lock = PTHREAD_MUTEX_INITIALIZER;
49 URLRequestContext* g_request_context = NULL;
51 // The default timeout for network fetches in NSS is 60 seconds. Choose a
52 // saner upper limit for OCSP/CRL/AIA fetches.
53 const int kNetworkFetchTimeoutInSecs = 15;
55 class OCSPRequestSession;
57 class OCSPIOLoop {
58 public:
59 void StartUsing() {
60 base::AutoLock autolock(lock_);
61 used_ = true;
62 io_loop_ = base::MessageLoopForIO::current();
63 DCHECK(io_loop_);
66 // Called on IO loop.
67 void Shutdown();
69 bool used() const {
70 base::AutoLock autolock(lock_);
71 return used_;
74 // Called from worker thread.
75 void PostTaskToIOLoop(const tracked_objects::Location& from_here,
76 const base::Closure& task);
78 void EnsureIOLoop();
80 void AddRequest(OCSPRequestSession* request);
81 void RemoveRequest(OCSPRequestSession* request);
83 // Clears internal state and calls |StartUsing()|. Should be called only in
84 // the context of testing.
85 void ReuseForTesting() {
87 base::AutoLock autolock(lock_);
88 DCHECK(base::MessageLoopForIO::current());
89 thread_checker_.DetachFromThread();
90 thread_checker_.CalledOnValidThread();
91 shutdown_ = false;
92 used_ = false;
94 StartUsing();
97 private:
98 friend struct base::DefaultLazyInstanceTraits<OCSPIOLoop>;
100 OCSPIOLoop();
101 ~OCSPIOLoop();
103 void CancelAllRequests();
105 mutable base::Lock lock_;
106 bool shutdown_; // Protected by |lock_|.
107 std::set<OCSPRequestSession*> requests_; // Protected by |lock_|.
108 bool used_; // Protected by |lock_|.
109 // This should not be modified after |used_|.
110 base::MessageLoopForIO* io_loop_; // Protected by |lock_|.
111 base::ThreadChecker thread_checker_;
113 DISALLOW_COPY_AND_ASSIGN(OCSPIOLoop);
116 base::LazyInstance<OCSPIOLoop>::Leaky
117 g_ocsp_io_loop = LAZY_INSTANCE_INITIALIZER;
119 const int kRecvBufferSize = 4096;
121 // All OCSP handlers should be called in the context of
122 // CertVerifier's thread (i.e. worker pool, not on the I/O thread).
123 // It supports blocking mode only.
125 SECStatus OCSPCreateSession(const char* host, PRUint16 portnum,
126 SEC_HTTP_SERVER_SESSION* pSession);
127 SECStatus OCSPKeepAliveSession(SEC_HTTP_SERVER_SESSION session,
128 PRPollDesc **pPollDesc);
129 SECStatus OCSPFreeSession(SEC_HTTP_SERVER_SESSION session);
131 SECStatus OCSPCreate(SEC_HTTP_SERVER_SESSION session,
132 const char* http_protocol_variant,
133 const char* path_and_query_string,
134 const char* http_request_method,
135 const PRIntervalTime timeout,
136 SEC_HTTP_REQUEST_SESSION* pRequest);
137 SECStatus OCSPSetPostData(SEC_HTTP_REQUEST_SESSION request,
138 const char* http_data,
139 const PRUint32 http_data_len,
140 const char* http_content_type);
141 SECStatus OCSPAddHeader(SEC_HTTP_REQUEST_SESSION request,
142 const char* http_header_name,
143 const char* http_header_value);
144 SECStatus OCSPTrySendAndReceive(SEC_HTTP_REQUEST_SESSION request,
145 PRPollDesc** pPollDesc,
146 PRUint16* http_response_code,
147 const char** http_response_content_type,
148 const char** http_response_headers,
149 const char** http_response_data,
150 PRUint32* http_response_data_len);
151 SECStatus OCSPFree(SEC_HTTP_REQUEST_SESSION request);
153 char* GetAlternateOCSPAIAInfo(CERTCertificate *cert);
155 class OCSPNSSInitialization {
156 private:
157 friend struct base::DefaultLazyInstanceTraits<OCSPNSSInitialization>;
159 OCSPNSSInitialization();
160 ~OCSPNSSInitialization();
162 SEC_HttpClientFcn client_fcn_;
164 DISALLOW_COPY_AND_ASSIGN(OCSPNSSInitialization);
167 base::LazyInstance<OCSPNSSInitialization> g_ocsp_nss_initialization =
168 LAZY_INSTANCE_INITIALIZER;
170 // Concrete class for SEC_HTTP_REQUEST_SESSION.
171 // Public methods except virtual methods of URLRequest::Delegate
172 // (On* methods) run on certificate verifier thread (worker thread).
173 // Virtual methods of URLRequest::Delegate and private methods run
174 // on IO thread.
175 class OCSPRequestSession
176 : public base::RefCountedThreadSafe<OCSPRequestSession>,
177 public URLRequest::Delegate {
178 public:
179 OCSPRequestSession(const GURL& url,
180 const char* http_request_method,
181 base::TimeDelta timeout)
182 : url_(url),
183 http_request_method_(http_request_method),
184 timeout_(timeout),
185 request_(NULL),
186 buffer_(new IOBuffer(kRecvBufferSize)),
187 response_code_(-1),
188 cv_(&lock_),
189 io_loop_(NULL),
190 finished_(false) {}
192 void SetPostData(const char* http_data, PRUint32 http_data_len,
193 const char* http_content_type) {
194 // |upload_content_| should not be modified if |request_| is active.
195 DCHECK(!request_);
196 upload_content_.assign(http_data, http_data_len);
197 upload_content_type_.assign(http_content_type);
200 void AddHeader(const char* http_header_name, const char* http_header_value) {
201 extra_request_headers_.SetHeader(http_header_name,
202 http_header_value);
205 void Start() {
206 // At this point, it runs on worker thread.
207 // |io_loop_| was initialized to be NULL in constructor, and
208 // set only in StartURLRequest, so no need to lock |lock_| here.
209 DCHECK(!io_loop_);
210 g_ocsp_io_loop.Get().PostTaskToIOLoop(
211 FROM_HERE,
212 base::Bind(&OCSPRequestSession::StartURLRequest, this));
215 bool Started() const {
216 return request_ != NULL;
219 void Cancel() {
220 // IO thread may set |io_loop_| to NULL, so protect by |lock_|.
221 base::AutoLock autolock(lock_);
222 CancelLocked();
225 bool Finished() const {
226 base::AutoLock autolock(lock_);
227 return finished_;
230 bool Wait() {
231 base::TimeDelta timeout = timeout_;
232 base::AutoLock autolock(lock_);
233 while (!finished_) {
234 base::TimeTicks last_time = base::TimeTicks::Now();
235 cv_.TimedWait(timeout);
236 // Check elapsed time
237 base::TimeDelta elapsed_time = base::TimeTicks::Now() - last_time;
238 timeout -= elapsed_time;
239 if (timeout < base::TimeDelta()) {
240 VLOG(1) << "OCSP Timed out";
241 if (!finished_)
242 CancelLocked();
243 break;
246 return finished_;
249 const GURL& url() const {
250 return url_;
253 const std::string& http_request_method() const {
254 return http_request_method_;
257 base::TimeDelta timeout() const {
258 return timeout_;
261 PRUint16 http_response_code() const {
262 DCHECK(finished_);
263 return response_code_;
266 const std::string& http_response_content_type() const {
267 DCHECK(finished_);
268 return response_content_type_;
271 const std::string& http_response_headers() const {
272 DCHECK(finished_);
273 return response_headers_->raw_headers();
276 const std::string& http_response_data() const {
277 DCHECK(finished_);
278 return data_;
281 virtual void OnReceivedRedirect(URLRequest* request,
282 const GURL& new_url,
283 bool* defer_redirect) OVERRIDE {
284 DCHECK_EQ(request, request_);
285 DCHECK_EQ(base::MessageLoopForIO::current(), io_loop_);
287 if (!new_url.SchemeIs("http")) {
288 // Prevent redirects to non-HTTP schemes, including HTTPS. This matches
289 // the initial check in OCSPServerSession::CreateRequest().
290 CancelURLRequest();
294 virtual void OnResponseStarted(URLRequest* request) OVERRIDE {
295 DCHECK_EQ(request, request_);
296 DCHECK_EQ(base::MessageLoopForIO::current(), io_loop_);
298 int bytes_read = 0;
299 if (request->status().is_success()) {
300 response_code_ = request_->GetResponseCode();
301 response_headers_ = request_->response_headers();
302 response_headers_->GetMimeType(&response_content_type_);
303 request_->Read(buffer_.get(), kRecvBufferSize, &bytes_read);
305 OnReadCompleted(request_, bytes_read);
308 virtual void OnReadCompleted(URLRequest* request,
309 int bytes_read) OVERRIDE {
310 DCHECK_EQ(request, request_);
311 DCHECK_EQ(base::MessageLoopForIO::current(), io_loop_);
313 do {
314 if (!request_->status().is_success() || bytes_read <= 0)
315 break;
316 data_.append(buffer_->data(), bytes_read);
317 } while (request_->Read(buffer_.get(), kRecvBufferSize, &bytes_read));
319 if (!request_->status().is_io_pending()) {
320 delete request_;
321 request_ = NULL;
322 g_ocsp_io_loop.Get().RemoveRequest(this);
324 base::AutoLock autolock(lock_);
325 finished_ = true;
326 io_loop_ = NULL;
328 cv_.Signal();
329 Release(); // Balanced with StartURLRequest().
333 // Must be called on the IO loop thread.
334 void CancelURLRequest() {
335 #ifndef NDEBUG
337 base::AutoLock autolock(lock_);
338 if (io_loop_)
339 DCHECK_EQ(base::MessageLoopForIO::current(), io_loop_);
341 #endif
342 if (request_) {
343 request_->Cancel();
344 delete request_;
345 request_ = NULL;
346 g_ocsp_io_loop.Get().RemoveRequest(this);
348 base::AutoLock autolock(lock_);
349 finished_ = true;
350 io_loop_ = NULL;
352 cv_.Signal();
353 Release(); // Balanced with StartURLRequest().
357 private:
358 friend class base::RefCountedThreadSafe<OCSPRequestSession>;
360 virtual ~OCSPRequestSession() {
361 // When this destructor is called, there should be only one thread that has
362 // a reference to this object, and so that thread doesn't need to lock
363 // |lock_| here.
364 DCHECK(!request_);
365 DCHECK(!io_loop_);
368 // Must call this method while holding |lock_|.
369 void CancelLocked() {
370 lock_.AssertAcquired();
371 if (io_loop_) {
372 io_loop_->PostTask(
373 FROM_HERE,
374 base::Bind(&OCSPRequestSession::CancelURLRequest, this));
378 // Runs on |g_ocsp_io_loop|'s IO loop.
379 void StartURLRequest() {
380 DCHECK(!request_);
382 pthread_mutex_lock(&g_request_context_lock);
383 URLRequestContext* url_request_context = g_request_context;
384 pthread_mutex_unlock(&g_request_context_lock);
386 if (url_request_context == NULL)
387 return;
390 base::AutoLock autolock(lock_);
391 DCHECK(!io_loop_);
392 io_loop_ = base::MessageLoopForIO::current();
393 g_ocsp_io_loop.Get().AddRequest(this);
396 request_ = new URLRequest(url_, this, url_request_context);
397 // To meet the privacy requirements of incognito mode.
398 request_->set_load_flags(LOAD_DISABLE_CACHE | LOAD_DO_NOT_SAVE_COOKIES |
399 LOAD_DO_NOT_SEND_COOKIES);
401 if (http_request_method_ == "POST") {
402 DCHECK(!upload_content_.empty());
403 DCHECK(!upload_content_type_.empty());
405 request_->set_method("POST");
406 extra_request_headers_.SetHeader(
407 HttpRequestHeaders::kContentType, upload_content_type_);
409 scoped_ptr<UploadElementReader> reader(new UploadBytesElementReader(
410 upload_content_.data(), upload_content_.size()));
411 request_->set_upload(make_scoped_ptr(
412 UploadDataStream::CreateWithReader(reader.Pass(), 0)));
414 if (!extra_request_headers_.IsEmpty())
415 request_->SetExtraRequestHeaders(extra_request_headers_);
417 request_->Start();
418 AddRef(); // Release after |request_| deleted.
421 GURL url_; // The URL we eventually wound up at
422 std::string http_request_method_;
423 base::TimeDelta timeout_; // The timeout for OCSP
424 URLRequest* request_; // The actual request this wraps
425 scoped_refptr<IOBuffer> buffer_; // Read buffer
426 HttpRequestHeaders extra_request_headers_;
428 // HTTP POST payload. |request_| reads bytes from this.
429 std::string upload_content_;
430 std::string upload_content_type_; // MIME type of POST payload
432 int response_code_; // HTTP status code for the request
433 std::string response_content_type_;
434 scoped_refptr<HttpResponseHeaders> response_headers_;
435 std::string data_; // Results of the request
437 // |lock_| protects |finished_| and |io_loop_|.
438 mutable base::Lock lock_;
439 base::ConditionVariable cv_;
441 base::MessageLoop* io_loop_; // Message loop of the IO thread
442 bool finished_;
444 DISALLOW_COPY_AND_ASSIGN(OCSPRequestSession);
447 // Concrete class for SEC_HTTP_SERVER_SESSION.
448 class OCSPServerSession {
449 public:
450 OCSPServerSession(const char* host, PRUint16 port)
451 : host_and_port_(host, port) {}
452 ~OCSPServerSession() {}
454 OCSPRequestSession* CreateRequest(const char* http_protocol_variant,
455 const char* path_and_query_string,
456 const char* http_request_method,
457 const PRIntervalTime timeout) {
458 // We dont' support "https" because we haven't thought about
459 // whether it's safe to re-enter this code from talking to an OCSP
460 // responder over SSL.
461 if (strcmp(http_protocol_variant, "http") != 0) {
462 PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
463 return NULL;
466 std::string url_string(base::StringPrintf(
467 "%s://%s%s",
468 http_protocol_variant,
469 host_and_port_.ToString().c_str(),
470 path_and_query_string));
471 VLOG(1) << "URL [" << url_string << "]";
472 GURL url(url_string);
474 // NSS does not expose public functions to adjust the fetch timeout when
475 // using libpkix, so hardcode the upper limit for network fetches.
476 base::TimeDelta actual_timeout = std::min(
477 base::TimeDelta::FromSeconds(kNetworkFetchTimeoutInSecs),
478 base::TimeDelta::FromMilliseconds(PR_IntervalToMilliseconds(timeout)));
480 return new OCSPRequestSession(url, http_request_method, actual_timeout);
484 private:
485 HostPortPair host_and_port_;
487 DISALLOW_COPY_AND_ASSIGN(OCSPServerSession);
490 OCSPIOLoop::OCSPIOLoop()
491 : shutdown_(false),
492 used_(false),
493 io_loop_(NULL) {
496 OCSPIOLoop::~OCSPIOLoop() {
497 // IO thread was already deleted before the singleton is deleted
498 // in AtExitManager.
500 base::AutoLock autolock(lock_);
501 DCHECK(!io_loop_);
502 DCHECK(!used_);
503 DCHECK(shutdown_);
506 pthread_mutex_lock(&g_request_context_lock);
507 DCHECK(!g_request_context);
508 pthread_mutex_unlock(&g_request_context_lock);
511 void OCSPIOLoop::Shutdown() {
512 // Safe to read outside lock since we only write on IO thread anyway.
513 DCHECK(thread_checker_.CalledOnValidThread());
515 // Prevent the worker thread from trying to access |io_loop_|.
517 base::AutoLock autolock(lock_);
518 io_loop_ = NULL;
519 used_ = false;
520 shutdown_ = true;
523 CancelAllRequests();
525 pthread_mutex_lock(&g_request_context_lock);
526 g_request_context = NULL;
527 pthread_mutex_unlock(&g_request_context_lock);
530 void OCSPIOLoop::PostTaskToIOLoop(
531 const tracked_objects::Location& from_here, const base::Closure& task) {
532 base::AutoLock autolock(lock_);
533 if (io_loop_)
534 io_loop_->PostTask(from_here, task);
537 void OCSPIOLoop::EnsureIOLoop() {
538 base::AutoLock autolock(lock_);
539 DCHECK_EQ(base::MessageLoopForIO::current(), io_loop_);
542 void OCSPIOLoop::AddRequest(OCSPRequestSession* request) {
543 DCHECK(!ContainsKey(requests_, request));
544 requests_.insert(request);
547 void OCSPIOLoop::RemoveRequest(OCSPRequestSession* request) {
548 DCHECK(ContainsKey(requests_, request));
549 requests_.erase(request);
552 void OCSPIOLoop::CancelAllRequests() {
553 // CancelURLRequest() always removes the request from the requests_
554 // set synchronously.
555 while (!requests_.empty())
556 (*requests_.begin())->CancelURLRequest();
559 OCSPNSSInitialization::OCSPNSSInitialization() {
560 // NSS calls the functions in the function table to download certificates
561 // or CRLs or talk to OCSP responders over HTTP. These functions must
562 // set an NSS/NSPR error code when they fail. Otherwise NSS will get the
563 // residual error code from an earlier failed function call.
564 client_fcn_.version = 1;
565 SEC_HttpClientFcnV1Struct *ft = &client_fcn_.fcnTable.ftable1;
566 ft->createSessionFcn = OCSPCreateSession;
567 ft->keepAliveSessionFcn = OCSPKeepAliveSession;
568 ft->freeSessionFcn = OCSPFreeSession;
569 ft->createFcn = OCSPCreate;
570 ft->setPostDataFcn = OCSPSetPostData;
571 ft->addHeaderFcn = OCSPAddHeader;
572 ft->trySendAndReceiveFcn = OCSPTrySendAndReceive;
573 ft->cancelFcn = NULL;
574 ft->freeFcn = OCSPFree;
575 SECStatus status = SEC_RegisterDefaultHttpClient(&client_fcn_);
576 if (status != SECSuccess) {
577 NOTREACHED() << "Error initializing OCSP: " << PR_GetError();
580 // Work around NSS bugs 524013 and 564334. NSS incorrectly thinks the
581 // CRLs for Network Solutions Certificate Authority have bad signatures,
582 // which causes certificates issued by that CA to be reported as revoked.
583 // By using OCSP for those certificates, which don't have AIA extensions,
584 // we can work around these bugs. See http://crbug.com/41730.
585 CERT_StringFromCertFcn old_callback = NULL;
586 status = CERT_RegisterAlternateOCSPAIAInfoCallBack(
587 GetAlternateOCSPAIAInfo, &old_callback);
588 if (status == SECSuccess) {
589 DCHECK(!old_callback);
590 } else {
591 NOTREACHED() << "Error initializing OCSP: " << PR_GetError();
595 OCSPNSSInitialization::~OCSPNSSInitialization() {
596 SECStatus status = CERT_RegisterAlternateOCSPAIAInfoCallBack(NULL, NULL);
597 if (status != SECSuccess) {
598 LOG(ERROR) << "Error unregistering OCSP: " << PR_GetError();
603 // OCSP Http Client functions.
604 // Our Http Client functions operate in blocking mode.
605 SECStatus OCSPCreateSession(const char* host, PRUint16 portnum,
606 SEC_HTTP_SERVER_SESSION* pSession) {
607 VLOG(1) << "OCSP create session: host=" << host << " port=" << portnum;
608 pthread_mutex_lock(&g_request_context_lock);
609 URLRequestContext* request_context = g_request_context;
610 pthread_mutex_unlock(&g_request_context_lock);
611 if (request_context == NULL) {
612 LOG(ERROR) << "No URLRequestContext for NSS HTTP handler. host: " << host;
613 // The application failed to call SetURLRequestContextForNSSHttpIO or
614 // has already called ShutdownNSSHttpIO, so we can't create and use
615 // URLRequest. PR_NOT_IMPLEMENTED_ERROR is not an accurate error
616 // code for these error conditions, but is close enough.
617 PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
618 return SECFailure;
620 *pSession = new OCSPServerSession(host, portnum);
621 return SECSuccess;
624 SECStatus OCSPKeepAliveSession(SEC_HTTP_SERVER_SESSION session,
625 PRPollDesc **pPollDesc) {
626 VLOG(1) << "OCSP keep alive";
627 if (pPollDesc)
628 *pPollDesc = NULL;
629 return SECSuccess;
632 SECStatus OCSPFreeSession(SEC_HTTP_SERVER_SESSION session) {
633 VLOG(1) << "OCSP free session";
634 delete reinterpret_cast<OCSPServerSession*>(session);
635 return SECSuccess;
638 SECStatus OCSPCreate(SEC_HTTP_SERVER_SESSION session,
639 const char* http_protocol_variant,
640 const char* path_and_query_string,
641 const char* http_request_method,
642 const PRIntervalTime timeout,
643 SEC_HTTP_REQUEST_SESSION* pRequest) {
644 VLOG(1) << "OCSP create protocol=" << http_protocol_variant
645 << " path_and_query=" << path_and_query_string
646 << " http_request_method=" << http_request_method
647 << " timeout=" << timeout;
648 OCSPServerSession* ocsp_session =
649 reinterpret_cast<OCSPServerSession*>(session);
651 OCSPRequestSession* req = ocsp_session->CreateRequest(http_protocol_variant,
652 path_and_query_string,
653 http_request_method,
654 timeout);
655 SECStatus rv = SECFailure;
656 if (req) {
657 req->AddRef(); // Release in OCSPFree().
658 rv = SECSuccess;
660 *pRequest = req;
661 return rv;
664 SECStatus OCSPSetPostData(SEC_HTTP_REQUEST_SESSION request,
665 const char* http_data,
666 const PRUint32 http_data_len,
667 const char* http_content_type) {
668 VLOG(1) << "OCSP set post data len=" << http_data_len;
669 OCSPRequestSession* req = reinterpret_cast<OCSPRequestSession*>(request);
671 req->SetPostData(http_data, http_data_len, http_content_type);
672 return SECSuccess;
675 SECStatus OCSPAddHeader(SEC_HTTP_REQUEST_SESSION request,
676 const char* http_header_name,
677 const char* http_header_value) {
678 VLOG(1) << "OCSP add header name=" << http_header_name
679 << " value=" << http_header_value;
680 OCSPRequestSession* req = reinterpret_cast<OCSPRequestSession*>(request);
682 req->AddHeader(http_header_name, http_header_value);
683 return SECSuccess;
686 // Sets response of |req| in the output parameters.
687 // It is helper routine for OCSP trySendAndReceiveFcn.
688 // |http_response_data_len| could be used as input parameter. If it has
689 // non-zero value, it is considered as maximum size of |http_response_data|.
690 SECStatus OCSPSetResponse(OCSPRequestSession* req,
691 PRUint16* http_response_code,
692 const char** http_response_content_type,
693 const char** http_response_headers,
694 const char** http_response_data,
695 PRUint32* http_response_data_len) {
696 DCHECK(req->Finished());
697 const std::string& data = req->http_response_data();
698 if (http_response_data_len && *http_response_data_len) {
699 if (*http_response_data_len < data.size()) {
700 LOG(ERROR) << "response body too large: " << *http_response_data_len
701 << " < " << data.size();
702 *http_response_data_len = data.size();
703 PORT_SetError(SEC_ERROR_BAD_HTTP_RESPONSE);
704 return SECFailure;
707 VLOG(1) << "OCSP response "
708 << " response_code=" << req->http_response_code()
709 << " content_type=" << req->http_response_content_type()
710 << " header=" << req->http_response_headers()
711 << " data_len=" << data.size();
712 if (http_response_code)
713 *http_response_code = req->http_response_code();
714 if (http_response_content_type)
715 *http_response_content_type = req->http_response_content_type().c_str();
716 if (http_response_headers)
717 *http_response_headers = req->http_response_headers().c_str();
718 if (http_response_data)
719 *http_response_data = data.data();
720 if (http_response_data_len)
721 *http_response_data_len = data.size();
722 return SECSuccess;
725 SECStatus OCSPTrySendAndReceive(SEC_HTTP_REQUEST_SESSION request,
726 PRPollDesc** pPollDesc,
727 PRUint16* http_response_code,
728 const char** http_response_content_type,
729 const char** http_response_headers,
730 const char** http_response_data,
731 PRUint32* http_response_data_len) {
732 if (http_response_data_len) {
733 // We must always set an output value, even on failure. The output value 0
734 // means the failure was unrelated to the acceptable response data length.
735 *http_response_data_len = 0;
738 VLOG(1) << "OCSP try send and receive";
739 OCSPRequestSession* req = reinterpret_cast<OCSPRequestSession*>(request);
740 // We support blocking mode only.
741 if (pPollDesc)
742 *pPollDesc = NULL;
744 if (req->Started() || req->Finished()) {
745 // We support blocking mode only, so this function shouldn't be called
746 // again when req has stareted or finished.
747 NOTREACHED();
748 PORT_SetError(SEC_ERROR_BAD_HTTP_RESPONSE); // Simple approximation.
749 return SECFailure;
752 const base::Time start_time = base::Time::Now();
753 bool request_ok = true;
754 req->Start();
755 if (!req->Wait() || req->http_response_code() == static_cast<PRUint16>(-1)) {
756 // If the response code is -1, the request failed and there is no response.
757 request_ok = false;
759 const base::TimeDelta duration = base::Time::Now() - start_time;
761 // For metrics, we want to know if the request was 'successful' or not.
762 // |request_ok| determines if we'll pass the response back to NSS and |ok|
763 // keep track of if we think the response was good.
764 bool ok = true;
765 if (!request_ok ||
766 (req->http_response_code() >= 400 && req->http_response_code() < 600) ||
767 req->http_response_data().size() == 0 ||
768 // 0x30 is the ASN.1 DER encoding of a SEQUENCE. All valid OCSP/CRL/CRT
769 // responses must start with this. If we didn't check for this then a
770 // captive portal could provide an HTML reply that we would count as a
771 // 'success' (although it wouldn't count in NSS, of course).
772 req->http_response_data().data()[0] != 0x30) {
773 ok = false;
776 // We want to know if this was:
777 // 1) An OCSP request
778 // 2) A CRL request
779 // 3) A request for a missing intermediate certificate
780 // There's no sure way to do this, so we use heuristics like MIME type and
781 // URL.
782 const char* mime_type = "";
783 if (ok)
784 mime_type = req->http_response_content_type().c_str();
785 bool is_ocsp =
786 strcasecmp(mime_type, "application/ocsp-response") == 0;
787 bool is_crl = strcasecmp(mime_type, "application/x-pkcs7-crl") == 0 ||
788 strcasecmp(mime_type, "application/x-x509-crl") == 0 ||
789 strcasecmp(mime_type, "application/pkix-crl") == 0;
790 bool is_cert =
791 strcasecmp(mime_type, "application/x-x509-ca-cert") == 0 ||
792 strcasecmp(mime_type, "application/x-x509-server-cert") == 0 ||
793 strcasecmp(mime_type, "application/pkix-cert") == 0 ||
794 strcasecmp(mime_type, "application/pkcs7-mime") == 0;
796 if (!is_cert && !is_crl && !is_ocsp) {
797 // We didn't get a hint from the MIME type, so do the best that we can.
798 const std::string path = req->url().path();
799 const std::string host = req->url().host();
800 is_crl = strcasestr(path.c_str(), ".crl") != NULL;
801 is_cert = strcasestr(path.c_str(), ".crt") != NULL ||
802 strcasestr(path.c_str(), ".p7c") != NULL ||
803 strcasestr(path.c_str(), ".cer") != NULL;
804 is_ocsp = strcasestr(host.c_str(), "ocsp") != NULL ||
805 req->http_request_method() == "POST";
808 if (is_ocsp) {
809 if (ok) {
810 UMA_HISTOGRAM_TIMES("Net.OCSPRequestTimeMs", duration);
811 UMA_HISTOGRAM_BOOLEAN("Net.OCSPRequestSuccess", true);
812 } else {
813 UMA_HISTOGRAM_TIMES("Net.OCSPRequestFailedTimeMs", duration);
814 UMA_HISTOGRAM_BOOLEAN("Net.OCSPRequestSuccess", false);
816 } else if (is_crl) {
817 if (ok) {
818 UMA_HISTOGRAM_TIMES("Net.CRLRequestTimeMs", duration);
819 UMA_HISTOGRAM_BOOLEAN("Net.CRLRequestSuccess", true);
820 } else {
821 UMA_HISTOGRAM_TIMES("Net.CRLRequestFailedTimeMs", duration);
822 UMA_HISTOGRAM_BOOLEAN("Net.CRLRequestSuccess", false);
824 } else if (is_cert) {
825 if (ok)
826 UMA_HISTOGRAM_TIMES("Net.CRTRequestTimeMs", duration);
827 } else {
828 if (ok)
829 UMA_HISTOGRAM_TIMES("Net.UnknownTypeRequestTimeMs", duration);
832 if (!request_ok) {
833 PORT_SetError(SEC_ERROR_BAD_HTTP_RESPONSE); // Simple approximation.
834 return SECFailure;
837 return OCSPSetResponse(
838 req, http_response_code,
839 http_response_content_type,
840 http_response_headers,
841 http_response_data,
842 http_response_data_len);
845 SECStatus OCSPFree(SEC_HTTP_REQUEST_SESSION request) {
846 VLOG(1) << "OCSP free";
847 OCSPRequestSession* req = reinterpret_cast<OCSPRequestSession*>(request);
848 req->Cancel();
849 req->Release();
850 return SECSuccess;
853 // Data for GetAlternateOCSPAIAInfo.
855 // CN=Network Solutions Certificate Authority,O=Network Solutions L.L.C.,C=US
857 // There are two CAs with this name. Their key IDs are listed next.
858 const unsigned char network_solutions_ca_name[] = {
859 0x30, 0x62, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04,
860 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x21, 0x30, 0x1f, 0x06,
861 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x4e, 0x65, 0x74, 0x77,
862 0x6f, 0x72, 0x6b, 0x20, 0x53, 0x6f, 0x6c, 0x75, 0x74, 0x69,
863 0x6f, 0x6e, 0x73, 0x20, 0x4c, 0x2e, 0x4c, 0x2e, 0x43, 0x2e,
864 0x31, 0x30, 0x30, 0x2e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
865 0x27, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x53,
866 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x43,
867 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65,
868 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79
870 const unsigned int network_solutions_ca_name_len = 100;
872 // This CA is an intermediate CA, subordinate to UTN-USERFirst-Hardware.
873 const unsigned char network_solutions_ca_key_id[] = {
874 0x3c, 0x41, 0xe2, 0x8f, 0x08, 0x08, 0xa9, 0x4c, 0x25, 0x89,
875 0x8d, 0x6d, 0xc5, 0x38, 0xd0, 0xfc, 0x85, 0x8c, 0x62, 0x17
877 const unsigned int network_solutions_ca_key_id_len = 20;
879 // This CA is a root CA. It is also cross-certified by
880 // UTN-USERFirst-Hardware.
881 const unsigned char network_solutions_ca_key_id2[] = {
882 0x21, 0x30, 0xc9, 0xfb, 0x00, 0xd7, 0x4e, 0x98, 0xda, 0x87,
883 0xaa, 0x2a, 0xd0, 0xa7, 0x2e, 0xb1, 0x40, 0x31, 0xa7, 0x4c
885 const unsigned int network_solutions_ca_key_id2_len = 20;
887 // An entry in our OCSP responder table. |issuer| and |issuer_key_id| are
888 // the key. |ocsp_url| is the value.
889 struct OCSPResponderTableEntry {
890 SECItem issuer;
891 SECItem issuer_key_id;
892 const char *ocsp_url;
895 const OCSPResponderTableEntry g_ocsp_responder_table[] = {
898 siBuffer,
899 const_cast<unsigned char*>(network_solutions_ca_name),
900 network_solutions_ca_name_len
903 siBuffer,
904 const_cast<unsigned char*>(network_solutions_ca_key_id),
905 network_solutions_ca_key_id_len
907 "http://ocsp.netsolssl.com"
911 siBuffer,
912 const_cast<unsigned char*>(network_solutions_ca_name),
913 network_solutions_ca_name_len
916 siBuffer,
917 const_cast<unsigned char*>(network_solutions_ca_key_id2),
918 network_solutions_ca_key_id2_len
920 "http://ocsp.netsolssl.com"
924 char* GetAlternateOCSPAIAInfo(CERTCertificate *cert) {
925 if (cert && !cert->isRoot && cert->authKeyID) {
926 for (unsigned int i=0; i < arraysize(g_ocsp_responder_table); i++) {
927 if (SECITEM_CompareItem(&g_ocsp_responder_table[i].issuer,
928 &cert->derIssuer) == SECEqual &&
929 SECITEM_CompareItem(&g_ocsp_responder_table[i].issuer_key_id,
930 &cert->authKeyID->keyID) == SECEqual) {
931 return PORT_Strdup(g_ocsp_responder_table[i].ocsp_url);
936 return NULL;
939 } // anonymous namespace
941 void SetMessageLoopForNSSHttpIO() {
942 // Must have a MessageLoopForIO.
943 DCHECK(base::MessageLoopForIO::current());
945 bool used = g_ocsp_io_loop.Get().used();
947 // Should not be called when g_ocsp_io_loop has already been used.
948 DCHECK(!used);
951 void EnsureNSSHttpIOInit() {
952 g_ocsp_io_loop.Get().StartUsing();
953 g_ocsp_nss_initialization.Get();
956 void ShutdownNSSHttpIO() {
957 g_ocsp_io_loop.Get().Shutdown();
960 void ResetNSSHttpIOForTesting() {
961 g_ocsp_io_loop.Get().ReuseForTesting();
964 // This function would be called before NSS initialization.
965 void SetURLRequestContextForNSSHttpIO(URLRequestContext* request_context) {
966 pthread_mutex_lock(&g_request_context_lock);
967 if (request_context) {
968 DCHECK(!g_request_context);
970 g_request_context = request_context;
971 pthread_mutex_unlock(&g_request_context_lock);
974 } // namespace net