1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "net/cert_net/nss_ocsp.h"
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"
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
;
63 base::AutoLock
autolock(lock_
);
65 io_loop_
= base::MessageLoopForIO::current();
73 base::AutoLock
autolock(lock_
);
77 // Called from worker thread.
78 void PostTaskToIOLoop(const tracked_objects::Location
& from_here
,
79 const base::Closure
& task
);
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());
104 friend struct base::DefaultLazyInstanceTraits
<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
{
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
181 class OCSPRequestSession
182 : public base::RefCountedThreadSafe
<OCSPRequestSession
>,
183 public URLRequest::Delegate
{
185 OCSPRequestSession(const GURL
& url
,
186 const char* http_request_method
,
187 base::TimeDelta timeout
)
189 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_
.get() != 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 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().
299 void OnResponseStarted(URLRequest
* request
) override
{
300 DCHECK_EQ(request_
.get(), 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_
.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_
);
318 if (!request_
->status().is_success() || bytes_read
<= 0)
320 data_
.append(buffer_
->data(), bytes_read
);
321 } while (request_
->Read(buffer_
.get(), kRecvBufferSize
, &bytes_read
));
323 if (!request_
->status().is_io_pending()) {
325 g_ocsp_io_loop
.Get().RemoveRequest(this);
327 base::AutoLock
autolock(lock_
);
332 Release(); // Balanced with StartURLRequest().
336 // Must be called on the IO loop thread.
337 void CancelURLRequest() {
340 base::AutoLock
autolock(lock_
);
342 DCHECK_EQ(base::MessageLoopForIO::current(), io_loop_
);
347 g_ocsp_io_loop
.Get().RemoveRequest(this);
349 base::AutoLock
autolock(lock_
);
354 Release(); // Balanced with StartURLRequest().
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
369 // Must call this method while holding |lock_|.
370 void CancelLocked() {
371 lock_
.AssertAcquired();
375 base::Bind(&OCSPRequestSession::CancelURLRequest
, this));
379 // Runs on |g_ocsp_io_loop|'s IO loop.
380 void StartURLRequest() {
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
)
391 base::AutoLock
autolock(lock_
);
393 io_loop_
= base::MessageLoopForIO::current();
394 g_ocsp_io_loop
.Get().AddRequest(this);
397 request_
= url_request_context
->CreateRequest(url_
, DEFAULT_PRIORITY
, this);
398 // To meet the privacy requirements of incognito mode.
399 request_
->SetLoadFlags(LOAD_DISABLE_CACHE
| LOAD_DO_NOT_SAVE_COOKIES
|
400 LOAD_DO_NOT_SEND_COOKIES
);
402 if (http_request_method_
== "POST") {
403 DCHECK(!upload_content_
.empty());
404 DCHECK(!upload_content_type_
.empty());
406 request_
->set_method("POST");
407 extra_request_headers_
.SetHeader(
408 HttpRequestHeaders::kContentType
, upload_content_type_
);
410 scoped_ptr
<UploadElementReader
> reader(new UploadBytesElementReader(
411 upload_content_
.data(), upload_content_
.size()));
412 request_
->set_upload(
413 ElementsUploadDataStream::CreateWithReader(reader
.Pass(), 0));
415 if (!extra_request_headers_
.IsEmpty())
416 request_
->SetExtraRequestHeaders(extra_request_headers_
);
419 AddRef(); // Release after |request_| deleted.
422 GURL url_
; // The URL we eventually wound up at
423 std::string http_request_method_
;
424 base::TimeDelta timeout_
; // The timeout for OCSP
425 scoped_ptr
<URLRequest
> request_
; // The actual request this wraps
426 scoped_refptr
<IOBuffer
> buffer_
; // Read buffer
427 HttpRequestHeaders extra_request_headers_
;
429 // HTTP POST payload. |request_| reads bytes from this.
430 std::string upload_content_
;
431 std::string upload_content_type_
; // MIME type of POST payload
433 int response_code_
; // HTTP status code for the request
434 std::string response_content_type_
;
435 scoped_refptr
<HttpResponseHeaders
> response_headers_
;
436 std::string data_
; // Results of the request
438 // |lock_| protects |finished_| and |io_loop_|.
439 mutable base::Lock lock_
;
440 base::ConditionVariable cv_
;
442 base::MessageLoop
* io_loop_
; // Message loop of the IO thread
445 DISALLOW_COPY_AND_ASSIGN(OCSPRequestSession
);
448 // Concrete class for SEC_HTTP_SERVER_SESSION.
449 class OCSPServerSession
{
451 OCSPServerSession(const char* host
, PRUint16 port
)
452 : host_and_port_(host
, port
) {}
453 ~OCSPServerSession() {}
455 OCSPRequestSession
* CreateRequest(const char* http_protocol_variant
,
456 const char* path_and_query_string
,
457 const char* http_request_method
,
458 const PRIntervalTime timeout
) {
459 // We dont' support "https" because we haven't thought about
460 // whether it's safe to re-enter this code from talking to an OCSP
461 // responder over SSL.
462 if (strcmp(http_protocol_variant
, "http") != 0) {
463 PORT_SetError(PR_NOT_IMPLEMENTED_ERROR
);
467 std::string
url_string(base::StringPrintf(
469 http_protocol_variant
,
470 host_and_port_
.ToString().c_str(),
471 path_and_query_string
));
472 VLOG(1) << "URL [" << url_string
<< "]";
473 GURL
url(url_string
);
475 // NSS does not expose public functions to adjust the fetch timeout when
476 // using libpkix, so hardcode the upper limit for network fetches.
477 base::TimeDelta actual_timeout
= std::min(
478 base::TimeDelta::FromSeconds(kNetworkFetchTimeoutInSecs
),
479 base::TimeDelta::FromMilliseconds(PR_IntervalToMilliseconds(timeout
)));
481 return new OCSPRequestSession(url
, http_request_method
, actual_timeout
);
486 HostPortPair host_and_port_
;
488 DISALLOW_COPY_AND_ASSIGN(OCSPServerSession
);
491 OCSPIOLoop::OCSPIOLoop()
497 OCSPIOLoop::~OCSPIOLoop() {
498 // IO thread was already deleted before the singleton is deleted
501 base::AutoLock
autolock(lock_
);
507 pthread_mutex_lock(&g_request_context_lock
);
508 DCHECK(!g_request_context
);
509 pthread_mutex_unlock(&g_request_context_lock
);
512 void OCSPIOLoop::Shutdown() {
513 // Safe to read outside lock since we only write on IO thread anyway.
514 DCHECK(thread_checker_
.CalledOnValidThread());
516 // Prevent the worker thread from trying to access |io_loop_|.
518 base::AutoLock
autolock(lock_
);
526 pthread_mutex_lock(&g_request_context_lock
);
527 g_request_context
= NULL
;
528 pthread_mutex_unlock(&g_request_context_lock
);
531 void OCSPIOLoop::PostTaskToIOLoop(
532 const tracked_objects::Location
& from_here
, const base::Closure
& task
) {
533 base::AutoLock
autolock(lock_
);
535 io_loop_
->PostTask(from_here
, task
);
538 void OCSPIOLoop::EnsureIOLoop() {
539 base::AutoLock
autolock(lock_
);
540 DCHECK_EQ(base::MessageLoopForIO::current(), io_loop_
);
543 void OCSPIOLoop::AddRequest(OCSPRequestSession
* request
) {
544 DCHECK(!ContainsKey(requests_
, request
));
545 requests_
.insert(request
);
548 void OCSPIOLoop::RemoveRequest(OCSPRequestSession
* request
) {
549 DCHECK(ContainsKey(requests_
, request
));
550 requests_
.erase(request
);
553 void OCSPIOLoop::CancelAllRequests() {
554 // CancelURLRequest() always removes the request from the requests_
555 // set synchronously.
556 while (!requests_
.empty())
557 (*requests_
.begin())->CancelURLRequest();
560 OCSPNSSInitialization::OCSPNSSInitialization() {
561 // NSS calls the functions in the function table to download certificates
562 // or CRLs or talk to OCSP responders over HTTP. These functions must
563 // set an NSS/NSPR error code when they fail. Otherwise NSS will get the
564 // residual error code from an earlier failed function call.
565 client_fcn_
.version
= 1;
566 SEC_HttpClientFcnV1Struct
*ft
= &client_fcn_
.fcnTable
.ftable1
;
567 ft
->createSessionFcn
= OCSPCreateSession
;
568 ft
->keepAliveSessionFcn
= OCSPKeepAliveSession
;
569 ft
->freeSessionFcn
= OCSPFreeSession
;
570 ft
->createFcn
= OCSPCreate
;
571 ft
->setPostDataFcn
= OCSPSetPostData
;
572 ft
->addHeaderFcn
= OCSPAddHeader
;
573 ft
->trySendAndReceiveFcn
= OCSPTrySendAndReceive
;
574 ft
->cancelFcn
= NULL
;
575 ft
->freeFcn
= OCSPFree
;
576 SECStatus status
= SEC_RegisterDefaultHttpClient(&client_fcn_
);
577 if (status
!= SECSuccess
) {
578 NOTREACHED() << "Error initializing OCSP: " << PR_GetError();
581 // Work around NSS bugs 524013 and 564334. NSS incorrectly thinks the
582 // CRLs for Network Solutions Certificate Authority have bad signatures,
583 // which causes certificates issued by that CA to be reported as revoked.
584 // By using OCSP for those certificates, which don't have AIA extensions,
585 // we can work around these bugs. See http://crbug.com/41730.
586 CERT_StringFromCertFcn old_callback
= NULL
;
587 status
= CERT_RegisterAlternateOCSPAIAInfoCallBack(
588 GetAlternateOCSPAIAInfo
, &old_callback
);
589 if (status
== SECSuccess
) {
590 DCHECK(!old_callback
);
592 NOTREACHED() << "Error initializing OCSP: " << PR_GetError();
596 OCSPNSSInitialization::~OCSPNSSInitialization() {
597 SECStatus status
= CERT_RegisterAlternateOCSPAIAInfoCallBack(NULL
, NULL
);
598 if (status
!= SECSuccess
) {
599 LOG(ERROR
) << "Error unregistering OCSP: " << PR_GetError();
604 // OCSP Http Client functions.
605 // Our Http Client functions operate in blocking mode.
606 SECStatus
OCSPCreateSession(const char* host
, PRUint16 portnum
,
607 SEC_HTTP_SERVER_SESSION
* pSession
) {
608 VLOG(1) << "OCSP create session: host=" << host
<< " port=" << portnum
;
609 pthread_mutex_lock(&g_request_context_lock
);
610 URLRequestContext
* request_context
= g_request_context
;
611 pthread_mutex_unlock(&g_request_context_lock
);
612 if (request_context
== NULL
) {
613 LOG(ERROR
) << "No URLRequestContext for NSS HTTP handler. host: " << host
;
614 // The application failed to call SetURLRequestContextForNSSHttpIO or
615 // has already called ShutdownNSSHttpIO, so we can't create and use
616 // URLRequest. PR_NOT_IMPLEMENTED_ERROR is not an accurate error
617 // code for these error conditions, but is close enough.
618 PORT_SetError(PR_NOT_IMPLEMENTED_ERROR
);
621 *pSession
= new OCSPServerSession(host
, portnum
);
625 SECStatus
OCSPKeepAliveSession(SEC_HTTP_SERVER_SESSION session
,
626 PRPollDesc
**pPollDesc
) {
627 VLOG(1) << "OCSP keep alive";
633 SECStatus
OCSPFreeSession(SEC_HTTP_SERVER_SESSION session
) {
634 VLOG(1) << "OCSP free session";
635 delete reinterpret_cast<OCSPServerSession
*>(session
);
639 SECStatus
OCSPCreate(SEC_HTTP_SERVER_SESSION session
,
640 const char* http_protocol_variant
,
641 const char* path_and_query_string
,
642 const char* http_request_method
,
643 const PRIntervalTime timeout
,
644 SEC_HTTP_REQUEST_SESSION
* pRequest
) {
645 VLOG(1) << "OCSP create protocol=" << http_protocol_variant
646 << " path_and_query=" << path_and_query_string
647 << " http_request_method=" << http_request_method
648 << " timeout=" << timeout
;
649 OCSPServerSession
* ocsp_session
=
650 reinterpret_cast<OCSPServerSession
*>(session
);
652 OCSPRequestSession
* req
= ocsp_session
->CreateRequest(http_protocol_variant
,
653 path_and_query_string
,
656 SECStatus rv
= SECFailure
;
658 req
->AddRef(); // Release in OCSPFree().
665 SECStatus
OCSPSetPostData(SEC_HTTP_REQUEST_SESSION request
,
666 const char* http_data
,
667 const PRUint32 http_data_len
,
668 const char* http_content_type
) {
669 VLOG(1) << "OCSP set post data len=" << http_data_len
;
670 OCSPRequestSession
* req
= reinterpret_cast<OCSPRequestSession
*>(request
);
672 req
->SetPostData(http_data
, http_data_len
, http_content_type
);
676 SECStatus
OCSPAddHeader(SEC_HTTP_REQUEST_SESSION request
,
677 const char* http_header_name
,
678 const char* http_header_value
) {
679 VLOG(1) << "OCSP add header name=" << http_header_name
680 << " value=" << http_header_value
;
681 OCSPRequestSession
* req
= reinterpret_cast<OCSPRequestSession
*>(request
);
683 req
->AddHeader(http_header_name
, http_header_value
);
687 // Sets response of |req| in the output parameters.
688 // It is helper routine for OCSP trySendAndReceiveFcn.
689 // |http_response_data_len| could be used as input parameter. If it has
690 // non-zero value, it is considered as maximum size of |http_response_data|.
691 SECStatus
OCSPSetResponse(OCSPRequestSession
* req
,
692 PRUint16
* http_response_code
,
693 const char** http_response_content_type
,
694 const char** http_response_headers
,
695 const char** http_response_data
,
696 PRUint32
* http_response_data_len
) {
697 DCHECK(req
->Finished());
698 const std::string
& data
= req
->http_response_data();
699 if (http_response_data_len
&& *http_response_data_len
) {
700 if (*http_response_data_len
< data
.size()) {
701 LOG(ERROR
) << "response body too large: " << *http_response_data_len
702 << " < " << data
.size();
703 *http_response_data_len
= data
.size();
704 PORT_SetError(SEC_ERROR_BAD_HTTP_RESPONSE
);
708 VLOG(1) << "OCSP response "
709 << " response_code=" << req
->http_response_code()
710 << " content_type=" << req
->http_response_content_type()
711 << " header=" << req
->http_response_headers()
712 << " data_len=" << data
.size();
713 if (http_response_code
)
714 *http_response_code
= req
->http_response_code();
715 if (http_response_content_type
)
716 *http_response_content_type
= req
->http_response_content_type().c_str();
717 if (http_response_headers
)
718 *http_response_headers
= req
->http_response_headers().c_str();
719 if (http_response_data
)
720 *http_response_data
= data
.data();
721 if (http_response_data_len
)
722 *http_response_data_len
= data
.size();
726 SECStatus
OCSPTrySendAndReceive(SEC_HTTP_REQUEST_SESSION request
,
727 PRPollDesc
** pPollDesc
,
728 PRUint16
* http_response_code
,
729 const char** http_response_content_type
,
730 const char** http_response_headers
,
731 const char** http_response_data
,
732 PRUint32
* http_response_data_len
) {
733 if (http_response_data_len
) {
734 // We must always set an output value, even on failure. The output value 0
735 // means the failure was unrelated to the acceptable response data length.
736 *http_response_data_len
= 0;
739 VLOG(1) << "OCSP try send and receive";
740 OCSPRequestSession
* req
= reinterpret_cast<OCSPRequestSession
*>(request
);
741 // We support blocking mode only.
745 if (req
->Started() || req
->Finished()) {
746 // We support blocking mode only, so this function shouldn't be called
747 // again when req has stareted or finished.
749 PORT_SetError(SEC_ERROR_BAD_HTTP_RESPONSE
); // Simple approximation.
753 const base::Time start_time
= base::Time::Now();
754 bool request_ok
= true;
756 if (!req
->Wait() || req
->http_response_code() == static_cast<PRUint16
>(-1)) {
757 // If the response code is -1, the request failed and there is no response.
760 const base::TimeDelta duration
= base::Time::Now() - start_time
;
762 // For metrics, we want to know if the request was 'successful' or not.
763 // |request_ok| determines if we'll pass the response back to NSS and |ok|
764 // keep track of if we think the response was good.
767 (req
->http_response_code() >= 400 && req
->http_response_code() < 600) ||
768 req
->http_response_data().size() == 0 ||
769 // 0x30 is the ASN.1 DER encoding of a SEQUENCE. All valid OCSP/CRL/CRT
770 // responses must start with this. If we didn't check for this then a
771 // captive portal could provide an HTML reply that we would count as a
772 // 'success' (although it wouldn't count in NSS, of course).
773 req
->http_response_data().data()[0] != 0x30) {
777 // We want to know if this was:
778 // 1) An OCSP request
780 // 3) A request for a missing intermediate certificate
781 // There's no sure way to do this, so we use heuristics like MIME type and
783 const char* mime_type
= "";
785 mime_type
= req
->http_response_content_type().c_str();
787 strcasecmp(mime_type
, "application/ocsp-response") == 0;
788 bool is_crl
= strcasecmp(mime_type
, "application/x-pkcs7-crl") == 0 ||
789 strcasecmp(mime_type
, "application/x-x509-crl") == 0 ||
790 strcasecmp(mime_type
, "application/pkix-crl") == 0;
792 strcasecmp(mime_type
, "application/x-x509-ca-cert") == 0 ||
793 strcasecmp(mime_type
, "application/x-x509-server-cert") == 0 ||
794 strcasecmp(mime_type
, "application/pkix-cert") == 0 ||
795 strcasecmp(mime_type
, "application/pkcs7-mime") == 0;
797 if (!is_cert
&& !is_crl
&& !is_ocsp
) {
798 // We didn't get a hint from the MIME type, so do the best that we can.
799 const std::string path
= req
->url().path();
800 const std::string host
= req
->url().host();
801 is_crl
= strcasestr(path
.c_str(), ".crl") != NULL
;
802 is_cert
= strcasestr(path
.c_str(), ".crt") != NULL
||
803 strcasestr(path
.c_str(), ".p7c") != NULL
||
804 strcasestr(path
.c_str(), ".cer") != NULL
;
805 is_ocsp
= strcasestr(host
.c_str(), "ocsp") != NULL
||
806 req
->http_request_method() == "POST";
811 UMA_HISTOGRAM_TIMES("Net.OCSPRequestTimeMs", duration
);
812 UMA_HISTOGRAM_BOOLEAN("Net.OCSPRequestSuccess", true);
814 UMA_HISTOGRAM_TIMES("Net.OCSPRequestFailedTimeMs", duration
);
815 UMA_HISTOGRAM_BOOLEAN("Net.OCSPRequestSuccess", false);
819 UMA_HISTOGRAM_TIMES("Net.CRLRequestTimeMs", duration
);
820 UMA_HISTOGRAM_BOOLEAN("Net.CRLRequestSuccess", true);
822 UMA_HISTOGRAM_TIMES("Net.CRLRequestFailedTimeMs", duration
);
823 UMA_HISTOGRAM_BOOLEAN("Net.CRLRequestSuccess", false);
825 } else if (is_cert
) {
827 UMA_HISTOGRAM_TIMES("Net.CRTRequestTimeMs", duration
);
830 UMA_HISTOGRAM_TIMES("Net.UnknownTypeRequestTimeMs", duration
);
834 PORT_SetError(SEC_ERROR_BAD_HTTP_RESPONSE
); // Simple approximation.
838 return OCSPSetResponse(
839 req
, http_response_code
,
840 http_response_content_type
,
841 http_response_headers
,
843 http_response_data_len
);
846 SECStatus
OCSPFree(SEC_HTTP_REQUEST_SESSION request
) {
847 VLOG(1) << "OCSP free";
848 OCSPRequestSession
* req
= reinterpret_cast<OCSPRequestSession
*>(request
);
854 // Data for GetAlternateOCSPAIAInfo.
856 // CN=Network Solutions Certificate Authority,O=Network Solutions L.L.C.,C=US
858 // There are two CAs with this name. Their key IDs are listed next.
859 const unsigned char network_solutions_ca_name
[] = {
860 0x30, 0x62, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04,
861 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x21, 0x30, 0x1f, 0x06,
862 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x4e, 0x65, 0x74, 0x77,
863 0x6f, 0x72, 0x6b, 0x20, 0x53, 0x6f, 0x6c, 0x75, 0x74, 0x69,
864 0x6f, 0x6e, 0x73, 0x20, 0x4c, 0x2e, 0x4c, 0x2e, 0x43, 0x2e,
865 0x31, 0x30, 0x30, 0x2e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
866 0x27, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x53,
867 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x43,
868 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65,
869 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79
871 const unsigned int network_solutions_ca_name_len
= 100;
873 // This CA is an intermediate CA, subordinate to UTN-USERFirst-Hardware.
874 const unsigned char network_solutions_ca_key_id
[] = {
875 0x3c, 0x41, 0xe2, 0x8f, 0x08, 0x08, 0xa9, 0x4c, 0x25, 0x89,
876 0x8d, 0x6d, 0xc5, 0x38, 0xd0, 0xfc, 0x85, 0x8c, 0x62, 0x17
878 const unsigned int network_solutions_ca_key_id_len
= 20;
880 // This CA is a root CA. It is also cross-certified by
881 // UTN-USERFirst-Hardware.
882 const unsigned char network_solutions_ca_key_id2
[] = {
883 0x21, 0x30, 0xc9, 0xfb, 0x00, 0xd7, 0x4e, 0x98, 0xda, 0x87,
884 0xaa, 0x2a, 0xd0, 0xa7, 0x2e, 0xb1, 0x40, 0x31, 0xa7, 0x4c
886 const unsigned int network_solutions_ca_key_id2_len
= 20;
888 // An entry in our OCSP responder table. |issuer| and |issuer_key_id| are
889 // the key. |ocsp_url| is the value.
890 struct OCSPResponderTableEntry
{
892 SECItem issuer_key_id
;
893 const char *ocsp_url
;
896 const OCSPResponderTableEntry g_ocsp_responder_table
[] = {
900 const_cast<unsigned char*>(network_solutions_ca_name
),
901 network_solutions_ca_name_len
905 const_cast<unsigned char*>(network_solutions_ca_key_id
),
906 network_solutions_ca_key_id_len
908 "http://ocsp.netsolssl.com"
913 const_cast<unsigned char*>(network_solutions_ca_name
),
914 network_solutions_ca_name_len
918 const_cast<unsigned char*>(network_solutions_ca_key_id2
),
919 network_solutions_ca_key_id2_len
921 "http://ocsp.netsolssl.com"
925 char* GetAlternateOCSPAIAInfo(CERTCertificate
*cert
) {
926 if (cert
&& !cert
->isRoot
&& cert
->authKeyID
) {
927 for (unsigned int i
=0; i
< arraysize(g_ocsp_responder_table
); i
++) {
928 if (SECITEM_CompareItem(&g_ocsp_responder_table
[i
].issuer
,
929 &cert
->derIssuer
) == SECEqual
&&
930 SECITEM_CompareItem(&g_ocsp_responder_table
[i
].issuer_key_id
,
931 &cert
->authKeyID
->keyID
) == SECEqual
) {
932 return PORT_Strdup(g_ocsp_responder_table
[i
].ocsp_url
);
940 } // anonymous namespace
942 void SetMessageLoopForNSSHttpIO() {
943 // Must have a MessageLoopForIO.
944 DCHECK(base::MessageLoopForIO::current());
946 bool used
= g_ocsp_io_loop
.Get().used();
948 // Should not be called when g_ocsp_io_loop has already been used.
952 void EnsureNSSHttpIOInit() {
953 g_ocsp_io_loop
.Get().StartUsing();
954 g_ocsp_nss_initialization
.Get();
957 void ShutdownNSSHttpIO() {
958 g_ocsp_io_loop
.Get().Shutdown();
961 void ResetNSSHttpIOForTesting() {
962 g_ocsp_io_loop
.Get().ReuseForTesting();
965 // This function would be called before NSS initialization.
966 void SetURLRequestContextForNSSHttpIO(URLRequestContext
* request_context
) {
967 pthread_mutex_lock(&g_request_context_lock
);
968 if (request_context
) {
969 DCHECK(!g_request_context
);
971 g_request_context
= request_context
;
972 pthread_mutex_unlock(&g_request_context_lock
);