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"
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/request_priority.h"
36 #include "net/base/upload_bytes_element_reader.h"
37 #include "net/base/upload_data_stream.h"
38 #include "net/http/http_request_headers.h"
39 #include "net/http/http_response_headers.h"
40 #include "net/url_request/redirect_info.h"
41 #include "net/url_request/url_request.h"
42 #include "net/url_request/url_request_context.h"
49 // Protects |g_request_context|.
50 pthread_mutex_t g_request_context_lock
= PTHREAD_MUTEX_INITIALIZER
;
51 URLRequestContext
* g_request_context
= NULL
;
53 // The default timeout for network fetches in NSS is 60 seconds. Choose a
54 // saner upper limit for OCSP/CRL/AIA fetches.
55 const int kNetworkFetchTimeoutInSecs
= 15;
57 class OCSPRequestSession
;
62 base::AutoLock
autolock(lock_
);
64 io_loop_
= base::MessageLoopForIO::current();
72 base::AutoLock
autolock(lock_
);
76 // Called from worker thread.
77 void PostTaskToIOLoop(const tracked_objects::Location
& from_here
,
78 const base::Closure
& task
);
82 void AddRequest(OCSPRequestSession
* request
);
83 void RemoveRequest(OCSPRequestSession
* request
);
85 // Clears internal state and calls |StartUsing()|. Should be called only in
86 // the context of testing.
87 void ReuseForTesting() {
89 base::AutoLock
autolock(lock_
);
90 DCHECK(base::MessageLoopForIO::current());
91 thread_checker_
.DetachFromThread();
93 // CalledOnValidThread is the only available API to reassociate
94 // thread_checker_ with the current thread. Result ignored intentionally.
95 ignore_result(thread_checker_
.CalledOnValidThread());
103 friend struct base::DefaultLazyInstanceTraits
<OCSPIOLoop
>;
108 void CancelAllRequests();
110 mutable base::Lock lock_
;
111 bool shutdown_
; // Protected by |lock_|.
112 std::set
<OCSPRequestSession
*> requests_
; // Protected by |lock_|.
113 bool used_
; // Protected by |lock_|.
114 // This should not be modified after |used_|.
115 base::MessageLoopForIO
* io_loop_
; // Protected by |lock_|.
116 base::ThreadChecker thread_checker_
;
118 DISALLOW_COPY_AND_ASSIGN(OCSPIOLoop
);
121 base::LazyInstance
<OCSPIOLoop
>::Leaky
122 g_ocsp_io_loop
= LAZY_INSTANCE_INITIALIZER
;
124 const int kRecvBufferSize
= 4096;
126 // All OCSP handlers should be called in the context of
127 // CertVerifier's thread (i.e. worker pool, not on the I/O thread).
128 // It supports blocking mode only.
130 SECStatus
OCSPCreateSession(const char* host
, PRUint16 portnum
,
131 SEC_HTTP_SERVER_SESSION
* pSession
);
132 SECStatus
OCSPKeepAliveSession(SEC_HTTP_SERVER_SESSION session
,
133 PRPollDesc
**pPollDesc
);
134 SECStatus
OCSPFreeSession(SEC_HTTP_SERVER_SESSION session
);
136 SECStatus
OCSPCreate(SEC_HTTP_SERVER_SESSION session
,
137 const char* http_protocol_variant
,
138 const char* path_and_query_string
,
139 const char* http_request_method
,
140 const PRIntervalTime timeout
,
141 SEC_HTTP_REQUEST_SESSION
* pRequest
);
142 SECStatus
OCSPSetPostData(SEC_HTTP_REQUEST_SESSION request
,
143 const char* http_data
,
144 const PRUint32 http_data_len
,
145 const char* http_content_type
);
146 SECStatus
OCSPAddHeader(SEC_HTTP_REQUEST_SESSION request
,
147 const char* http_header_name
,
148 const char* http_header_value
);
149 SECStatus
OCSPTrySendAndReceive(SEC_HTTP_REQUEST_SESSION request
,
150 PRPollDesc
** pPollDesc
,
151 PRUint16
* http_response_code
,
152 const char** http_response_content_type
,
153 const char** http_response_headers
,
154 const char** http_response_data
,
155 PRUint32
* http_response_data_len
);
156 SECStatus
OCSPFree(SEC_HTTP_REQUEST_SESSION request
);
158 char* GetAlternateOCSPAIAInfo(CERTCertificate
*cert
);
160 class OCSPNSSInitialization
{
162 friend struct base::DefaultLazyInstanceTraits
<OCSPNSSInitialization
>;
164 OCSPNSSInitialization();
165 ~OCSPNSSInitialization();
167 SEC_HttpClientFcn client_fcn_
;
169 DISALLOW_COPY_AND_ASSIGN(OCSPNSSInitialization
);
172 base::LazyInstance
<OCSPNSSInitialization
> g_ocsp_nss_initialization
=
173 LAZY_INSTANCE_INITIALIZER
;
175 // Concrete class for SEC_HTTP_REQUEST_SESSION.
176 // Public methods except virtual methods of URLRequest::Delegate
177 // (On* methods) run on certificate verifier thread (worker thread).
178 // Virtual methods of URLRequest::Delegate and private methods run
180 class OCSPRequestSession
181 : public base::RefCountedThreadSafe
<OCSPRequestSession
>,
182 public URLRequest::Delegate
{
184 OCSPRequestSession(const GURL
& url
,
185 const char* http_request_method
,
186 base::TimeDelta timeout
)
188 http_request_method_(http_request_method
),
191 buffer_(new IOBuffer(kRecvBufferSize
)),
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.
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
,
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.
215 g_ocsp_io_loop
.Get().PostTaskToIOLoop(
217 base::Bind(&OCSPRequestSession::StartURLRequest
, this));
220 bool Started() const {
221 return request_
!= NULL
;
225 // IO thread may set |io_loop_| to NULL, so protect by |lock_|.
226 base::AutoLock
autolock(lock_
);
230 bool Finished() const {
231 base::AutoLock
autolock(lock_
);
236 base::TimeDelta timeout
= timeout_
;
237 base::AutoLock
autolock(lock_
);
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";
254 const GURL
& url() const {
258 const std::string
& http_request_method() const {
259 return http_request_method_
;
262 base::TimeDelta
timeout() const {
266 PRUint16
http_response_code() const {
268 return response_code_
;
271 const std::string
& http_response_content_type() const {
273 return response_content_type_
;
276 const std::string
& http_response_headers() const {
278 return response_headers_
->raw_headers();
281 const std::string
& http_response_data() const {
286 virtual void OnReceivedRedirect(URLRequest
* request
,
287 const RedirectInfo
& redirect_info
,
288 bool* defer_redirect
) OVERRIDE
{
289 DCHECK_EQ(request
, 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().
299 virtual void OnResponseStarted(URLRequest
* request
) OVERRIDE
{
300 DCHECK_EQ(request
, request_
);
301 DCHECK_EQ(base::MessageLoopForIO::current(), io_loop_
);
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_
, bytes_read
);
313 virtual void OnReadCompleted(URLRequest
* request
,
314 int bytes_read
) OVERRIDE
{
315 DCHECK_EQ(request
, request_
);
316 DCHECK_EQ(base::MessageLoopForIO::current(), io_loop_
);
319 if (!request_
->status().is_success() || bytes_read
<= 0)
321 data_
.append(buffer_
->data(), bytes_read
);
322 } while (request_
->Read(buffer_
.get(), kRecvBufferSize
, &bytes_read
));
324 if (!request_
->status().is_io_pending()) {
327 g_ocsp_io_loop
.Get().RemoveRequest(this);
329 base::AutoLock
autolock(lock_
);
334 Release(); // Balanced with StartURLRequest().
338 // Must be called on the IO loop thread.
339 void CancelURLRequest() {
342 base::AutoLock
autolock(lock_
);
344 DCHECK_EQ(base::MessageLoopForIO::current(), io_loop_
);
351 g_ocsp_io_loop
.Get().RemoveRequest(this);
353 base::AutoLock
autolock(lock_
);
358 Release(); // Balanced with StartURLRequest().
363 friend class base::RefCountedThreadSafe
<OCSPRequestSession
>;
365 virtual ~OCSPRequestSession() {
366 // When this destructor is called, there should be only one thread that has
367 // a reference to this object, and so that thread doesn't need to lock
373 // Must call this method while holding |lock_|.
374 void CancelLocked() {
375 lock_
.AssertAcquired();
379 base::Bind(&OCSPRequestSession::CancelURLRequest
, this));
383 // Runs on |g_ocsp_io_loop|'s IO loop.
384 void StartURLRequest() {
387 pthread_mutex_lock(&g_request_context_lock
);
388 URLRequestContext
* url_request_context
= g_request_context
;
389 pthread_mutex_unlock(&g_request_context_lock
);
391 if (url_request_context
== NULL
)
395 base::AutoLock
autolock(lock_
);
397 io_loop_
= base::MessageLoopForIO::current();
398 g_ocsp_io_loop
.Get().AddRequest(this);
402 new URLRequest(url_
, DEFAULT_PRIORITY
, this, url_request_context
);
403 // To meet the privacy requirements of incognito mode.
404 request_
->SetLoadFlags(LOAD_DISABLE_CACHE
| LOAD_DO_NOT_SAVE_COOKIES
|
405 LOAD_DO_NOT_SEND_COOKIES
);
407 if (http_request_method_
== "POST") {
408 DCHECK(!upload_content_
.empty());
409 DCHECK(!upload_content_type_
.empty());
411 request_
->set_method("POST");
412 extra_request_headers_
.SetHeader(
413 HttpRequestHeaders::kContentType
, upload_content_type_
);
415 scoped_ptr
<UploadElementReader
> reader(new UploadBytesElementReader(
416 upload_content_
.data(), upload_content_
.size()));
417 request_
->set_upload(make_scoped_ptr(
418 UploadDataStream::CreateWithReader(reader
.Pass(), 0)));
420 if (!extra_request_headers_
.IsEmpty())
421 request_
->SetExtraRequestHeaders(extra_request_headers_
);
424 AddRef(); // Release after |request_| deleted.
427 GURL url_
; // The URL we eventually wound up at
428 std::string http_request_method_
;
429 base::TimeDelta timeout_
; // The timeout for OCSP
430 URLRequest
* request_
; // The actual request this wraps
431 scoped_refptr
<IOBuffer
> buffer_
; // Read buffer
432 HttpRequestHeaders extra_request_headers_
;
434 // HTTP POST payload. |request_| reads bytes from this.
435 std::string upload_content_
;
436 std::string upload_content_type_
; // MIME type of POST payload
438 int response_code_
; // HTTP status code for the request
439 std::string response_content_type_
;
440 scoped_refptr
<HttpResponseHeaders
> response_headers_
;
441 std::string data_
; // Results of the request
443 // |lock_| protects |finished_| and |io_loop_|.
444 mutable base::Lock lock_
;
445 base::ConditionVariable cv_
;
447 base::MessageLoop
* io_loop_
; // Message loop of the IO thread
450 DISALLOW_COPY_AND_ASSIGN(OCSPRequestSession
);
453 // Concrete class for SEC_HTTP_SERVER_SESSION.
454 class OCSPServerSession
{
456 OCSPServerSession(const char* host
, PRUint16 port
)
457 : host_and_port_(host
, port
) {}
458 ~OCSPServerSession() {}
460 OCSPRequestSession
* CreateRequest(const char* http_protocol_variant
,
461 const char* path_and_query_string
,
462 const char* http_request_method
,
463 const PRIntervalTime timeout
) {
464 // We dont' support "https" because we haven't thought about
465 // whether it's safe to re-enter this code from talking to an OCSP
466 // responder over SSL.
467 if (strcmp(http_protocol_variant
, "http") != 0) {
468 PORT_SetError(PR_NOT_IMPLEMENTED_ERROR
);
472 std::string
url_string(base::StringPrintf(
474 http_protocol_variant
,
475 host_and_port_
.ToString().c_str(),
476 path_and_query_string
));
477 VLOG(1) << "URL [" << url_string
<< "]";
478 GURL
url(url_string
);
480 // NSS does not expose public functions to adjust the fetch timeout when
481 // using libpkix, so hardcode the upper limit for network fetches.
482 base::TimeDelta actual_timeout
= std::min(
483 base::TimeDelta::FromSeconds(kNetworkFetchTimeoutInSecs
),
484 base::TimeDelta::FromMilliseconds(PR_IntervalToMilliseconds(timeout
)));
486 return new OCSPRequestSession(url
, http_request_method
, actual_timeout
);
491 HostPortPair host_and_port_
;
493 DISALLOW_COPY_AND_ASSIGN(OCSPServerSession
);
496 OCSPIOLoop::OCSPIOLoop()
502 OCSPIOLoop::~OCSPIOLoop() {
503 // IO thread was already deleted before the singleton is deleted
506 base::AutoLock
autolock(lock_
);
512 pthread_mutex_lock(&g_request_context_lock
);
513 DCHECK(!g_request_context
);
514 pthread_mutex_unlock(&g_request_context_lock
);
517 void OCSPIOLoop::Shutdown() {
518 // Safe to read outside lock since we only write on IO thread anyway.
519 DCHECK(thread_checker_
.CalledOnValidThread());
521 // Prevent the worker thread from trying to access |io_loop_|.
523 base::AutoLock
autolock(lock_
);
531 pthread_mutex_lock(&g_request_context_lock
);
532 g_request_context
= NULL
;
533 pthread_mutex_unlock(&g_request_context_lock
);
536 void OCSPIOLoop::PostTaskToIOLoop(
537 const tracked_objects::Location
& from_here
, const base::Closure
& task
) {
538 base::AutoLock
autolock(lock_
);
540 io_loop_
->PostTask(from_here
, task
);
543 void OCSPIOLoop::EnsureIOLoop() {
544 base::AutoLock
autolock(lock_
);
545 DCHECK_EQ(base::MessageLoopForIO::current(), io_loop_
);
548 void OCSPIOLoop::AddRequest(OCSPRequestSession
* request
) {
549 DCHECK(!ContainsKey(requests_
, request
));
550 requests_
.insert(request
);
553 void OCSPIOLoop::RemoveRequest(OCSPRequestSession
* request
) {
554 DCHECK(ContainsKey(requests_
, request
));
555 requests_
.erase(request
);
558 void OCSPIOLoop::CancelAllRequests() {
559 // CancelURLRequest() always removes the request from the requests_
560 // set synchronously.
561 while (!requests_
.empty())
562 (*requests_
.begin())->CancelURLRequest();
565 OCSPNSSInitialization::OCSPNSSInitialization() {
566 // NSS calls the functions in the function table to download certificates
567 // or CRLs or talk to OCSP responders over HTTP. These functions must
568 // set an NSS/NSPR error code when they fail. Otherwise NSS will get the
569 // residual error code from an earlier failed function call.
570 client_fcn_
.version
= 1;
571 SEC_HttpClientFcnV1Struct
*ft
= &client_fcn_
.fcnTable
.ftable1
;
572 ft
->createSessionFcn
= OCSPCreateSession
;
573 ft
->keepAliveSessionFcn
= OCSPKeepAliveSession
;
574 ft
->freeSessionFcn
= OCSPFreeSession
;
575 ft
->createFcn
= OCSPCreate
;
576 ft
->setPostDataFcn
= OCSPSetPostData
;
577 ft
->addHeaderFcn
= OCSPAddHeader
;
578 ft
->trySendAndReceiveFcn
= OCSPTrySendAndReceive
;
579 ft
->cancelFcn
= NULL
;
580 ft
->freeFcn
= OCSPFree
;
581 SECStatus status
= SEC_RegisterDefaultHttpClient(&client_fcn_
);
582 if (status
!= SECSuccess
) {
583 NOTREACHED() << "Error initializing OCSP: " << PR_GetError();
586 // Work around NSS bugs 524013 and 564334. NSS incorrectly thinks the
587 // CRLs for Network Solutions Certificate Authority have bad signatures,
588 // which causes certificates issued by that CA to be reported as revoked.
589 // By using OCSP for those certificates, which don't have AIA extensions,
590 // we can work around these bugs. See http://crbug.com/41730.
591 CERT_StringFromCertFcn old_callback
= NULL
;
592 status
= CERT_RegisterAlternateOCSPAIAInfoCallBack(
593 GetAlternateOCSPAIAInfo
, &old_callback
);
594 if (status
== SECSuccess
) {
595 DCHECK(!old_callback
);
597 NOTREACHED() << "Error initializing OCSP: " << PR_GetError();
601 OCSPNSSInitialization::~OCSPNSSInitialization() {
602 SECStatus status
= CERT_RegisterAlternateOCSPAIAInfoCallBack(NULL
, NULL
);
603 if (status
!= SECSuccess
) {
604 LOG(ERROR
) << "Error unregistering OCSP: " << PR_GetError();
609 // OCSP Http Client functions.
610 // Our Http Client functions operate in blocking mode.
611 SECStatus
OCSPCreateSession(const char* host
, PRUint16 portnum
,
612 SEC_HTTP_SERVER_SESSION
* pSession
) {
613 VLOG(1) << "OCSP create session: host=" << host
<< " port=" << portnum
;
614 pthread_mutex_lock(&g_request_context_lock
);
615 URLRequestContext
* request_context
= g_request_context
;
616 pthread_mutex_unlock(&g_request_context_lock
);
617 if (request_context
== NULL
) {
618 LOG(ERROR
) << "No URLRequestContext for NSS HTTP handler. host: " << host
;
619 // The application failed to call SetURLRequestContextForNSSHttpIO or
620 // has already called ShutdownNSSHttpIO, so we can't create and use
621 // URLRequest. PR_NOT_IMPLEMENTED_ERROR is not an accurate error
622 // code for these error conditions, but is close enough.
623 PORT_SetError(PR_NOT_IMPLEMENTED_ERROR
);
626 *pSession
= new OCSPServerSession(host
, portnum
);
630 SECStatus
OCSPKeepAliveSession(SEC_HTTP_SERVER_SESSION session
,
631 PRPollDesc
**pPollDesc
) {
632 VLOG(1) << "OCSP keep alive";
638 SECStatus
OCSPFreeSession(SEC_HTTP_SERVER_SESSION session
) {
639 VLOG(1) << "OCSP free session";
640 delete reinterpret_cast<OCSPServerSession
*>(session
);
644 SECStatus
OCSPCreate(SEC_HTTP_SERVER_SESSION session
,
645 const char* http_protocol_variant
,
646 const char* path_and_query_string
,
647 const char* http_request_method
,
648 const PRIntervalTime timeout
,
649 SEC_HTTP_REQUEST_SESSION
* pRequest
) {
650 VLOG(1) << "OCSP create protocol=" << http_protocol_variant
651 << " path_and_query=" << path_and_query_string
652 << " http_request_method=" << http_request_method
653 << " timeout=" << timeout
;
654 OCSPServerSession
* ocsp_session
=
655 reinterpret_cast<OCSPServerSession
*>(session
);
657 OCSPRequestSession
* req
= ocsp_session
->CreateRequest(http_protocol_variant
,
658 path_and_query_string
,
661 SECStatus rv
= SECFailure
;
663 req
->AddRef(); // Release in OCSPFree().
670 SECStatus
OCSPSetPostData(SEC_HTTP_REQUEST_SESSION request
,
671 const char* http_data
,
672 const PRUint32 http_data_len
,
673 const char* http_content_type
) {
674 VLOG(1) << "OCSP set post data len=" << http_data_len
;
675 OCSPRequestSession
* req
= reinterpret_cast<OCSPRequestSession
*>(request
);
677 req
->SetPostData(http_data
, http_data_len
, http_content_type
);
681 SECStatus
OCSPAddHeader(SEC_HTTP_REQUEST_SESSION request
,
682 const char* http_header_name
,
683 const char* http_header_value
) {
684 VLOG(1) << "OCSP add header name=" << http_header_name
685 << " value=" << http_header_value
;
686 OCSPRequestSession
* req
= reinterpret_cast<OCSPRequestSession
*>(request
);
688 req
->AddHeader(http_header_name
, http_header_value
);
692 // Sets response of |req| in the output parameters.
693 // It is helper routine for OCSP trySendAndReceiveFcn.
694 // |http_response_data_len| could be used as input parameter. If it has
695 // non-zero value, it is considered as maximum size of |http_response_data|.
696 SECStatus
OCSPSetResponse(OCSPRequestSession
* req
,
697 PRUint16
* http_response_code
,
698 const char** http_response_content_type
,
699 const char** http_response_headers
,
700 const char** http_response_data
,
701 PRUint32
* http_response_data_len
) {
702 DCHECK(req
->Finished());
703 const std::string
& data
= req
->http_response_data();
704 if (http_response_data_len
&& *http_response_data_len
) {
705 if (*http_response_data_len
< data
.size()) {
706 LOG(ERROR
) << "response body too large: " << *http_response_data_len
707 << " < " << data
.size();
708 *http_response_data_len
= data
.size();
709 PORT_SetError(SEC_ERROR_BAD_HTTP_RESPONSE
);
713 VLOG(1) << "OCSP response "
714 << " response_code=" << req
->http_response_code()
715 << " content_type=" << req
->http_response_content_type()
716 << " header=" << req
->http_response_headers()
717 << " data_len=" << data
.size();
718 if (http_response_code
)
719 *http_response_code
= req
->http_response_code();
720 if (http_response_content_type
)
721 *http_response_content_type
= req
->http_response_content_type().c_str();
722 if (http_response_headers
)
723 *http_response_headers
= req
->http_response_headers().c_str();
724 if (http_response_data
)
725 *http_response_data
= data
.data();
726 if (http_response_data_len
)
727 *http_response_data_len
= data
.size();
731 SECStatus
OCSPTrySendAndReceive(SEC_HTTP_REQUEST_SESSION request
,
732 PRPollDesc
** pPollDesc
,
733 PRUint16
* http_response_code
,
734 const char** http_response_content_type
,
735 const char** http_response_headers
,
736 const char** http_response_data
,
737 PRUint32
* http_response_data_len
) {
738 if (http_response_data_len
) {
739 // We must always set an output value, even on failure. The output value 0
740 // means the failure was unrelated to the acceptable response data length.
741 *http_response_data_len
= 0;
744 VLOG(1) << "OCSP try send and receive";
745 OCSPRequestSession
* req
= reinterpret_cast<OCSPRequestSession
*>(request
);
746 // We support blocking mode only.
750 if (req
->Started() || req
->Finished()) {
751 // We support blocking mode only, so this function shouldn't be called
752 // again when req has stareted or finished.
754 PORT_SetError(SEC_ERROR_BAD_HTTP_RESPONSE
); // Simple approximation.
758 const base::Time start_time
= base::Time::Now();
759 bool request_ok
= true;
761 if (!req
->Wait() || req
->http_response_code() == static_cast<PRUint16
>(-1)) {
762 // If the response code is -1, the request failed and there is no response.
765 const base::TimeDelta duration
= base::Time::Now() - start_time
;
767 // For metrics, we want to know if the request was 'successful' or not.
768 // |request_ok| determines if we'll pass the response back to NSS and |ok|
769 // keep track of if we think the response was good.
772 (req
->http_response_code() >= 400 && req
->http_response_code() < 600) ||
773 req
->http_response_data().size() == 0 ||
774 // 0x30 is the ASN.1 DER encoding of a SEQUENCE. All valid OCSP/CRL/CRT
775 // responses must start with this. If we didn't check for this then a
776 // captive portal could provide an HTML reply that we would count as a
777 // 'success' (although it wouldn't count in NSS, of course).
778 req
->http_response_data().data()[0] != 0x30) {
782 // We want to know if this was:
783 // 1) An OCSP request
785 // 3) A request for a missing intermediate certificate
786 // There's no sure way to do this, so we use heuristics like MIME type and
788 const char* mime_type
= "";
790 mime_type
= req
->http_response_content_type().c_str();
792 strcasecmp(mime_type
, "application/ocsp-response") == 0;
793 bool is_crl
= strcasecmp(mime_type
, "application/x-pkcs7-crl") == 0 ||
794 strcasecmp(mime_type
, "application/x-x509-crl") == 0 ||
795 strcasecmp(mime_type
, "application/pkix-crl") == 0;
797 strcasecmp(mime_type
, "application/x-x509-ca-cert") == 0 ||
798 strcasecmp(mime_type
, "application/x-x509-server-cert") == 0 ||
799 strcasecmp(mime_type
, "application/pkix-cert") == 0 ||
800 strcasecmp(mime_type
, "application/pkcs7-mime") == 0;
802 if (!is_cert
&& !is_crl
&& !is_ocsp
) {
803 // We didn't get a hint from the MIME type, so do the best that we can.
804 const std::string path
= req
->url().path();
805 const std::string host
= req
->url().host();
806 is_crl
= strcasestr(path
.c_str(), ".crl") != NULL
;
807 is_cert
= strcasestr(path
.c_str(), ".crt") != NULL
||
808 strcasestr(path
.c_str(), ".p7c") != NULL
||
809 strcasestr(path
.c_str(), ".cer") != NULL
;
810 is_ocsp
= strcasestr(host
.c_str(), "ocsp") != NULL
||
811 req
->http_request_method() == "POST";
816 UMA_HISTOGRAM_TIMES("Net.OCSPRequestTimeMs", duration
);
817 UMA_HISTOGRAM_BOOLEAN("Net.OCSPRequestSuccess", true);
819 UMA_HISTOGRAM_TIMES("Net.OCSPRequestFailedTimeMs", duration
);
820 UMA_HISTOGRAM_BOOLEAN("Net.OCSPRequestSuccess", false);
824 UMA_HISTOGRAM_TIMES("Net.CRLRequestTimeMs", duration
);
825 UMA_HISTOGRAM_BOOLEAN("Net.CRLRequestSuccess", true);
827 UMA_HISTOGRAM_TIMES("Net.CRLRequestFailedTimeMs", duration
);
828 UMA_HISTOGRAM_BOOLEAN("Net.CRLRequestSuccess", false);
830 } else if (is_cert
) {
832 UMA_HISTOGRAM_TIMES("Net.CRTRequestTimeMs", duration
);
835 UMA_HISTOGRAM_TIMES("Net.UnknownTypeRequestTimeMs", duration
);
839 PORT_SetError(SEC_ERROR_BAD_HTTP_RESPONSE
); // Simple approximation.
843 return OCSPSetResponse(
844 req
, http_response_code
,
845 http_response_content_type
,
846 http_response_headers
,
848 http_response_data_len
);
851 SECStatus
OCSPFree(SEC_HTTP_REQUEST_SESSION request
) {
852 VLOG(1) << "OCSP free";
853 OCSPRequestSession
* req
= reinterpret_cast<OCSPRequestSession
*>(request
);
859 // Data for GetAlternateOCSPAIAInfo.
861 // CN=Network Solutions Certificate Authority,O=Network Solutions L.L.C.,C=US
863 // There are two CAs with this name. Their key IDs are listed next.
864 const unsigned char network_solutions_ca_name
[] = {
865 0x30, 0x62, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04,
866 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x21, 0x30, 0x1f, 0x06,
867 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x4e, 0x65, 0x74, 0x77,
868 0x6f, 0x72, 0x6b, 0x20, 0x53, 0x6f, 0x6c, 0x75, 0x74, 0x69,
869 0x6f, 0x6e, 0x73, 0x20, 0x4c, 0x2e, 0x4c, 0x2e, 0x43, 0x2e,
870 0x31, 0x30, 0x30, 0x2e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
871 0x27, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x53,
872 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x43,
873 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65,
874 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79
876 const unsigned int network_solutions_ca_name_len
= 100;
878 // This CA is an intermediate CA, subordinate to UTN-USERFirst-Hardware.
879 const unsigned char network_solutions_ca_key_id
[] = {
880 0x3c, 0x41, 0xe2, 0x8f, 0x08, 0x08, 0xa9, 0x4c, 0x25, 0x89,
881 0x8d, 0x6d, 0xc5, 0x38, 0xd0, 0xfc, 0x85, 0x8c, 0x62, 0x17
883 const unsigned int network_solutions_ca_key_id_len
= 20;
885 // This CA is a root CA. It is also cross-certified by
886 // UTN-USERFirst-Hardware.
887 const unsigned char network_solutions_ca_key_id2
[] = {
888 0x21, 0x30, 0xc9, 0xfb, 0x00, 0xd7, 0x4e, 0x98, 0xda, 0x87,
889 0xaa, 0x2a, 0xd0, 0xa7, 0x2e, 0xb1, 0x40, 0x31, 0xa7, 0x4c
891 const unsigned int network_solutions_ca_key_id2_len
= 20;
893 // An entry in our OCSP responder table. |issuer| and |issuer_key_id| are
894 // the key. |ocsp_url| is the value.
895 struct OCSPResponderTableEntry
{
897 SECItem issuer_key_id
;
898 const char *ocsp_url
;
901 const OCSPResponderTableEntry g_ocsp_responder_table
[] = {
905 const_cast<unsigned char*>(network_solutions_ca_name
),
906 network_solutions_ca_name_len
910 const_cast<unsigned char*>(network_solutions_ca_key_id
),
911 network_solutions_ca_key_id_len
913 "http://ocsp.netsolssl.com"
918 const_cast<unsigned char*>(network_solutions_ca_name
),
919 network_solutions_ca_name_len
923 const_cast<unsigned char*>(network_solutions_ca_key_id2
),
924 network_solutions_ca_key_id2_len
926 "http://ocsp.netsolssl.com"
930 char* GetAlternateOCSPAIAInfo(CERTCertificate
*cert
) {
931 if (cert
&& !cert
->isRoot
&& cert
->authKeyID
) {
932 for (unsigned int i
=0; i
< arraysize(g_ocsp_responder_table
); i
++) {
933 if (SECITEM_CompareItem(&g_ocsp_responder_table
[i
].issuer
,
934 &cert
->derIssuer
) == SECEqual
&&
935 SECITEM_CompareItem(&g_ocsp_responder_table
[i
].issuer_key_id
,
936 &cert
->authKeyID
->keyID
) == SECEqual
) {
937 return PORT_Strdup(g_ocsp_responder_table
[i
].ocsp_url
);
945 } // anonymous namespace
947 void SetMessageLoopForNSSHttpIO() {
948 // Must have a MessageLoopForIO.
949 DCHECK(base::MessageLoopForIO::current());
951 bool used
= g_ocsp_io_loop
.Get().used();
953 // Should not be called when g_ocsp_io_loop has already been used.
957 void EnsureNSSHttpIOInit() {
958 g_ocsp_io_loop
.Get().StartUsing();
959 g_ocsp_nss_initialization
.Get();
962 void ShutdownNSSHttpIO() {
963 g_ocsp_io_loop
.Get().Shutdown();
966 void ResetNSSHttpIOForTesting() {
967 g_ocsp_io_loop
.Get().ReuseForTesting();
970 // This function would be called before NSS initialization.
971 void SetURLRequestContextForNSSHttpIO(URLRequestContext
* request_context
) {
972 pthread_mutex_lock(&g_request_context_lock
);
973 if (request_context
) {
974 DCHECK(!g_request_context
);
976 g_request_context
= request_context
;
977 pthread_mutex_unlock(&g_request_context_lock
);