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/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(
398 url_
, DEFAULT_PRIORITY
, this, NULL
);
399 // To meet the privacy requirements of incognito mode.
400 request_
->SetLoadFlags(LOAD_DISABLE_CACHE
| LOAD_DO_NOT_SAVE_COOKIES
|
401 LOAD_DO_NOT_SEND_COOKIES
);
403 if (http_request_method_
== "POST") {
404 DCHECK(!upload_content_
.empty());
405 DCHECK(!upload_content_type_
.empty());
407 request_
->set_method("POST");
408 extra_request_headers_
.SetHeader(
409 HttpRequestHeaders::kContentType
, upload_content_type_
);
411 scoped_ptr
<UploadElementReader
> reader(new UploadBytesElementReader(
412 upload_content_
.data(), upload_content_
.size()));
413 request_
->set_upload(
414 ElementsUploadDataStream::CreateWithReader(reader
.Pass(), 0));
416 if (!extra_request_headers_
.IsEmpty())
417 request_
->SetExtraRequestHeaders(extra_request_headers_
);
420 AddRef(); // Release after |request_| deleted.
423 GURL url_
; // The URL we eventually wound up at
424 std::string http_request_method_
;
425 base::TimeDelta timeout_
; // The timeout for OCSP
426 scoped_ptr
<URLRequest
> request_
; // The actual request this wraps
427 scoped_refptr
<IOBuffer
> buffer_
; // Read buffer
428 HttpRequestHeaders extra_request_headers_
;
430 // HTTP POST payload. |request_| reads bytes from this.
431 std::string upload_content_
;
432 std::string upload_content_type_
; // MIME type of POST payload
434 int response_code_
; // HTTP status code for the request
435 std::string response_content_type_
;
436 scoped_refptr
<HttpResponseHeaders
> response_headers_
;
437 std::string data_
; // Results of the request
439 // |lock_| protects |finished_| and |io_loop_|.
440 mutable base::Lock lock_
;
441 base::ConditionVariable cv_
;
443 base::MessageLoop
* io_loop_
; // Message loop of the IO thread
446 DISALLOW_COPY_AND_ASSIGN(OCSPRequestSession
);
449 // Concrete class for SEC_HTTP_SERVER_SESSION.
450 class OCSPServerSession
{
452 OCSPServerSession(const char* host
, PRUint16 port
)
453 : host_and_port_(host
, port
) {}
454 ~OCSPServerSession() {}
456 OCSPRequestSession
* CreateRequest(const char* http_protocol_variant
,
457 const char* path_and_query_string
,
458 const char* http_request_method
,
459 const PRIntervalTime timeout
) {
460 // We dont' support "https" because we haven't thought about
461 // whether it's safe to re-enter this code from talking to an OCSP
462 // responder over SSL.
463 if (strcmp(http_protocol_variant
, "http") != 0) {
464 PORT_SetError(PR_NOT_IMPLEMENTED_ERROR
);
468 std::string
url_string(base::StringPrintf(
470 http_protocol_variant
,
471 host_and_port_
.ToString().c_str(),
472 path_and_query_string
));
473 VLOG(1) << "URL [" << url_string
<< "]";
474 GURL
url(url_string
);
476 // NSS does not expose public functions to adjust the fetch timeout when
477 // using libpkix, so hardcode the upper limit for network fetches.
478 base::TimeDelta actual_timeout
= std::min(
479 base::TimeDelta::FromSeconds(kNetworkFetchTimeoutInSecs
),
480 base::TimeDelta::FromMilliseconds(PR_IntervalToMilliseconds(timeout
)));
482 return new OCSPRequestSession(url
, http_request_method
, actual_timeout
);
487 HostPortPair host_and_port_
;
489 DISALLOW_COPY_AND_ASSIGN(OCSPServerSession
);
492 OCSPIOLoop::OCSPIOLoop()
498 OCSPIOLoop::~OCSPIOLoop() {
499 // IO thread was already deleted before the singleton is deleted
502 base::AutoLock
autolock(lock_
);
508 pthread_mutex_lock(&g_request_context_lock
);
509 DCHECK(!g_request_context
);
510 pthread_mutex_unlock(&g_request_context_lock
);
513 void OCSPIOLoop::Shutdown() {
514 // Safe to read outside lock since we only write on IO thread anyway.
515 DCHECK(thread_checker_
.CalledOnValidThread());
517 // Prevent the worker thread from trying to access |io_loop_|.
519 base::AutoLock
autolock(lock_
);
527 pthread_mutex_lock(&g_request_context_lock
);
528 g_request_context
= NULL
;
529 pthread_mutex_unlock(&g_request_context_lock
);
532 void OCSPIOLoop::PostTaskToIOLoop(
533 const tracked_objects::Location
& from_here
, const base::Closure
& task
) {
534 base::AutoLock
autolock(lock_
);
536 io_loop_
->PostTask(from_here
, task
);
539 void OCSPIOLoop::EnsureIOLoop() {
540 base::AutoLock
autolock(lock_
);
541 DCHECK_EQ(base::MessageLoopForIO::current(), io_loop_
);
544 void OCSPIOLoop::AddRequest(OCSPRequestSession
* request
) {
545 DCHECK(!ContainsKey(requests_
, request
));
546 requests_
.insert(request
);
549 void OCSPIOLoop::RemoveRequest(OCSPRequestSession
* request
) {
550 DCHECK(ContainsKey(requests_
, request
));
551 requests_
.erase(request
);
554 void OCSPIOLoop::CancelAllRequests() {
555 // CancelURLRequest() always removes the request from the requests_
556 // set synchronously.
557 while (!requests_
.empty())
558 (*requests_
.begin())->CancelURLRequest();
561 OCSPNSSInitialization::OCSPNSSInitialization() {
562 // NSS calls the functions in the function table to download certificates
563 // or CRLs or talk to OCSP responders over HTTP. These functions must
564 // set an NSS/NSPR error code when they fail. Otherwise NSS will get the
565 // residual error code from an earlier failed function call.
566 client_fcn_
.version
= 1;
567 SEC_HttpClientFcnV1Struct
*ft
= &client_fcn_
.fcnTable
.ftable1
;
568 ft
->createSessionFcn
= OCSPCreateSession
;
569 ft
->keepAliveSessionFcn
= OCSPKeepAliveSession
;
570 ft
->freeSessionFcn
= OCSPFreeSession
;
571 ft
->createFcn
= OCSPCreate
;
572 ft
->setPostDataFcn
= OCSPSetPostData
;
573 ft
->addHeaderFcn
= OCSPAddHeader
;
574 ft
->trySendAndReceiveFcn
= OCSPTrySendAndReceive
;
575 ft
->cancelFcn
= NULL
;
576 ft
->freeFcn
= OCSPFree
;
577 SECStatus status
= SEC_RegisterDefaultHttpClient(&client_fcn_
);
578 if (status
!= SECSuccess
) {
579 NOTREACHED() << "Error initializing OCSP: " << PR_GetError();
582 // Work around NSS bugs 524013 and 564334. NSS incorrectly thinks the
583 // CRLs for Network Solutions Certificate Authority have bad signatures,
584 // which causes certificates issued by that CA to be reported as revoked.
585 // By using OCSP for those certificates, which don't have AIA extensions,
586 // we can work around these bugs. See http://crbug.com/41730.
587 CERT_StringFromCertFcn old_callback
= NULL
;
588 status
= CERT_RegisterAlternateOCSPAIAInfoCallBack(
589 GetAlternateOCSPAIAInfo
, &old_callback
);
590 if (status
== SECSuccess
) {
591 DCHECK(!old_callback
);
593 NOTREACHED() << "Error initializing OCSP: " << PR_GetError();
597 OCSPNSSInitialization::~OCSPNSSInitialization() {
598 SECStatus status
= CERT_RegisterAlternateOCSPAIAInfoCallBack(NULL
, NULL
);
599 if (status
!= SECSuccess
) {
600 LOG(ERROR
) << "Error unregistering OCSP: " << PR_GetError();
605 // OCSP Http Client functions.
606 // Our Http Client functions operate in blocking mode.
607 SECStatus
OCSPCreateSession(const char* host
, PRUint16 portnum
,
608 SEC_HTTP_SERVER_SESSION
* pSession
) {
609 VLOG(1) << "OCSP create session: host=" << host
<< " port=" << portnum
;
610 pthread_mutex_lock(&g_request_context_lock
);
611 URLRequestContext
* request_context
= g_request_context
;
612 pthread_mutex_unlock(&g_request_context_lock
);
613 if (request_context
== NULL
) {
614 LOG(ERROR
) << "No URLRequestContext for NSS HTTP handler. host: " << host
;
615 // The application failed to call SetURLRequestContextForNSSHttpIO or
616 // has already called ShutdownNSSHttpIO, so we can't create and use
617 // URLRequest. PR_NOT_IMPLEMENTED_ERROR is not an accurate error
618 // code for these error conditions, but is close enough.
619 PORT_SetError(PR_NOT_IMPLEMENTED_ERROR
);
622 *pSession
= new OCSPServerSession(host
, portnum
);
626 SECStatus
OCSPKeepAliveSession(SEC_HTTP_SERVER_SESSION session
,
627 PRPollDesc
**pPollDesc
) {
628 VLOG(1) << "OCSP keep alive";
634 SECStatus
OCSPFreeSession(SEC_HTTP_SERVER_SESSION session
) {
635 VLOG(1) << "OCSP free session";
636 delete reinterpret_cast<OCSPServerSession
*>(session
);
640 SECStatus
OCSPCreate(SEC_HTTP_SERVER_SESSION session
,
641 const char* http_protocol_variant
,
642 const char* path_and_query_string
,
643 const char* http_request_method
,
644 const PRIntervalTime timeout
,
645 SEC_HTTP_REQUEST_SESSION
* pRequest
) {
646 VLOG(1) << "OCSP create protocol=" << http_protocol_variant
647 << " path_and_query=" << path_and_query_string
648 << " http_request_method=" << http_request_method
649 << " timeout=" << timeout
;
650 OCSPServerSession
* ocsp_session
=
651 reinterpret_cast<OCSPServerSession
*>(session
);
653 OCSPRequestSession
* req
= ocsp_session
->CreateRequest(http_protocol_variant
,
654 path_and_query_string
,
657 SECStatus rv
= SECFailure
;
659 req
->AddRef(); // Release in OCSPFree().
666 SECStatus
OCSPSetPostData(SEC_HTTP_REQUEST_SESSION request
,
667 const char* http_data
,
668 const PRUint32 http_data_len
,
669 const char* http_content_type
) {
670 VLOG(1) << "OCSP set post data len=" << http_data_len
;
671 OCSPRequestSession
* req
= reinterpret_cast<OCSPRequestSession
*>(request
);
673 req
->SetPostData(http_data
, http_data_len
, http_content_type
);
677 SECStatus
OCSPAddHeader(SEC_HTTP_REQUEST_SESSION request
,
678 const char* http_header_name
,
679 const char* http_header_value
) {
680 VLOG(1) << "OCSP add header name=" << http_header_name
681 << " value=" << http_header_value
;
682 OCSPRequestSession
* req
= reinterpret_cast<OCSPRequestSession
*>(request
);
684 req
->AddHeader(http_header_name
, http_header_value
);
688 // Sets response of |req| in the output parameters.
689 // It is helper routine for OCSP trySendAndReceiveFcn.
690 // |http_response_data_len| could be used as input parameter. If it has
691 // non-zero value, it is considered as maximum size of |http_response_data|.
692 SECStatus
OCSPSetResponse(OCSPRequestSession
* req
,
693 PRUint16
* http_response_code
,
694 const char** http_response_content_type
,
695 const char** http_response_headers
,
696 const char** http_response_data
,
697 PRUint32
* http_response_data_len
) {
698 DCHECK(req
->Finished());
699 const std::string
& data
= req
->http_response_data();
700 if (http_response_data_len
&& *http_response_data_len
) {
701 if (*http_response_data_len
< data
.size()) {
702 LOG(ERROR
) << "response body too large: " << *http_response_data_len
703 << " < " << data
.size();
704 *http_response_data_len
= data
.size();
705 PORT_SetError(SEC_ERROR_BAD_HTTP_RESPONSE
);
709 VLOG(1) << "OCSP response "
710 << " response_code=" << req
->http_response_code()
711 << " content_type=" << req
->http_response_content_type()
712 << " header=" << req
->http_response_headers()
713 << " data_len=" << data
.size();
714 if (http_response_code
)
715 *http_response_code
= req
->http_response_code();
716 if (http_response_content_type
)
717 *http_response_content_type
= req
->http_response_content_type().c_str();
718 if (http_response_headers
)
719 *http_response_headers
= req
->http_response_headers().c_str();
720 if (http_response_data
)
721 *http_response_data
= data
.data();
722 if (http_response_data_len
)
723 *http_response_data_len
= data
.size();
727 SECStatus
OCSPTrySendAndReceive(SEC_HTTP_REQUEST_SESSION request
,
728 PRPollDesc
** pPollDesc
,
729 PRUint16
* http_response_code
,
730 const char** http_response_content_type
,
731 const char** http_response_headers
,
732 const char** http_response_data
,
733 PRUint32
* http_response_data_len
) {
734 if (http_response_data_len
) {
735 // We must always set an output value, even on failure. The output value 0
736 // means the failure was unrelated to the acceptable response data length.
737 *http_response_data_len
= 0;
740 VLOG(1) << "OCSP try send and receive";
741 OCSPRequestSession
* req
= reinterpret_cast<OCSPRequestSession
*>(request
);
742 // We support blocking mode only.
746 if (req
->Started() || req
->Finished()) {
747 // We support blocking mode only, so this function shouldn't be called
748 // again when req has stareted or finished.
750 PORT_SetError(SEC_ERROR_BAD_HTTP_RESPONSE
); // Simple approximation.
754 const base::Time start_time
= base::Time::Now();
755 bool request_ok
= true;
757 if (!req
->Wait() || req
->http_response_code() == static_cast<PRUint16
>(-1)) {
758 // If the response code is -1, the request failed and there is no response.
761 const base::TimeDelta duration
= base::Time::Now() - start_time
;
763 // For metrics, we want to know if the request was 'successful' or not.
764 // |request_ok| determines if we'll pass the response back to NSS and |ok|
765 // keep track of if we think the response was good.
768 (req
->http_response_code() >= 400 && req
->http_response_code() < 600) ||
769 req
->http_response_data().size() == 0 ||
770 // 0x30 is the ASN.1 DER encoding of a SEQUENCE. All valid OCSP/CRL/CRT
771 // responses must start with this. If we didn't check for this then a
772 // captive portal could provide an HTML reply that we would count as a
773 // 'success' (although it wouldn't count in NSS, of course).
774 req
->http_response_data().data()[0] != 0x30) {
778 // We want to know if this was:
779 // 1) An OCSP request
781 // 3) A request for a missing intermediate certificate
782 // There's no sure way to do this, so we use heuristics like MIME type and
784 const char* mime_type
= "";
786 mime_type
= req
->http_response_content_type().c_str();
788 strcasecmp(mime_type
, "application/ocsp-response") == 0;
789 bool is_crl
= strcasecmp(mime_type
, "application/x-pkcs7-crl") == 0 ||
790 strcasecmp(mime_type
, "application/x-x509-crl") == 0 ||
791 strcasecmp(mime_type
, "application/pkix-crl") == 0;
793 strcasecmp(mime_type
, "application/x-x509-ca-cert") == 0 ||
794 strcasecmp(mime_type
, "application/x-x509-server-cert") == 0 ||
795 strcasecmp(mime_type
, "application/pkix-cert") == 0 ||
796 strcasecmp(mime_type
, "application/pkcs7-mime") == 0;
798 if (!is_cert
&& !is_crl
&& !is_ocsp
) {
799 // We didn't get a hint from the MIME type, so do the best that we can.
800 const std::string path
= req
->url().path();
801 const std::string host
= req
->url().host();
802 is_crl
= strcasestr(path
.c_str(), ".crl") != NULL
;
803 is_cert
= strcasestr(path
.c_str(), ".crt") != NULL
||
804 strcasestr(path
.c_str(), ".p7c") != NULL
||
805 strcasestr(path
.c_str(), ".cer") != NULL
;
806 is_ocsp
= strcasestr(host
.c_str(), "ocsp") != NULL
||
807 req
->http_request_method() == "POST";
812 UMA_HISTOGRAM_TIMES("Net.OCSPRequestTimeMs", duration
);
813 UMA_HISTOGRAM_BOOLEAN("Net.OCSPRequestSuccess", true);
815 UMA_HISTOGRAM_TIMES("Net.OCSPRequestFailedTimeMs", duration
);
816 UMA_HISTOGRAM_BOOLEAN("Net.OCSPRequestSuccess", false);
820 UMA_HISTOGRAM_TIMES("Net.CRLRequestTimeMs", duration
);
821 UMA_HISTOGRAM_BOOLEAN("Net.CRLRequestSuccess", true);
823 UMA_HISTOGRAM_TIMES("Net.CRLRequestFailedTimeMs", duration
);
824 UMA_HISTOGRAM_BOOLEAN("Net.CRLRequestSuccess", false);
826 } else if (is_cert
) {
828 UMA_HISTOGRAM_TIMES("Net.CRTRequestTimeMs", duration
);
831 UMA_HISTOGRAM_TIMES("Net.UnknownTypeRequestTimeMs", duration
);
835 PORT_SetError(SEC_ERROR_BAD_HTTP_RESPONSE
); // Simple approximation.
839 return OCSPSetResponse(
840 req
, http_response_code
,
841 http_response_content_type
,
842 http_response_headers
,
844 http_response_data_len
);
847 SECStatus
OCSPFree(SEC_HTTP_REQUEST_SESSION request
) {
848 VLOG(1) << "OCSP free";
849 OCSPRequestSession
* req
= reinterpret_cast<OCSPRequestSession
*>(request
);
855 // Data for GetAlternateOCSPAIAInfo.
857 // CN=Network Solutions Certificate Authority,O=Network Solutions L.L.C.,C=US
859 // There are two CAs with this name. Their key IDs are listed next.
860 const unsigned char network_solutions_ca_name
[] = {
861 0x30, 0x62, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04,
862 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x21, 0x30, 0x1f, 0x06,
863 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x4e, 0x65, 0x74, 0x77,
864 0x6f, 0x72, 0x6b, 0x20, 0x53, 0x6f, 0x6c, 0x75, 0x74, 0x69,
865 0x6f, 0x6e, 0x73, 0x20, 0x4c, 0x2e, 0x4c, 0x2e, 0x43, 0x2e,
866 0x31, 0x30, 0x30, 0x2e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
867 0x27, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x53,
868 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x43,
869 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65,
870 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79
872 const unsigned int network_solutions_ca_name_len
= 100;
874 // This CA is an intermediate CA, subordinate to UTN-USERFirst-Hardware.
875 const unsigned char network_solutions_ca_key_id
[] = {
876 0x3c, 0x41, 0xe2, 0x8f, 0x08, 0x08, 0xa9, 0x4c, 0x25, 0x89,
877 0x8d, 0x6d, 0xc5, 0x38, 0xd0, 0xfc, 0x85, 0x8c, 0x62, 0x17
879 const unsigned int network_solutions_ca_key_id_len
= 20;
881 // This CA is a root CA. It is also cross-certified by
882 // UTN-USERFirst-Hardware.
883 const unsigned char network_solutions_ca_key_id2
[] = {
884 0x21, 0x30, 0xc9, 0xfb, 0x00, 0xd7, 0x4e, 0x98, 0xda, 0x87,
885 0xaa, 0x2a, 0xd0, 0xa7, 0x2e, 0xb1, 0x40, 0x31, 0xa7, 0x4c
887 const unsigned int network_solutions_ca_key_id2_len
= 20;
889 // An entry in our OCSP responder table. |issuer| and |issuer_key_id| are
890 // the key. |ocsp_url| is the value.
891 struct OCSPResponderTableEntry
{
893 SECItem issuer_key_id
;
894 const char *ocsp_url
;
897 const OCSPResponderTableEntry g_ocsp_responder_table
[] = {
901 const_cast<unsigned char*>(network_solutions_ca_name
),
902 network_solutions_ca_name_len
906 const_cast<unsigned char*>(network_solutions_ca_key_id
),
907 network_solutions_ca_key_id_len
909 "http://ocsp.netsolssl.com"
914 const_cast<unsigned char*>(network_solutions_ca_name
),
915 network_solutions_ca_name_len
919 const_cast<unsigned char*>(network_solutions_ca_key_id2
),
920 network_solutions_ca_key_id2_len
922 "http://ocsp.netsolssl.com"
926 char* GetAlternateOCSPAIAInfo(CERTCertificate
*cert
) {
927 if (cert
&& !cert
->isRoot
&& cert
->authKeyID
) {
928 for (unsigned int i
=0; i
< arraysize(g_ocsp_responder_table
); i
++) {
929 if (SECITEM_CompareItem(&g_ocsp_responder_table
[i
].issuer
,
930 &cert
->derIssuer
) == SECEqual
&&
931 SECITEM_CompareItem(&g_ocsp_responder_table
[i
].issuer_key_id
,
932 &cert
->authKeyID
->keyID
) == SECEqual
) {
933 return PORT_Strdup(g_ocsp_responder_table
[i
].ocsp_url
);
941 } // anonymous namespace
943 void SetMessageLoopForNSSHttpIO() {
944 // Must have a MessageLoopForIO.
945 DCHECK(base::MessageLoopForIO::current());
947 bool used
= g_ocsp_io_loop
.Get().used();
949 // Should not be called when g_ocsp_io_loop has already been used.
953 void EnsureNSSHttpIOInit() {
954 g_ocsp_io_loop
.Get().StartUsing();
955 g_ocsp_nss_initialization
.Get();
958 void ShutdownNSSHttpIO() {
959 g_ocsp_io_loop
.Get().Shutdown();
962 void ResetNSSHttpIOForTesting() {
963 g_ocsp_io_loop
.Get().ReuseForTesting();
966 // This function would be called before NSS initialization.
967 void SetURLRequestContextForNSSHttpIO(URLRequestContext
* request_context
) {
968 pthread_mutex_lock(&g_request_context_lock
);
969 if (request_context
) {
970 DCHECK(!g_request_context
);
972 g_request_context
= request_context
;
973 pthread_mutex_unlock(&g_request_context_lock
);