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/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"
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
;
60 base::AutoLock
autolock(lock_
);
62 io_loop_
= base::MessageLoopForIO::current();
70 base::AutoLock
autolock(lock_
);
74 // Called from worker thread.
75 void PostTaskToIOLoop(const tracked_objects::Location
& from_here
,
76 const base::Closure
& task
);
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();
98 friend struct base::DefaultLazyInstanceTraits
<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
{
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
175 class OCSPRequestSession
176 : public base::RefCountedThreadSafe
<OCSPRequestSession
>,
177 public URLRequest::Delegate
{
179 OCSPRequestSession(const GURL
& url
,
180 const char* http_request_method
,
181 base::TimeDelta timeout
)
183 http_request_method_(http_request_method
),
186 buffer_(new IOBuffer(kRecvBufferSize
)),
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.
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
,
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.
210 g_ocsp_io_loop
.Get().PostTaskToIOLoop(
212 base::Bind(&OCSPRequestSession::StartURLRequest
, this));
215 bool Started() const {
216 return request_
!= NULL
;
220 // IO thread may set |io_loop_| to NULL, so protect by |lock_|.
221 base::AutoLock
autolock(lock_
);
225 bool Finished() const {
226 base::AutoLock
autolock(lock_
);
231 base::TimeDelta timeout
= timeout_
;
232 base::AutoLock
autolock(lock_
);
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";
249 const GURL
& url() const {
253 const std::string
& http_request_method() const {
254 return http_request_method_
;
257 base::TimeDelta
timeout() const {
261 PRUint16
http_response_code() const {
263 return response_code_
;
266 const std::string
& http_response_content_type() const {
268 return response_content_type_
;
271 const std::string
& http_response_headers() const {
273 return response_headers_
->raw_headers();
276 const std::string
& http_response_data() const {
281 virtual void OnReceivedRedirect(URLRequest
* request
,
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().
294 virtual void OnResponseStarted(URLRequest
* request
) OVERRIDE
{
295 DCHECK_EQ(request
, request_
);
296 DCHECK_EQ(base::MessageLoopForIO::current(), io_loop_
);
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_
);
314 if (!request_
->status().is_success() || bytes_read
<= 0)
316 data_
.append(buffer_
->data(), bytes_read
);
317 } while (request_
->Read(buffer_
.get(), kRecvBufferSize
, &bytes_read
));
319 if (!request_
->status().is_io_pending()) {
322 g_ocsp_io_loop
.Get().RemoveRequest(this);
324 base::AutoLock
autolock(lock_
);
329 Release(); // Balanced with StartURLRequest().
333 // Must be called on the IO loop thread.
334 void CancelURLRequest() {
337 base::AutoLock
autolock(lock_
);
339 DCHECK_EQ(base::MessageLoopForIO::current(), io_loop_
);
346 g_ocsp_io_loop
.Get().RemoveRequest(this);
348 base::AutoLock
autolock(lock_
);
353 Release(); // Balanced with StartURLRequest().
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
368 // Must call this method while holding |lock_|.
369 void CancelLocked() {
370 lock_
.AssertAcquired();
374 base::Bind(&OCSPRequestSession::CancelURLRequest
, this));
378 // Runs on |g_ocsp_io_loop|'s IO loop.
379 void StartURLRequest() {
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
)
390 base::AutoLock
autolock(lock_
);
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_
);
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
444 DISALLOW_COPY_AND_ASSIGN(OCSPRequestSession
);
447 // Concrete class for SEC_HTTP_SERVER_SESSION.
448 class OCSPServerSession
{
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
);
466 std::string
url_string(base::StringPrintf(
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
);
485 HostPortPair host_and_port_
;
487 DISALLOW_COPY_AND_ASSIGN(OCSPServerSession
);
490 OCSPIOLoop::OCSPIOLoop()
496 OCSPIOLoop::~OCSPIOLoop() {
497 // IO thread was already deleted before the singleton is deleted
500 base::AutoLock
autolock(lock_
);
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_
);
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_
);
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
);
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
);
620 *pSession
= new OCSPServerSession(host
, portnum
);
624 SECStatus
OCSPKeepAliveSession(SEC_HTTP_SERVER_SESSION session
,
625 PRPollDesc
**pPollDesc
) {
626 VLOG(1) << "OCSP keep alive";
632 SECStatus
OCSPFreeSession(SEC_HTTP_SERVER_SESSION session
) {
633 VLOG(1) << "OCSP free session";
634 delete reinterpret_cast<OCSPServerSession
*>(session
);
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
,
655 SECStatus rv
= SECFailure
;
657 req
->AddRef(); // Release in OCSPFree().
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
);
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
);
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
);
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();
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.
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.
748 PORT_SetError(SEC_ERROR_BAD_HTTP_RESPONSE
); // Simple approximation.
752 const base::Time start_time
= base::Time::Now();
753 bool request_ok
= true;
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.
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.
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) {
776 // We want to know if this was:
777 // 1) An OCSP 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
782 const char* mime_type
= "";
784 mime_type
= req
->http_response_content_type().c_str();
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;
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";
810 UMA_HISTOGRAM_TIMES("Net.OCSPRequestTimeMs", duration
);
811 UMA_HISTOGRAM_BOOLEAN("Net.OCSPRequestSuccess", true);
813 UMA_HISTOGRAM_TIMES("Net.OCSPRequestFailedTimeMs", duration
);
814 UMA_HISTOGRAM_BOOLEAN("Net.OCSPRequestSuccess", false);
818 UMA_HISTOGRAM_TIMES("Net.CRLRequestTimeMs", duration
);
819 UMA_HISTOGRAM_BOOLEAN("Net.CRLRequestSuccess", true);
821 UMA_HISTOGRAM_TIMES("Net.CRLRequestFailedTimeMs", duration
);
822 UMA_HISTOGRAM_BOOLEAN("Net.CRLRequestSuccess", false);
824 } else if (is_cert
) {
826 UMA_HISTOGRAM_TIMES("Net.CRTRequestTimeMs", duration
);
829 UMA_HISTOGRAM_TIMES("Net.UnknownTypeRequestTimeMs", duration
);
833 PORT_SetError(SEC_ERROR_BAD_HTTP_RESPONSE
); // Simple approximation.
837 return OCSPSetResponse(
838 req
, http_response_code
,
839 http_response_content_type
,
840 http_response_headers
,
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
);
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
{
891 SECItem issuer_key_id
;
892 const char *ocsp_url
;
895 const OCSPResponderTableEntry g_ocsp_responder_table
[] = {
899 const_cast<unsigned char*>(network_solutions_ca_name
),
900 network_solutions_ca_name_len
904 const_cast<unsigned char*>(network_solutions_ca_key_id
),
905 network_solutions_ca_key_id_len
907 "http://ocsp.netsolssl.com"
912 const_cast<unsigned char*>(network_solutions_ca_name
),
913 network_solutions_ca_name_len
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
);
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.
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
);