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/profiler/scoped_tracker.h"
27 #include "base/stl_util.h"
28 #include "base/strings/string_util.h"
29 #include "base/strings/stringprintf.h"
30 #include "base/synchronization/condition_variable.h"
31 #include "base/synchronization/lock.h"
32 #include "base/threading/thread_checker.h"
33 #include "base/time/time.h"
34 #include "net/base/elements_upload_data_stream.h"
35 #include "net/base/host_port_pair.h"
36 #include "net/base/io_buffer.h"
37 #include "net/base/load_flags.h"
38 #include "net/base/request_priority.h"
39 #include "net/base/upload_bytes_element_reader.h"
40 #include "net/http/http_request_headers.h"
41 #include "net/http/http_response_headers.h"
42 #include "net/url_request/redirect_info.h"
43 #include "net/url_request/url_request.h"
44 #include "net/url_request/url_request_context.h"
51 // Protects |g_request_context|.
52 pthread_mutex_t g_request_context_lock
= PTHREAD_MUTEX_INITIALIZER
;
53 URLRequestContext
* g_request_context
= NULL
;
55 // The default timeout for network fetches in NSS is 60 seconds. Choose a
56 // saner upper limit for OCSP/CRL/AIA fetches.
57 const int kNetworkFetchTimeoutInSecs
= 15;
59 class OCSPRequestSession
;
64 base::AutoLock
autolock(lock_
);
66 io_loop_
= base::MessageLoopForIO::current();
74 base::AutoLock
autolock(lock_
);
78 // Called from worker thread.
79 void PostTaskToIOLoop(const tracked_objects::Location
& from_here
,
80 const base::Closure
& task
);
84 void AddRequest(OCSPRequestSession
* request
);
85 void RemoveRequest(OCSPRequestSession
* request
);
87 // Clears internal state and calls |StartUsing()|. Should be called only in
88 // the context of testing.
89 void ReuseForTesting() {
91 base::AutoLock
autolock(lock_
);
92 DCHECK(base::MessageLoopForIO::current());
93 thread_checker_
.DetachFromThread();
95 // CalledOnValidThread is the only available API to reassociate
96 // thread_checker_ with the current thread. Result ignored intentionally.
97 ignore_result(thread_checker_
.CalledOnValidThread());
105 friend struct base::DefaultLazyInstanceTraits
<OCSPIOLoop
>;
110 void CancelAllRequests();
112 mutable base::Lock lock_
;
113 bool shutdown_
; // Protected by |lock_|.
114 std::set
<OCSPRequestSession
*> requests_
; // Protected by |lock_|.
115 bool used_
; // Protected by |lock_|.
116 // This should not be modified after |used_|.
117 base::MessageLoopForIO
* io_loop_
; // Protected by |lock_|.
118 base::ThreadChecker thread_checker_
;
120 DISALLOW_COPY_AND_ASSIGN(OCSPIOLoop
);
123 base::LazyInstance
<OCSPIOLoop
>::Leaky
124 g_ocsp_io_loop
= LAZY_INSTANCE_INITIALIZER
;
126 const int kRecvBufferSize
= 4096;
128 // All OCSP handlers should be called in the context of
129 // CertVerifier's thread (i.e. worker pool, not on the I/O thread).
130 // It supports blocking mode only.
132 SECStatus
OCSPCreateSession(const char* host
, PRUint16 portnum
,
133 SEC_HTTP_SERVER_SESSION
* pSession
);
134 SECStatus
OCSPKeepAliveSession(SEC_HTTP_SERVER_SESSION session
,
135 PRPollDesc
**pPollDesc
);
136 SECStatus
OCSPFreeSession(SEC_HTTP_SERVER_SESSION session
);
138 SECStatus
OCSPCreate(SEC_HTTP_SERVER_SESSION session
,
139 const char* http_protocol_variant
,
140 const char* path_and_query_string
,
141 const char* http_request_method
,
142 const PRIntervalTime timeout
,
143 SEC_HTTP_REQUEST_SESSION
* pRequest
);
144 SECStatus
OCSPSetPostData(SEC_HTTP_REQUEST_SESSION request
,
145 const char* http_data
,
146 const PRUint32 http_data_len
,
147 const char* http_content_type
);
148 SECStatus
OCSPAddHeader(SEC_HTTP_REQUEST_SESSION request
,
149 const char* http_header_name
,
150 const char* http_header_value
);
151 SECStatus
OCSPTrySendAndReceive(SEC_HTTP_REQUEST_SESSION request
,
152 PRPollDesc
** pPollDesc
,
153 PRUint16
* http_response_code
,
154 const char** http_response_content_type
,
155 const char** http_response_headers
,
156 const char** http_response_data
,
157 PRUint32
* http_response_data_len
);
158 SECStatus
OCSPFree(SEC_HTTP_REQUEST_SESSION request
);
160 char* GetAlternateOCSPAIAInfo(CERTCertificate
*cert
);
162 class OCSPNSSInitialization
{
164 friend struct base::DefaultLazyInstanceTraits
<OCSPNSSInitialization
>;
166 OCSPNSSInitialization();
167 ~OCSPNSSInitialization();
169 SEC_HttpClientFcn client_fcn_
;
171 DISALLOW_COPY_AND_ASSIGN(OCSPNSSInitialization
);
174 base::LazyInstance
<OCSPNSSInitialization
> g_ocsp_nss_initialization
=
175 LAZY_INSTANCE_INITIALIZER
;
177 // Concrete class for SEC_HTTP_REQUEST_SESSION.
178 // Public methods except virtual methods of URLRequest::Delegate
179 // (On* methods) run on certificate verifier thread (worker thread).
180 // Virtual methods of URLRequest::Delegate and private methods run
182 class OCSPRequestSession
183 : public base::RefCountedThreadSafe
<OCSPRequestSession
>,
184 public URLRequest::Delegate
{
186 OCSPRequestSession(const GURL
& url
,
187 const char* http_request_method
,
188 base::TimeDelta timeout
)
190 http_request_method_(http_request_method
),
192 buffer_(new IOBuffer(kRecvBufferSize
)),
198 void SetPostData(const char* http_data
, PRUint32 http_data_len
,
199 const char* http_content_type
) {
200 // |upload_content_| should not be modified if |request_| is active.
202 upload_content_
.assign(http_data
, http_data_len
);
203 upload_content_type_
.assign(http_content_type
);
206 void AddHeader(const char* http_header_name
, const char* http_header_value
) {
207 extra_request_headers_
.SetHeader(http_header_name
,
212 // At this point, it runs on worker thread.
213 // |io_loop_| was initialized to be NULL in constructor, and
214 // set only in StartURLRequest, so no need to lock |lock_| here.
216 g_ocsp_io_loop
.Get().PostTaskToIOLoop(
218 base::Bind(&OCSPRequestSession::StartURLRequest
, this));
221 bool Started() const {
222 return request_
.get() != NULL
;
226 // IO thread may set |io_loop_| to NULL, so protect by |lock_|.
227 base::AutoLock
autolock(lock_
);
231 bool Finished() const {
232 base::AutoLock
autolock(lock_
);
237 base::TimeDelta timeout
= timeout_
;
238 base::AutoLock
autolock(lock_
);
240 base::TimeTicks last_time
= base::TimeTicks::Now();
241 cv_
.TimedWait(timeout
);
242 // Check elapsed time
243 base::TimeDelta elapsed_time
= base::TimeTicks::Now() - last_time
;
244 timeout
-= elapsed_time
;
245 if (timeout
< base::TimeDelta()) {
246 VLOG(1) << "OCSP Timed out";
255 const GURL
& url() const {
259 const std::string
& http_request_method() const {
260 return http_request_method_
;
263 base::TimeDelta
timeout() const {
267 PRUint16
http_response_code() const {
269 return response_code_
;
272 const std::string
& http_response_content_type() const {
274 return response_content_type_
;
277 const std::string
& http_response_headers() const {
279 return response_headers_
->raw_headers();
282 const std::string
& http_response_data() const {
287 void OnReceivedRedirect(URLRequest
* request
,
288 const RedirectInfo
& redirect_info
,
289 bool* defer_redirect
) override
{
290 DCHECK_EQ(request_
.get(), request
);
291 DCHECK_EQ(base::MessageLoopForIO::current(), io_loop_
);
293 if (!redirect_info
.new_url
.SchemeIs("http")) {
294 // Prevent redirects to non-HTTP schemes, including HTTPS. This matches
295 // the initial check in OCSPServerSession::CreateRequest().
300 void OnResponseStarted(URLRequest
* request
) override
{
301 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed.
302 tracked_objects::ScopedTracker
tracking_profile(
303 FROM_HERE_WITH_EXPLICIT_FUNCTION(
304 "423948 OCSPRequestSession::OnResponseStarted"));
306 DCHECK_EQ(request_
.get(), request
);
307 DCHECK_EQ(base::MessageLoopForIO::current(), io_loop_
);
310 if (request
->status().is_success()) {
311 response_code_
= request_
->GetResponseCode();
312 response_headers_
= request_
->response_headers();
313 response_headers_
->GetMimeType(&response_content_type_
);
314 request_
->Read(buffer_
.get(), kRecvBufferSize
, &bytes_read
);
316 OnReadCompleted(request_
.get(), bytes_read
);
319 void OnReadCompleted(URLRequest
* request
, int bytes_read
) override
{
320 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed.
321 tracked_objects::ScopedTracker
tracking_profile(
322 FROM_HERE_WITH_EXPLICIT_FUNCTION(
323 "423948 OCSPRequestSession::OnReadCompleted"));
325 DCHECK_EQ(request_
.get(), request
);
326 DCHECK_EQ(base::MessageLoopForIO::current(), io_loop_
);
329 if (!request_
->status().is_success() || bytes_read
<= 0)
331 data_
.append(buffer_
->data(), bytes_read
);
332 } while (request_
->Read(buffer_
.get(), kRecvBufferSize
, &bytes_read
));
334 if (!request_
->status().is_io_pending()) {
336 g_ocsp_io_loop
.Get().RemoveRequest(this);
338 base::AutoLock
autolock(lock_
);
343 Release(); // Balanced with StartURLRequest().
347 // Must be called on the IO loop thread.
348 void CancelURLRequest() {
351 base::AutoLock
autolock(lock_
);
353 DCHECK_EQ(base::MessageLoopForIO::current(), io_loop_
);
358 g_ocsp_io_loop
.Get().RemoveRequest(this);
360 base::AutoLock
autolock(lock_
);
365 Release(); // Balanced with StartURLRequest().
370 friend class base::RefCountedThreadSafe
<OCSPRequestSession
>;
372 ~OCSPRequestSession() override
{
373 // When this destructor is called, there should be only one thread that has
374 // a reference to this object, and so that thread doesn't need to lock
380 // Must call this method while holding |lock_|.
381 void CancelLocked() {
382 lock_
.AssertAcquired();
386 base::Bind(&OCSPRequestSession::CancelURLRequest
, this));
390 // Runs on |g_ocsp_io_loop|'s IO loop.
391 void StartURLRequest() {
394 pthread_mutex_lock(&g_request_context_lock
);
395 URLRequestContext
* url_request_context
= g_request_context
;
396 pthread_mutex_unlock(&g_request_context_lock
);
398 if (url_request_context
== NULL
)
402 base::AutoLock
autolock(lock_
);
404 io_loop_
= base::MessageLoopForIO::current();
405 g_ocsp_io_loop
.Get().AddRequest(this);
408 request_
= url_request_context
->CreateRequest(
409 url_
, DEFAULT_PRIORITY
, this, NULL
);
410 // To meet the privacy requirements of incognito mode.
411 request_
->SetLoadFlags(LOAD_DISABLE_CACHE
| LOAD_DO_NOT_SAVE_COOKIES
|
412 LOAD_DO_NOT_SEND_COOKIES
);
414 if (http_request_method_
== "POST") {
415 DCHECK(!upload_content_
.empty());
416 DCHECK(!upload_content_type_
.empty());
418 request_
->set_method("POST");
419 extra_request_headers_
.SetHeader(
420 HttpRequestHeaders::kContentType
, upload_content_type_
);
422 scoped_ptr
<UploadElementReader
> reader(new UploadBytesElementReader(
423 upload_content_
.data(), upload_content_
.size()));
424 request_
->set_upload(
425 ElementsUploadDataStream::CreateWithReader(reader
.Pass(), 0));
427 if (!extra_request_headers_
.IsEmpty())
428 request_
->SetExtraRequestHeaders(extra_request_headers_
);
431 AddRef(); // Release after |request_| deleted.
434 GURL url_
; // The URL we eventually wound up at
435 std::string http_request_method_
;
436 base::TimeDelta timeout_
; // The timeout for OCSP
437 scoped_ptr
<URLRequest
> request_
; // The actual request this wraps
438 scoped_refptr
<IOBuffer
> buffer_
; // Read buffer
439 HttpRequestHeaders extra_request_headers_
;
441 // HTTP POST payload. |request_| reads bytes from this.
442 std::string upload_content_
;
443 std::string upload_content_type_
; // MIME type of POST payload
445 int response_code_
; // HTTP status code for the request
446 std::string response_content_type_
;
447 scoped_refptr
<HttpResponseHeaders
> response_headers_
;
448 std::string data_
; // Results of the request
450 // |lock_| protects |finished_| and |io_loop_|.
451 mutable base::Lock lock_
;
452 base::ConditionVariable cv_
;
454 base::MessageLoop
* io_loop_
; // Message loop of the IO thread
457 DISALLOW_COPY_AND_ASSIGN(OCSPRequestSession
);
460 // Concrete class for SEC_HTTP_SERVER_SESSION.
461 class OCSPServerSession
{
463 OCSPServerSession(const char* host
, PRUint16 port
)
464 : host_and_port_(host
, port
) {}
465 ~OCSPServerSession() {}
467 OCSPRequestSession
* CreateRequest(const char* http_protocol_variant
,
468 const char* path_and_query_string
,
469 const char* http_request_method
,
470 const PRIntervalTime timeout
) {
471 // We dont' support "https" because we haven't thought about
472 // whether it's safe to re-enter this code from talking to an OCSP
473 // responder over SSL.
474 if (strcmp(http_protocol_variant
, "http") != 0) {
475 PORT_SetError(PR_NOT_IMPLEMENTED_ERROR
);
479 std::string
url_string(base::StringPrintf(
481 http_protocol_variant
,
482 host_and_port_
.ToString().c_str(),
483 path_and_query_string
));
484 VLOG(1) << "URL [" << url_string
<< "]";
485 GURL
url(url_string
);
487 // NSS does not expose public functions to adjust the fetch timeout when
488 // using libpkix, so hardcode the upper limit for network fetches.
489 base::TimeDelta actual_timeout
= std::min(
490 base::TimeDelta::FromSeconds(kNetworkFetchTimeoutInSecs
),
491 base::TimeDelta::FromMilliseconds(PR_IntervalToMilliseconds(timeout
)));
493 return new OCSPRequestSession(url
, http_request_method
, actual_timeout
);
498 HostPortPair host_and_port_
;
500 DISALLOW_COPY_AND_ASSIGN(OCSPServerSession
);
503 OCSPIOLoop::OCSPIOLoop()
509 OCSPIOLoop::~OCSPIOLoop() {
510 // IO thread was already deleted before the singleton is deleted
513 base::AutoLock
autolock(lock_
);
519 pthread_mutex_lock(&g_request_context_lock
);
520 DCHECK(!g_request_context
);
521 pthread_mutex_unlock(&g_request_context_lock
);
524 void OCSPIOLoop::Shutdown() {
525 // Safe to read outside lock since we only write on IO thread anyway.
526 DCHECK(thread_checker_
.CalledOnValidThread());
528 // Prevent the worker thread from trying to access |io_loop_|.
530 base::AutoLock
autolock(lock_
);
538 pthread_mutex_lock(&g_request_context_lock
);
539 g_request_context
= NULL
;
540 pthread_mutex_unlock(&g_request_context_lock
);
543 void OCSPIOLoop::PostTaskToIOLoop(
544 const tracked_objects::Location
& from_here
, const base::Closure
& task
) {
545 base::AutoLock
autolock(lock_
);
547 io_loop_
->PostTask(from_here
, task
);
550 void OCSPIOLoop::EnsureIOLoop() {
551 base::AutoLock
autolock(lock_
);
552 DCHECK_EQ(base::MessageLoopForIO::current(), io_loop_
);
555 void OCSPIOLoop::AddRequest(OCSPRequestSession
* request
) {
556 DCHECK(!ContainsKey(requests_
, request
));
557 requests_
.insert(request
);
560 void OCSPIOLoop::RemoveRequest(OCSPRequestSession
* request
) {
561 DCHECK(ContainsKey(requests_
, request
));
562 requests_
.erase(request
);
565 void OCSPIOLoop::CancelAllRequests() {
566 // CancelURLRequest() always removes the request from the requests_
567 // set synchronously.
568 while (!requests_
.empty())
569 (*requests_
.begin())->CancelURLRequest();
572 OCSPNSSInitialization::OCSPNSSInitialization() {
573 // NSS calls the functions in the function table to download certificates
574 // or CRLs or talk to OCSP responders over HTTP. These functions must
575 // set an NSS/NSPR error code when they fail. Otherwise NSS will get the
576 // residual error code from an earlier failed function call.
577 client_fcn_
.version
= 1;
578 SEC_HttpClientFcnV1Struct
*ft
= &client_fcn_
.fcnTable
.ftable1
;
579 ft
->createSessionFcn
= OCSPCreateSession
;
580 ft
->keepAliveSessionFcn
= OCSPKeepAliveSession
;
581 ft
->freeSessionFcn
= OCSPFreeSession
;
582 ft
->createFcn
= OCSPCreate
;
583 ft
->setPostDataFcn
= OCSPSetPostData
;
584 ft
->addHeaderFcn
= OCSPAddHeader
;
585 ft
->trySendAndReceiveFcn
= OCSPTrySendAndReceive
;
586 ft
->cancelFcn
= NULL
;
587 ft
->freeFcn
= OCSPFree
;
588 SECStatus status
= SEC_RegisterDefaultHttpClient(&client_fcn_
);
589 if (status
!= SECSuccess
) {
590 NOTREACHED() << "Error initializing OCSP: " << PR_GetError();
593 // Work around NSS bugs 524013 and 564334. NSS incorrectly thinks the
594 // CRLs for Network Solutions Certificate Authority have bad signatures,
595 // which causes certificates issued by that CA to be reported as revoked.
596 // By using OCSP for those certificates, which don't have AIA extensions,
597 // we can work around these bugs. See http://crbug.com/41730.
598 CERT_StringFromCertFcn old_callback
= NULL
;
599 status
= CERT_RegisterAlternateOCSPAIAInfoCallBack(
600 GetAlternateOCSPAIAInfo
, &old_callback
);
601 if (status
== SECSuccess
) {
602 DCHECK(!old_callback
);
604 NOTREACHED() << "Error initializing OCSP: " << PR_GetError();
608 OCSPNSSInitialization::~OCSPNSSInitialization() {
609 SECStatus status
= CERT_RegisterAlternateOCSPAIAInfoCallBack(NULL
, NULL
);
610 if (status
!= SECSuccess
) {
611 LOG(ERROR
) << "Error unregistering OCSP: " << PR_GetError();
616 // OCSP Http Client functions.
617 // Our Http Client functions operate in blocking mode.
618 SECStatus
OCSPCreateSession(const char* host
, PRUint16 portnum
,
619 SEC_HTTP_SERVER_SESSION
* pSession
) {
620 VLOG(1) << "OCSP create session: host=" << host
<< " port=" << portnum
;
621 pthread_mutex_lock(&g_request_context_lock
);
622 URLRequestContext
* request_context
= g_request_context
;
623 pthread_mutex_unlock(&g_request_context_lock
);
624 if (request_context
== NULL
) {
625 LOG(ERROR
) << "No URLRequestContext for NSS HTTP handler. host: " << host
;
626 // The application failed to call SetURLRequestContextForNSSHttpIO or
627 // has already called ShutdownNSSHttpIO, so we can't create and use
628 // URLRequest. PR_NOT_IMPLEMENTED_ERROR is not an accurate error
629 // code for these error conditions, but is close enough.
630 PORT_SetError(PR_NOT_IMPLEMENTED_ERROR
);
633 *pSession
= new OCSPServerSession(host
, portnum
);
637 SECStatus
OCSPKeepAliveSession(SEC_HTTP_SERVER_SESSION session
,
638 PRPollDesc
**pPollDesc
) {
639 VLOG(1) << "OCSP keep alive";
645 SECStatus
OCSPFreeSession(SEC_HTTP_SERVER_SESSION session
) {
646 VLOG(1) << "OCSP free session";
647 delete reinterpret_cast<OCSPServerSession
*>(session
);
651 SECStatus
OCSPCreate(SEC_HTTP_SERVER_SESSION session
,
652 const char* http_protocol_variant
,
653 const char* path_and_query_string
,
654 const char* http_request_method
,
655 const PRIntervalTime timeout
,
656 SEC_HTTP_REQUEST_SESSION
* pRequest
) {
657 VLOG(1) << "OCSP create protocol=" << http_protocol_variant
658 << " path_and_query=" << path_and_query_string
659 << " http_request_method=" << http_request_method
660 << " timeout=" << timeout
;
661 OCSPServerSession
* ocsp_session
=
662 reinterpret_cast<OCSPServerSession
*>(session
);
664 OCSPRequestSession
* req
= ocsp_session
->CreateRequest(http_protocol_variant
,
665 path_and_query_string
,
668 SECStatus rv
= SECFailure
;
670 req
->AddRef(); // Release in OCSPFree().
677 SECStatus
OCSPSetPostData(SEC_HTTP_REQUEST_SESSION request
,
678 const char* http_data
,
679 const PRUint32 http_data_len
,
680 const char* http_content_type
) {
681 VLOG(1) << "OCSP set post data len=" << http_data_len
;
682 OCSPRequestSession
* req
= reinterpret_cast<OCSPRequestSession
*>(request
);
684 req
->SetPostData(http_data
, http_data_len
, http_content_type
);
688 SECStatus
OCSPAddHeader(SEC_HTTP_REQUEST_SESSION request
,
689 const char* http_header_name
,
690 const char* http_header_value
) {
691 VLOG(1) << "OCSP add header name=" << http_header_name
692 << " value=" << http_header_value
;
693 OCSPRequestSession
* req
= reinterpret_cast<OCSPRequestSession
*>(request
);
695 req
->AddHeader(http_header_name
, http_header_value
);
699 // Sets response of |req| in the output parameters.
700 // It is helper routine for OCSP trySendAndReceiveFcn.
701 // |http_response_data_len| could be used as input parameter. If it has
702 // non-zero value, it is considered as maximum size of |http_response_data|.
703 SECStatus
OCSPSetResponse(OCSPRequestSession
* req
,
704 PRUint16
* http_response_code
,
705 const char** http_response_content_type
,
706 const char** http_response_headers
,
707 const char** http_response_data
,
708 PRUint32
* http_response_data_len
) {
709 DCHECK(req
->Finished());
710 const std::string
& data
= req
->http_response_data();
711 if (http_response_data_len
&& *http_response_data_len
) {
712 if (*http_response_data_len
< data
.size()) {
713 LOG(ERROR
) << "response body too large: " << *http_response_data_len
714 << " < " << data
.size();
715 *http_response_data_len
= data
.size();
716 PORT_SetError(SEC_ERROR_BAD_HTTP_RESPONSE
);
720 VLOG(1) << "OCSP response "
721 << " response_code=" << req
->http_response_code()
722 << " content_type=" << req
->http_response_content_type()
723 << " header=" << req
->http_response_headers()
724 << " data_len=" << data
.size();
725 if (http_response_code
)
726 *http_response_code
= req
->http_response_code();
727 if (http_response_content_type
)
728 *http_response_content_type
= req
->http_response_content_type().c_str();
729 if (http_response_headers
)
730 *http_response_headers
= req
->http_response_headers().c_str();
731 if (http_response_data
)
732 *http_response_data
= data
.data();
733 if (http_response_data_len
)
734 *http_response_data_len
= data
.size();
738 SECStatus
OCSPTrySendAndReceive(SEC_HTTP_REQUEST_SESSION request
,
739 PRPollDesc
** pPollDesc
,
740 PRUint16
* http_response_code
,
741 const char** http_response_content_type
,
742 const char** http_response_headers
,
743 const char** http_response_data
,
744 PRUint32
* http_response_data_len
) {
745 if (http_response_data_len
) {
746 // We must always set an output value, even on failure. The output value 0
747 // means the failure was unrelated to the acceptable response data length.
748 *http_response_data_len
= 0;
751 VLOG(1) << "OCSP try send and receive";
752 OCSPRequestSession
* req
= reinterpret_cast<OCSPRequestSession
*>(request
);
753 // We support blocking mode only.
757 if (req
->Started() || req
->Finished()) {
758 // We support blocking mode only, so this function shouldn't be called
759 // again when req has stareted or finished.
761 PORT_SetError(SEC_ERROR_BAD_HTTP_RESPONSE
); // Simple approximation.
765 const base::Time start_time
= base::Time::Now();
766 bool request_ok
= true;
768 if (!req
->Wait() || req
->http_response_code() == static_cast<PRUint16
>(-1)) {
769 // If the response code is -1, the request failed and there is no response.
772 const base::TimeDelta duration
= base::Time::Now() - start_time
;
774 // For metrics, we want to know if the request was 'successful' or not.
775 // |request_ok| determines if we'll pass the response back to NSS and |ok|
776 // keep track of if we think the response was good.
779 (req
->http_response_code() >= 400 && req
->http_response_code() < 600) ||
780 req
->http_response_data().size() == 0 ||
781 // 0x30 is the ASN.1 DER encoding of a SEQUENCE. All valid OCSP/CRL/CRT
782 // responses must start with this. If we didn't check for this then a
783 // captive portal could provide an HTML reply that we would count as a
784 // 'success' (although it wouldn't count in NSS, of course).
785 req
->http_response_data().data()[0] != 0x30) {
789 // We want to know if this was:
790 // 1) An OCSP request
792 // 3) A request for a missing intermediate certificate
793 // There's no sure way to do this, so we use heuristics like MIME type and
795 const char* mime_type
= "";
797 mime_type
= req
->http_response_content_type().c_str();
799 strcasecmp(mime_type
, "application/ocsp-response") == 0;
800 bool is_crl
= strcasecmp(mime_type
, "application/x-pkcs7-crl") == 0 ||
801 strcasecmp(mime_type
, "application/x-x509-crl") == 0 ||
802 strcasecmp(mime_type
, "application/pkix-crl") == 0;
804 strcasecmp(mime_type
, "application/x-x509-ca-cert") == 0 ||
805 strcasecmp(mime_type
, "application/x-x509-server-cert") == 0 ||
806 strcasecmp(mime_type
, "application/pkix-cert") == 0 ||
807 strcasecmp(mime_type
, "application/pkcs7-mime") == 0;
809 if (!is_cert
&& !is_crl
&& !is_ocsp
) {
810 // We didn't get a hint from the MIME type, so do the best that we can.
811 const std::string path
= req
->url().path();
812 const std::string host
= req
->url().host();
813 is_crl
= strcasestr(path
.c_str(), ".crl") != NULL
;
814 is_cert
= strcasestr(path
.c_str(), ".crt") != NULL
||
815 strcasestr(path
.c_str(), ".p7c") != NULL
||
816 strcasestr(path
.c_str(), ".cer") != NULL
;
817 is_ocsp
= strcasestr(host
.c_str(), "ocsp") != NULL
||
818 req
->http_request_method() == "POST";
823 UMA_HISTOGRAM_TIMES("Net.OCSPRequestTimeMs", duration
);
824 UMA_HISTOGRAM_BOOLEAN("Net.OCSPRequestSuccess", true);
826 UMA_HISTOGRAM_TIMES("Net.OCSPRequestFailedTimeMs", duration
);
827 UMA_HISTOGRAM_BOOLEAN("Net.OCSPRequestSuccess", false);
831 UMA_HISTOGRAM_TIMES("Net.CRLRequestTimeMs", duration
);
832 UMA_HISTOGRAM_BOOLEAN("Net.CRLRequestSuccess", true);
834 UMA_HISTOGRAM_TIMES("Net.CRLRequestFailedTimeMs", duration
);
835 UMA_HISTOGRAM_BOOLEAN("Net.CRLRequestSuccess", false);
837 } else if (is_cert
) {
839 UMA_HISTOGRAM_TIMES("Net.CRTRequestTimeMs", duration
);
842 UMA_HISTOGRAM_TIMES("Net.UnknownTypeRequestTimeMs", duration
);
846 PORT_SetError(SEC_ERROR_BAD_HTTP_RESPONSE
); // Simple approximation.
850 return OCSPSetResponse(
851 req
, http_response_code
,
852 http_response_content_type
,
853 http_response_headers
,
855 http_response_data_len
);
858 SECStatus
OCSPFree(SEC_HTTP_REQUEST_SESSION request
) {
859 VLOG(1) << "OCSP free";
860 OCSPRequestSession
* req
= reinterpret_cast<OCSPRequestSession
*>(request
);
866 // Data for GetAlternateOCSPAIAInfo.
868 // CN=Network Solutions Certificate Authority,O=Network Solutions L.L.C.,C=US
870 // There are two CAs with this name. Their key IDs are listed next.
871 const unsigned char network_solutions_ca_name
[] = {
872 0x30, 0x62, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04,
873 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x21, 0x30, 0x1f, 0x06,
874 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x4e, 0x65, 0x74, 0x77,
875 0x6f, 0x72, 0x6b, 0x20, 0x53, 0x6f, 0x6c, 0x75, 0x74, 0x69,
876 0x6f, 0x6e, 0x73, 0x20, 0x4c, 0x2e, 0x4c, 0x2e, 0x43, 0x2e,
877 0x31, 0x30, 0x30, 0x2e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
878 0x27, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x53,
879 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x43,
880 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65,
881 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79
883 const unsigned int network_solutions_ca_name_len
= 100;
885 // This CA is an intermediate CA, subordinate to UTN-USERFirst-Hardware.
886 const unsigned char network_solutions_ca_key_id
[] = {
887 0x3c, 0x41, 0xe2, 0x8f, 0x08, 0x08, 0xa9, 0x4c, 0x25, 0x89,
888 0x8d, 0x6d, 0xc5, 0x38, 0xd0, 0xfc, 0x85, 0x8c, 0x62, 0x17
890 const unsigned int network_solutions_ca_key_id_len
= 20;
892 // This CA is a root CA. It is also cross-certified by
893 // UTN-USERFirst-Hardware.
894 const unsigned char network_solutions_ca_key_id2
[] = {
895 0x21, 0x30, 0xc9, 0xfb, 0x00, 0xd7, 0x4e, 0x98, 0xda, 0x87,
896 0xaa, 0x2a, 0xd0, 0xa7, 0x2e, 0xb1, 0x40, 0x31, 0xa7, 0x4c
898 const unsigned int network_solutions_ca_key_id2_len
= 20;
900 // An entry in our OCSP responder table. |issuer| and |issuer_key_id| are
901 // the key. |ocsp_url| is the value.
902 struct OCSPResponderTableEntry
{
904 SECItem issuer_key_id
;
905 const char *ocsp_url
;
908 const OCSPResponderTableEntry g_ocsp_responder_table
[] = {
912 const_cast<unsigned char*>(network_solutions_ca_name
),
913 network_solutions_ca_name_len
917 const_cast<unsigned char*>(network_solutions_ca_key_id
),
918 network_solutions_ca_key_id_len
920 "http://ocsp.netsolssl.com"
925 const_cast<unsigned char*>(network_solutions_ca_name
),
926 network_solutions_ca_name_len
930 const_cast<unsigned char*>(network_solutions_ca_key_id2
),
931 network_solutions_ca_key_id2_len
933 "http://ocsp.netsolssl.com"
937 char* GetAlternateOCSPAIAInfo(CERTCertificate
*cert
) {
938 if (cert
&& !cert
->isRoot
&& cert
->authKeyID
) {
939 for (unsigned int i
=0; i
< arraysize(g_ocsp_responder_table
); i
++) {
940 if (SECITEM_CompareItem(&g_ocsp_responder_table
[i
].issuer
,
941 &cert
->derIssuer
) == SECEqual
&&
942 SECITEM_CompareItem(&g_ocsp_responder_table
[i
].issuer_key_id
,
943 &cert
->authKeyID
->keyID
) == SECEqual
) {
944 return PORT_Strdup(g_ocsp_responder_table
[i
].ocsp_url
);
952 } // anonymous namespace
954 void SetMessageLoopForNSSHttpIO() {
955 // Must have a MessageLoopForIO.
956 DCHECK(base::MessageLoopForIO::current());
958 bool used
= g_ocsp_io_loop
.Get().used();
960 // Should not be called when g_ocsp_io_loop has already been used.
964 void EnsureNSSHttpIOInit() {
965 g_ocsp_io_loop
.Get().StartUsing();
966 g_ocsp_nss_initialization
.Get();
969 void ShutdownNSSHttpIO() {
970 g_ocsp_io_loop
.Get().Shutdown();
973 void ResetNSSHttpIOForTesting() {
974 g_ocsp_io_loop
.Get().ReuseForTesting();
977 // This function would be called before NSS initialization.
978 void SetURLRequestContextForNSSHttpIO(URLRequestContext
* request_context
) {
979 pthread_mutex_lock(&g_request_context_lock
);
980 if (request_context
) {
981 DCHECK(!g_request_context
);
983 g_request_context
= request_context
;
984 pthread_mutex_unlock(&g_request_context_lock
);