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(url_
, DEFAULT_PRIORITY
, this);
409 // To meet the privacy requirements of incognito mode.
410 request_
->SetLoadFlags(LOAD_DISABLE_CACHE
| LOAD_DO_NOT_SAVE_COOKIES
|
411 LOAD_DO_NOT_SEND_COOKIES
);
413 if (http_request_method_
== "POST") {
414 DCHECK(!upload_content_
.empty());
415 DCHECK(!upload_content_type_
.empty());
417 request_
->set_method("POST");
418 extra_request_headers_
.SetHeader(
419 HttpRequestHeaders::kContentType
, upload_content_type_
);
421 scoped_ptr
<UploadElementReader
> reader(new UploadBytesElementReader(
422 upload_content_
.data(), upload_content_
.size()));
423 request_
->set_upload(
424 ElementsUploadDataStream::CreateWithReader(reader
.Pass(), 0));
426 if (!extra_request_headers_
.IsEmpty())
427 request_
->SetExtraRequestHeaders(extra_request_headers_
);
430 AddRef(); // Release after |request_| deleted.
433 GURL url_
; // The URL we eventually wound up at
434 std::string http_request_method_
;
435 base::TimeDelta timeout_
; // The timeout for OCSP
436 scoped_ptr
<URLRequest
> request_
; // The actual request this wraps
437 scoped_refptr
<IOBuffer
> buffer_
; // Read buffer
438 HttpRequestHeaders extra_request_headers_
;
440 // HTTP POST payload. |request_| reads bytes from this.
441 std::string upload_content_
;
442 std::string upload_content_type_
; // MIME type of POST payload
444 int response_code_
; // HTTP status code for the request
445 std::string response_content_type_
;
446 scoped_refptr
<HttpResponseHeaders
> response_headers_
;
447 std::string data_
; // Results of the request
449 // |lock_| protects |finished_| and |io_loop_|.
450 mutable base::Lock lock_
;
451 base::ConditionVariable cv_
;
453 base::MessageLoop
* io_loop_
; // Message loop of the IO thread
456 DISALLOW_COPY_AND_ASSIGN(OCSPRequestSession
);
459 // Concrete class for SEC_HTTP_SERVER_SESSION.
460 class OCSPServerSession
{
462 OCSPServerSession(const char* host
, PRUint16 port
)
463 : host_and_port_(host
, port
) {}
464 ~OCSPServerSession() {}
466 OCSPRequestSession
* CreateRequest(const char* http_protocol_variant
,
467 const char* path_and_query_string
,
468 const char* http_request_method
,
469 const PRIntervalTime timeout
) {
470 // We dont' support "https" because we haven't thought about
471 // whether it's safe to re-enter this code from talking to an OCSP
472 // responder over SSL.
473 if (strcmp(http_protocol_variant
, "http") != 0) {
474 PORT_SetError(PR_NOT_IMPLEMENTED_ERROR
);
478 std::string
url_string(base::StringPrintf(
480 http_protocol_variant
,
481 host_and_port_
.ToString().c_str(),
482 path_and_query_string
));
483 VLOG(1) << "URL [" << url_string
<< "]";
484 GURL
url(url_string
);
486 // NSS does not expose public functions to adjust the fetch timeout when
487 // using libpkix, so hardcode the upper limit for network fetches.
488 base::TimeDelta actual_timeout
= std::min(
489 base::TimeDelta::FromSeconds(kNetworkFetchTimeoutInSecs
),
490 base::TimeDelta::FromMilliseconds(PR_IntervalToMilliseconds(timeout
)));
492 return new OCSPRequestSession(url
, http_request_method
, actual_timeout
);
497 HostPortPair host_and_port_
;
499 DISALLOW_COPY_AND_ASSIGN(OCSPServerSession
);
502 OCSPIOLoop::OCSPIOLoop()
508 OCSPIOLoop::~OCSPIOLoop() {
509 // IO thread was already deleted before the singleton is deleted
512 base::AutoLock
autolock(lock_
);
518 pthread_mutex_lock(&g_request_context_lock
);
519 DCHECK(!g_request_context
);
520 pthread_mutex_unlock(&g_request_context_lock
);
523 void OCSPIOLoop::Shutdown() {
524 // Safe to read outside lock since we only write on IO thread anyway.
525 DCHECK(thread_checker_
.CalledOnValidThread());
527 // Prevent the worker thread from trying to access |io_loop_|.
529 base::AutoLock
autolock(lock_
);
537 pthread_mutex_lock(&g_request_context_lock
);
538 g_request_context
= NULL
;
539 pthread_mutex_unlock(&g_request_context_lock
);
542 void OCSPIOLoop::PostTaskToIOLoop(
543 const tracked_objects::Location
& from_here
, const base::Closure
& task
) {
544 base::AutoLock
autolock(lock_
);
546 io_loop_
->PostTask(from_here
, task
);
549 void OCSPIOLoop::EnsureIOLoop() {
550 base::AutoLock
autolock(lock_
);
551 DCHECK_EQ(base::MessageLoopForIO::current(), io_loop_
);
554 void OCSPIOLoop::AddRequest(OCSPRequestSession
* request
) {
555 DCHECK(!ContainsKey(requests_
, request
));
556 requests_
.insert(request
);
559 void OCSPIOLoop::RemoveRequest(OCSPRequestSession
* request
) {
560 DCHECK(ContainsKey(requests_
, request
));
561 requests_
.erase(request
);
564 void OCSPIOLoop::CancelAllRequests() {
565 // CancelURLRequest() always removes the request from the requests_
566 // set synchronously.
567 while (!requests_
.empty())
568 (*requests_
.begin())->CancelURLRequest();
571 OCSPNSSInitialization::OCSPNSSInitialization() {
572 // NSS calls the functions in the function table to download certificates
573 // or CRLs or talk to OCSP responders over HTTP. These functions must
574 // set an NSS/NSPR error code when they fail. Otherwise NSS will get the
575 // residual error code from an earlier failed function call.
576 client_fcn_
.version
= 1;
577 SEC_HttpClientFcnV1Struct
*ft
= &client_fcn_
.fcnTable
.ftable1
;
578 ft
->createSessionFcn
= OCSPCreateSession
;
579 ft
->keepAliveSessionFcn
= OCSPKeepAliveSession
;
580 ft
->freeSessionFcn
= OCSPFreeSession
;
581 ft
->createFcn
= OCSPCreate
;
582 ft
->setPostDataFcn
= OCSPSetPostData
;
583 ft
->addHeaderFcn
= OCSPAddHeader
;
584 ft
->trySendAndReceiveFcn
= OCSPTrySendAndReceive
;
585 ft
->cancelFcn
= NULL
;
586 ft
->freeFcn
= OCSPFree
;
587 SECStatus status
= SEC_RegisterDefaultHttpClient(&client_fcn_
);
588 if (status
!= SECSuccess
) {
589 NOTREACHED() << "Error initializing OCSP: " << PR_GetError();
592 // Work around NSS bugs 524013 and 564334. NSS incorrectly thinks the
593 // CRLs for Network Solutions Certificate Authority have bad signatures,
594 // which causes certificates issued by that CA to be reported as revoked.
595 // By using OCSP for those certificates, which don't have AIA extensions,
596 // we can work around these bugs. See http://crbug.com/41730.
597 CERT_StringFromCertFcn old_callback
= NULL
;
598 status
= CERT_RegisterAlternateOCSPAIAInfoCallBack(
599 GetAlternateOCSPAIAInfo
, &old_callback
);
600 if (status
== SECSuccess
) {
601 DCHECK(!old_callback
);
603 NOTREACHED() << "Error initializing OCSP: " << PR_GetError();
607 OCSPNSSInitialization::~OCSPNSSInitialization() {
608 SECStatus status
= CERT_RegisterAlternateOCSPAIAInfoCallBack(NULL
, NULL
);
609 if (status
!= SECSuccess
) {
610 LOG(ERROR
) << "Error unregistering OCSP: " << PR_GetError();
615 // OCSP Http Client functions.
616 // Our Http Client functions operate in blocking mode.
617 SECStatus
OCSPCreateSession(const char* host
, PRUint16 portnum
,
618 SEC_HTTP_SERVER_SESSION
* pSession
) {
619 VLOG(1) << "OCSP create session: host=" << host
<< " port=" << portnum
;
620 pthread_mutex_lock(&g_request_context_lock
);
621 URLRequestContext
* request_context
= g_request_context
;
622 pthread_mutex_unlock(&g_request_context_lock
);
623 if (request_context
== NULL
) {
624 LOG(ERROR
) << "No URLRequestContext for NSS HTTP handler. host: " << host
;
625 // The application failed to call SetURLRequestContextForNSSHttpIO or
626 // has already called ShutdownNSSHttpIO, so we can't create and use
627 // URLRequest. PR_NOT_IMPLEMENTED_ERROR is not an accurate error
628 // code for these error conditions, but is close enough.
629 PORT_SetError(PR_NOT_IMPLEMENTED_ERROR
);
632 *pSession
= new OCSPServerSession(host
, portnum
);
636 SECStatus
OCSPKeepAliveSession(SEC_HTTP_SERVER_SESSION session
,
637 PRPollDesc
**pPollDesc
) {
638 VLOG(1) << "OCSP keep alive";
644 SECStatus
OCSPFreeSession(SEC_HTTP_SERVER_SESSION session
) {
645 VLOG(1) << "OCSP free session";
646 delete reinterpret_cast<OCSPServerSession
*>(session
);
650 SECStatus
OCSPCreate(SEC_HTTP_SERVER_SESSION session
,
651 const char* http_protocol_variant
,
652 const char* path_and_query_string
,
653 const char* http_request_method
,
654 const PRIntervalTime timeout
,
655 SEC_HTTP_REQUEST_SESSION
* pRequest
) {
656 VLOG(1) << "OCSP create protocol=" << http_protocol_variant
657 << " path_and_query=" << path_and_query_string
658 << " http_request_method=" << http_request_method
659 << " timeout=" << timeout
;
660 OCSPServerSession
* ocsp_session
=
661 reinterpret_cast<OCSPServerSession
*>(session
);
663 OCSPRequestSession
* req
= ocsp_session
->CreateRequest(http_protocol_variant
,
664 path_and_query_string
,
667 SECStatus rv
= SECFailure
;
669 req
->AddRef(); // Release in OCSPFree().
676 SECStatus
OCSPSetPostData(SEC_HTTP_REQUEST_SESSION request
,
677 const char* http_data
,
678 const PRUint32 http_data_len
,
679 const char* http_content_type
) {
680 VLOG(1) << "OCSP set post data len=" << http_data_len
;
681 OCSPRequestSession
* req
= reinterpret_cast<OCSPRequestSession
*>(request
);
683 req
->SetPostData(http_data
, http_data_len
, http_content_type
);
687 SECStatus
OCSPAddHeader(SEC_HTTP_REQUEST_SESSION request
,
688 const char* http_header_name
,
689 const char* http_header_value
) {
690 VLOG(1) << "OCSP add header name=" << http_header_name
691 << " value=" << http_header_value
;
692 OCSPRequestSession
* req
= reinterpret_cast<OCSPRequestSession
*>(request
);
694 req
->AddHeader(http_header_name
, http_header_value
);
698 // Sets response of |req| in the output parameters.
699 // It is helper routine for OCSP trySendAndReceiveFcn.
700 // |http_response_data_len| could be used as input parameter. If it has
701 // non-zero value, it is considered as maximum size of |http_response_data|.
702 SECStatus
OCSPSetResponse(OCSPRequestSession
* req
,
703 PRUint16
* http_response_code
,
704 const char** http_response_content_type
,
705 const char** http_response_headers
,
706 const char** http_response_data
,
707 PRUint32
* http_response_data_len
) {
708 DCHECK(req
->Finished());
709 const std::string
& data
= req
->http_response_data();
710 if (http_response_data_len
&& *http_response_data_len
) {
711 if (*http_response_data_len
< data
.size()) {
712 LOG(ERROR
) << "response body too large: " << *http_response_data_len
713 << " < " << data
.size();
714 *http_response_data_len
= data
.size();
715 PORT_SetError(SEC_ERROR_BAD_HTTP_RESPONSE
);
719 VLOG(1) << "OCSP response "
720 << " response_code=" << req
->http_response_code()
721 << " content_type=" << req
->http_response_content_type()
722 << " header=" << req
->http_response_headers()
723 << " data_len=" << data
.size();
724 if (http_response_code
)
725 *http_response_code
= req
->http_response_code();
726 if (http_response_content_type
)
727 *http_response_content_type
= req
->http_response_content_type().c_str();
728 if (http_response_headers
)
729 *http_response_headers
= req
->http_response_headers().c_str();
730 if (http_response_data
)
731 *http_response_data
= data
.data();
732 if (http_response_data_len
)
733 *http_response_data_len
= data
.size();
737 SECStatus
OCSPTrySendAndReceive(SEC_HTTP_REQUEST_SESSION request
,
738 PRPollDesc
** pPollDesc
,
739 PRUint16
* http_response_code
,
740 const char** http_response_content_type
,
741 const char** http_response_headers
,
742 const char** http_response_data
,
743 PRUint32
* http_response_data_len
) {
744 if (http_response_data_len
) {
745 // We must always set an output value, even on failure. The output value 0
746 // means the failure was unrelated to the acceptable response data length.
747 *http_response_data_len
= 0;
750 VLOG(1) << "OCSP try send and receive";
751 OCSPRequestSession
* req
= reinterpret_cast<OCSPRequestSession
*>(request
);
752 // We support blocking mode only.
756 if (req
->Started() || req
->Finished()) {
757 // We support blocking mode only, so this function shouldn't be called
758 // again when req has stareted or finished.
760 PORT_SetError(SEC_ERROR_BAD_HTTP_RESPONSE
); // Simple approximation.
764 const base::Time start_time
= base::Time::Now();
765 bool request_ok
= true;
767 if (!req
->Wait() || req
->http_response_code() == static_cast<PRUint16
>(-1)) {
768 // If the response code is -1, the request failed and there is no response.
771 const base::TimeDelta duration
= base::Time::Now() - start_time
;
773 // For metrics, we want to know if the request was 'successful' or not.
774 // |request_ok| determines if we'll pass the response back to NSS and |ok|
775 // keep track of if we think the response was good.
778 (req
->http_response_code() >= 400 && req
->http_response_code() < 600) ||
779 req
->http_response_data().size() == 0 ||
780 // 0x30 is the ASN.1 DER encoding of a SEQUENCE. All valid OCSP/CRL/CRT
781 // responses must start with this. If we didn't check for this then a
782 // captive portal could provide an HTML reply that we would count as a
783 // 'success' (although it wouldn't count in NSS, of course).
784 req
->http_response_data().data()[0] != 0x30) {
788 // We want to know if this was:
789 // 1) An OCSP request
791 // 3) A request for a missing intermediate certificate
792 // There's no sure way to do this, so we use heuristics like MIME type and
794 const char* mime_type
= "";
796 mime_type
= req
->http_response_content_type().c_str();
798 strcasecmp(mime_type
, "application/ocsp-response") == 0;
799 bool is_crl
= strcasecmp(mime_type
, "application/x-pkcs7-crl") == 0 ||
800 strcasecmp(mime_type
, "application/x-x509-crl") == 0 ||
801 strcasecmp(mime_type
, "application/pkix-crl") == 0;
803 strcasecmp(mime_type
, "application/x-x509-ca-cert") == 0 ||
804 strcasecmp(mime_type
, "application/x-x509-server-cert") == 0 ||
805 strcasecmp(mime_type
, "application/pkix-cert") == 0 ||
806 strcasecmp(mime_type
, "application/pkcs7-mime") == 0;
808 if (!is_cert
&& !is_crl
&& !is_ocsp
) {
809 // We didn't get a hint from the MIME type, so do the best that we can.
810 const std::string path
= req
->url().path();
811 const std::string host
= req
->url().host();
812 is_crl
= strcasestr(path
.c_str(), ".crl") != NULL
;
813 is_cert
= strcasestr(path
.c_str(), ".crt") != NULL
||
814 strcasestr(path
.c_str(), ".p7c") != NULL
||
815 strcasestr(path
.c_str(), ".cer") != NULL
;
816 is_ocsp
= strcasestr(host
.c_str(), "ocsp") != NULL
||
817 req
->http_request_method() == "POST";
822 UMA_HISTOGRAM_TIMES("Net.OCSPRequestTimeMs", duration
);
823 UMA_HISTOGRAM_BOOLEAN("Net.OCSPRequestSuccess", true);
825 UMA_HISTOGRAM_TIMES("Net.OCSPRequestFailedTimeMs", duration
);
826 UMA_HISTOGRAM_BOOLEAN("Net.OCSPRequestSuccess", false);
830 UMA_HISTOGRAM_TIMES("Net.CRLRequestTimeMs", duration
);
831 UMA_HISTOGRAM_BOOLEAN("Net.CRLRequestSuccess", true);
833 UMA_HISTOGRAM_TIMES("Net.CRLRequestFailedTimeMs", duration
);
834 UMA_HISTOGRAM_BOOLEAN("Net.CRLRequestSuccess", false);
836 } else if (is_cert
) {
838 UMA_HISTOGRAM_TIMES("Net.CRTRequestTimeMs", duration
);
841 UMA_HISTOGRAM_TIMES("Net.UnknownTypeRequestTimeMs", duration
);
845 PORT_SetError(SEC_ERROR_BAD_HTTP_RESPONSE
); // Simple approximation.
849 return OCSPSetResponse(
850 req
, http_response_code
,
851 http_response_content_type
,
852 http_response_headers
,
854 http_response_data_len
);
857 SECStatus
OCSPFree(SEC_HTTP_REQUEST_SESSION request
) {
858 VLOG(1) << "OCSP free";
859 OCSPRequestSession
* req
= reinterpret_cast<OCSPRequestSession
*>(request
);
865 // Data for GetAlternateOCSPAIAInfo.
867 // CN=Network Solutions Certificate Authority,O=Network Solutions L.L.C.,C=US
869 // There are two CAs with this name. Their key IDs are listed next.
870 const unsigned char network_solutions_ca_name
[] = {
871 0x30, 0x62, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04,
872 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x21, 0x30, 0x1f, 0x06,
873 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x4e, 0x65, 0x74, 0x77,
874 0x6f, 0x72, 0x6b, 0x20, 0x53, 0x6f, 0x6c, 0x75, 0x74, 0x69,
875 0x6f, 0x6e, 0x73, 0x20, 0x4c, 0x2e, 0x4c, 0x2e, 0x43, 0x2e,
876 0x31, 0x30, 0x30, 0x2e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
877 0x27, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x53,
878 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x43,
879 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65,
880 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79
882 const unsigned int network_solutions_ca_name_len
= 100;
884 // This CA is an intermediate CA, subordinate to UTN-USERFirst-Hardware.
885 const unsigned char network_solutions_ca_key_id
[] = {
886 0x3c, 0x41, 0xe2, 0x8f, 0x08, 0x08, 0xa9, 0x4c, 0x25, 0x89,
887 0x8d, 0x6d, 0xc5, 0x38, 0xd0, 0xfc, 0x85, 0x8c, 0x62, 0x17
889 const unsigned int network_solutions_ca_key_id_len
= 20;
891 // This CA is a root CA. It is also cross-certified by
892 // UTN-USERFirst-Hardware.
893 const unsigned char network_solutions_ca_key_id2
[] = {
894 0x21, 0x30, 0xc9, 0xfb, 0x00, 0xd7, 0x4e, 0x98, 0xda, 0x87,
895 0xaa, 0x2a, 0xd0, 0xa7, 0x2e, 0xb1, 0x40, 0x31, 0xa7, 0x4c
897 const unsigned int network_solutions_ca_key_id2_len
= 20;
899 // An entry in our OCSP responder table. |issuer| and |issuer_key_id| are
900 // the key. |ocsp_url| is the value.
901 struct OCSPResponderTableEntry
{
903 SECItem issuer_key_id
;
904 const char *ocsp_url
;
907 const OCSPResponderTableEntry g_ocsp_responder_table
[] = {
911 const_cast<unsigned char*>(network_solutions_ca_name
),
912 network_solutions_ca_name_len
916 const_cast<unsigned char*>(network_solutions_ca_key_id
),
917 network_solutions_ca_key_id_len
919 "http://ocsp.netsolssl.com"
924 const_cast<unsigned char*>(network_solutions_ca_name
),
925 network_solutions_ca_name_len
929 const_cast<unsigned char*>(network_solutions_ca_key_id2
),
930 network_solutions_ca_key_id2_len
932 "http://ocsp.netsolssl.com"
936 char* GetAlternateOCSPAIAInfo(CERTCertificate
*cert
) {
937 if (cert
&& !cert
->isRoot
&& cert
->authKeyID
) {
938 for (unsigned int i
=0; i
< arraysize(g_ocsp_responder_table
); i
++) {
939 if (SECITEM_CompareItem(&g_ocsp_responder_table
[i
].issuer
,
940 &cert
->derIssuer
) == SECEqual
&&
941 SECITEM_CompareItem(&g_ocsp_responder_table
[i
].issuer_key_id
,
942 &cert
->authKeyID
->keyID
) == SECEqual
) {
943 return PORT_Strdup(g_ocsp_responder_table
[i
].ocsp_url
);
951 } // anonymous namespace
953 void SetMessageLoopForNSSHttpIO() {
954 // Must have a MessageLoopForIO.
955 DCHECK(base::MessageLoopForIO::current());
957 bool used
= g_ocsp_io_loop
.Get().used();
959 // Should not be called when g_ocsp_io_loop has already been used.
963 void EnsureNSSHttpIOInit() {
964 g_ocsp_io_loop
.Get().StartUsing();
965 g_ocsp_nss_initialization
.Get();
968 void ShutdownNSSHttpIO() {
969 g_ocsp_io_loop
.Get().Shutdown();
972 void ResetNSSHttpIOForTesting() {
973 g_ocsp_io_loop
.Get().ReuseForTesting();
976 // This function would be called before NSS initialization.
977 void SetURLRequestContextForNSSHttpIO(URLRequestContext
* request_context
) {
978 pthread_mutex_lock(&g_request_context_lock
);
979 if (request_context
) {
980 DCHECK(!g_request_context
);
982 g_request_context
= request_context
;
983 pthread_mutex_unlock(&g_request_context_lock
);