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/request_priority.h"
36 #include "net/base/upload_bytes_element_reader.h"
37 #include "net/base/upload_data_stream.h"
38 #include "net/http/http_request_headers.h"
39 #include "net/http/http_response_headers.h"
40 #include "net/url_request/url_request.h"
41 #include "net/url_request/url_request_context.h"
48 // Protects |g_request_context|.
49 pthread_mutex_t g_request_context_lock
= PTHREAD_MUTEX_INITIALIZER
;
50 URLRequestContext
* g_request_context
= NULL
;
52 // The default timeout for network fetches in NSS is 60 seconds. Choose a
53 // saner upper limit for OCSP/CRL/AIA fetches.
54 const int kNetworkFetchTimeoutInSecs
= 15;
56 class OCSPRequestSession
;
61 base::AutoLock
autolock(lock_
);
63 io_loop_
= base::MessageLoopForIO::current();
71 base::AutoLock
autolock(lock_
);
75 // Called from worker thread.
76 void PostTaskToIOLoop(const tracked_objects::Location
& from_here
,
77 const base::Closure
& task
);
81 void AddRequest(OCSPRequestSession
* request
);
82 void RemoveRequest(OCSPRequestSession
* request
);
84 // Clears internal state and calls |StartUsing()|. Should be called only in
85 // the context of testing.
86 void ReuseForTesting() {
88 base::AutoLock
autolock(lock_
);
89 DCHECK(base::MessageLoopForIO::current());
90 thread_checker_
.DetachFromThread();
92 // CalledOnValidThread is the only available API to reassociate
93 // thread_checker_ with the current thread. Result ignored intentionally.
94 ignore_result(thread_checker_
.CalledOnValidThread());
102 friend struct base::DefaultLazyInstanceTraits
<OCSPIOLoop
>;
107 void CancelAllRequests();
109 mutable base::Lock lock_
;
110 bool shutdown_
; // Protected by |lock_|.
111 std::set
<OCSPRequestSession
*> requests_
; // Protected by |lock_|.
112 bool used_
; // Protected by |lock_|.
113 // This should not be modified after |used_|.
114 base::MessageLoopForIO
* io_loop_
; // Protected by |lock_|.
115 base::ThreadChecker thread_checker_
;
117 DISALLOW_COPY_AND_ASSIGN(OCSPIOLoop
);
120 base::LazyInstance
<OCSPIOLoop
>::Leaky
121 g_ocsp_io_loop
= LAZY_INSTANCE_INITIALIZER
;
123 const int kRecvBufferSize
= 4096;
125 // All OCSP handlers should be called in the context of
126 // CertVerifier's thread (i.e. worker pool, not on the I/O thread).
127 // It supports blocking mode only.
129 SECStatus
OCSPCreateSession(const char* host
, PRUint16 portnum
,
130 SEC_HTTP_SERVER_SESSION
* pSession
);
131 SECStatus
OCSPKeepAliveSession(SEC_HTTP_SERVER_SESSION session
,
132 PRPollDesc
**pPollDesc
);
133 SECStatus
OCSPFreeSession(SEC_HTTP_SERVER_SESSION session
);
135 SECStatus
OCSPCreate(SEC_HTTP_SERVER_SESSION session
,
136 const char* http_protocol_variant
,
137 const char* path_and_query_string
,
138 const char* http_request_method
,
139 const PRIntervalTime timeout
,
140 SEC_HTTP_REQUEST_SESSION
* pRequest
);
141 SECStatus
OCSPSetPostData(SEC_HTTP_REQUEST_SESSION request
,
142 const char* http_data
,
143 const PRUint32 http_data_len
,
144 const char* http_content_type
);
145 SECStatus
OCSPAddHeader(SEC_HTTP_REQUEST_SESSION request
,
146 const char* http_header_name
,
147 const char* http_header_value
);
148 SECStatus
OCSPTrySendAndReceive(SEC_HTTP_REQUEST_SESSION request
,
149 PRPollDesc
** pPollDesc
,
150 PRUint16
* http_response_code
,
151 const char** http_response_content_type
,
152 const char** http_response_headers
,
153 const char** http_response_data
,
154 PRUint32
* http_response_data_len
);
155 SECStatus
OCSPFree(SEC_HTTP_REQUEST_SESSION request
);
157 char* GetAlternateOCSPAIAInfo(CERTCertificate
*cert
);
159 class OCSPNSSInitialization
{
161 friend struct base::DefaultLazyInstanceTraits
<OCSPNSSInitialization
>;
163 OCSPNSSInitialization();
164 ~OCSPNSSInitialization();
166 SEC_HttpClientFcn client_fcn_
;
168 DISALLOW_COPY_AND_ASSIGN(OCSPNSSInitialization
);
171 base::LazyInstance
<OCSPNSSInitialization
> g_ocsp_nss_initialization
=
172 LAZY_INSTANCE_INITIALIZER
;
174 // Concrete class for SEC_HTTP_REQUEST_SESSION.
175 // Public methods except virtual methods of URLRequest::Delegate
176 // (On* methods) run on certificate verifier thread (worker thread).
177 // Virtual methods of URLRequest::Delegate and private methods run
179 class OCSPRequestSession
180 : public base::RefCountedThreadSafe
<OCSPRequestSession
>,
181 public URLRequest::Delegate
{
183 OCSPRequestSession(const GURL
& url
,
184 const char* http_request_method
,
185 base::TimeDelta timeout
)
187 http_request_method_(http_request_method
),
190 buffer_(new IOBuffer(kRecvBufferSize
)),
196 void SetPostData(const char* http_data
, PRUint32 http_data_len
,
197 const char* http_content_type
) {
198 // |upload_content_| should not be modified if |request_| is active.
200 upload_content_
.assign(http_data
, http_data_len
);
201 upload_content_type_
.assign(http_content_type
);
204 void AddHeader(const char* http_header_name
, const char* http_header_value
) {
205 extra_request_headers_
.SetHeader(http_header_name
,
210 // At this point, it runs on worker thread.
211 // |io_loop_| was initialized to be NULL in constructor, and
212 // set only in StartURLRequest, so no need to lock |lock_| here.
214 g_ocsp_io_loop
.Get().PostTaskToIOLoop(
216 base::Bind(&OCSPRequestSession::StartURLRequest
, this));
219 bool Started() const {
220 return request_
!= NULL
;
224 // IO thread may set |io_loop_| to NULL, so protect by |lock_|.
225 base::AutoLock
autolock(lock_
);
229 bool Finished() const {
230 base::AutoLock
autolock(lock_
);
235 base::TimeDelta timeout
= timeout_
;
236 base::AutoLock
autolock(lock_
);
238 base::TimeTicks last_time
= base::TimeTicks::Now();
239 cv_
.TimedWait(timeout
);
240 // Check elapsed time
241 base::TimeDelta elapsed_time
= base::TimeTicks::Now() - last_time
;
242 timeout
-= elapsed_time
;
243 if (timeout
< base::TimeDelta()) {
244 VLOG(1) << "OCSP Timed out";
253 const GURL
& url() const {
257 const std::string
& http_request_method() const {
258 return http_request_method_
;
261 base::TimeDelta
timeout() const {
265 PRUint16
http_response_code() const {
267 return response_code_
;
270 const std::string
& http_response_content_type() const {
272 return response_content_type_
;
275 const std::string
& http_response_headers() const {
277 return response_headers_
->raw_headers();
280 const std::string
& http_response_data() const {
285 virtual void OnReceivedRedirect(URLRequest
* request
,
287 bool* defer_redirect
) OVERRIDE
{
288 DCHECK_EQ(request
, request_
);
289 DCHECK_EQ(base::MessageLoopForIO::current(), io_loop_
);
291 if (!new_url
.SchemeIs("http")) {
292 // Prevent redirects to non-HTTP schemes, including HTTPS. This matches
293 // the initial check in OCSPServerSession::CreateRequest().
298 virtual void OnResponseStarted(URLRequest
* request
) OVERRIDE
{
299 DCHECK_EQ(request
, request_
);
300 DCHECK_EQ(base::MessageLoopForIO::current(), io_loop_
);
303 if (request
->status().is_success()) {
304 response_code_
= request_
->GetResponseCode();
305 response_headers_
= request_
->response_headers();
306 response_headers_
->GetMimeType(&response_content_type_
);
307 request_
->Read(buffer_
.get(), kRecvBufferSize
, &bytes_read
);
309 OnReadCompleted(request_
, bytes_read
);
312 virtual void OnReadCompleted(URLRequest
* request
,
313 int bytes_read
) OVERRIDE
{
314 DCHECK_EQ(request
, 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()) {
326 g_ocsp_io_loop
.Get().RemoveRequest(this);
328 base::AutoLock
autolock(lock_
);
333 Release(); // Balanced with StartURLRequest().
337 // Must be called on the IO loop thread.
338 void CancelURLRequest() {
341 base::AutoLock
autolock(lock_
);
343 DCHECK_EQ(base::MessageLoopForIO::current(), io_loop_
);
350 g_ocsp_io_loop
.Get().RemoveRequest(this);
352 base::AutoLock
autolock(lock_
);
357 Release(); // Balanced with StartURLRequest().
362 friend class base::RefCountedThreadSafe
<OCSPRequestSession
>;
364 virtual ~OCSPRequestSession() {
365 // When this destructor is called, there should be only one thread that has
366 // a reference to this object, and so that thread doesn't need to lock
372 // Must call this method while holding |lock_|.
373 void CancelLocked() {
374 lock_
.AssertAcquired();
378 base::Bind(&OCSPRequestSession::CancelURLRequest
, this));
382 // Runs on |g_ocsp_io_loop|'s IO loop.
383 void StartURLRequest() {
386 pthread_mutex_lock(&g_request_context_lock
);
387 URLRequestContext
* url_request_context
= g_request_context
;
388 pthread_mutex_unlock(&g_request_context_lock
);
390 if (url_request_context
== NULL
)
394 base::AutoLock
autolock(lock_
);
396 io_loop_
= base::MessageLoopForIO::current();
397 g_ocsp_io_loop
.Get().AddRequest(this);
401 new URLRequest(url_
, DEFAULT_PRIORITY
, this, url_request_context
);
402 // To meet the privacy requirements of incognito mode.
403 request_
->SetLoadFlags(LOAD_DISABLE_CACHE
| LOAD_DO_NOT_SAVE_COOKIES
|
404 LOAD_DO_NOT_SEND_COOKIES
);
406 if (http_request_method_
== "POST") {
407 DCHECK(!upload_content_
.empty());
408 DCHECK(!upload_content_type_
.empty());
410 request_
->set_method("POST");
411 extra_request_headers_
.SetHeader(
412 HttpRequestHeaders::kContentType
, upload_content_type_
);
414 scoped_ptr
<UploadElementReader
> reader(new UploadBytesElementReader(
415 upload_content_
.data(), upload_content_
.size()));
416 request_
->set_upload(make_scoped_ptr(
417 UploadDataStream::CreateWithReader(reader
.Pass(), 0)));
419 if (!extra_request_headers_
.IsEmpty())
420 request_
->SetExtraRequestHeaders(extra_request_headers_
);
423 AddRef(); // Release after |request_| deleted.
426 GURL url_
; // The URL we eventually wound up at
427 std::string http_request_method_
;
428 base::TimeDelta timeout_
; // The timeout for OCSP
429 URLRequest
* request_
; // The actual request this wraps
430 scoped_refptr
<IOBuffer
> buffer_
; // Read buffer
431 HttpRequestHeaders extra_request_headers_
;
433 // HTTP POST payload. |request_| reads bytes from this.
434 std::string upload_content_
;
435 std::string upload_content_type_
; // MIME type of POST payload
437 int response_code_
; // HTTP status code for the request
438 std::string response_content_type_
;
439 scoped_refptr
<HttpResponseHeaders
> response_headers_
;
440 std::string data_
; // Results of the request
442 // |lock_| protects |finished_| and |io_loop_|.
443 mutable base::Lock lock_
;
444 base::ConditionVariable cv_
;
446 base::MessageLoop
* io_loop_
; // Message loop of the IO thread
449 DISALLOW_COPY_AND_ASSIGN(OCSPRequestSession
);
452 // Concrete class for SEC_HTTP_SERVER_SESSION.
453 class OCSPServerSession
{
455 OCSPServerSession(const char* host
, PRUint16 port
)
456 : host_and_port_(host
, port
) {}
457 ~OCSPServerSession() {}
459 OCSPRequestSession
* CreateRequest(const char* http_protocol_variant
,
460 const char* path_and_query_string
,
461 const char* http_request_method
,
462 const PRIntervalTime timeout
) {
463 // We dont' support "https" because we haven't thought about
464 // whether it's safe to re-enter this code from talking to an OCSP
465 // responder over SSL.
466 if (strcmp(http_protocol_variant
, "http") != 0) {
467 PORT_SetError(PR_NOT_IMPLEMENTED_ERROR
);
471 std::string
url_string(base::StringPrintf(
473 http_protocol_variant
,
474 host_and_port_
.ToString().c_str(),
475 path_and_query_string
));
476 VLOG(1) << "URL [" << url_string
<< "]";
477 GURL
url(url_string
);
479 // NSS does not expose public functions to adjust the fetch timeout when
480 // using libpkix, so hardcode the upper limit for network fetches.
481 base::TimeDelta actual_timeout
= std::min(
482 base::TimeDelta::FromSeconds(kNetworkFetchTimeoutInSecs
),
483 base::TimeDelta::FromMilliseconds(PR_IntervalToMilliseconds(timeout
)));
485 return new OCSPRequestSession(url
, http_request_method
, actual_timeout
);
490 HostPortPair host_and_port_
;
492 DISALLOW_COPY_AND_ASSIGN(OCSPServerSession
);
495 OCSPIOLoop::OCSPIOLoop()
501 OCSPIOLoop::~OCSPIOLoop() {
502 // IO thread was already deleted before the singleton is deleted
505 base::AutoLock
autolock(lock_
);
511 pthread_mutex_lock(&g_request_context_lock
);
512 DCHECK(!g_request_context
);
513 pthread_mutex_unlock(&g_request_context_lock
);
516 void OCSPIOLoop::Shutdown() {
517 // Safe to read outside lock since we only write on IO thread anyway.
518 DCHECK(thread_checker_
.CalledOnValidThread());
520 // Prevent the worker thread from trying to access |io_loop_|.
522 base::AutoLock
autolock(lock_
);
530 pthread_mutex_lock(&g_request_context_lock
);
531 g_request_context
= NULL
;
532 pthread_mutex_unlock(&g_request_context_lock
);
535 void OCSPIOLoop::PostTaskToIOLoop(
536 const tracked_objects::Location
& from_here
, const base::Closure
& task
) {
537 base::AutoLock
autolock(lock_
);
539 io_loop_
->PostTask(from_here
, task
);
542 void OCSPIOLoop::EnsureIOLoop() {
543 base::AutoLock
autolock(lock_
);
544 DCHECK_EQ(base::MessageLoopForIO::current(), io_loop_
);
547 void OCSPIOLoop::AddRequest(OCSPRequestSession
* request
) {
548 DCHECK(!ContainsKey(requests_
, request
));
549 requests_
.insert(request
);
552 void OCSPIOLoop::RemoveRequest(OCSPRequestSession
* request
) {
553 DCHECK(ContainsKey(requests_
, request
));
554 requests_
.erase(request
);
557 void OCSPIOLoop::CancelAllRequests() {
558 // CancelURLRequest() always removes the request from the requests_
559 // set synchronously.
560 while (!requests_
.empty())
561 (*requests_
.begin())->CancelURLRequest();
564 OCSPNSSInitialization::OCSPNSSInitialization() {
565 // NSS calls the functions in the function table to download certificates
566 // or CRLs or talk to OCSP responders over HTTP. These functions must
567 // set an NSS/NSPR error code when they fail. Otherwise NSS will get the
568 // residual error code from an earlier failed function call.
569 client_fcn_
.version
= 1;
570 SEC_HttpClientFcnV1Struct
*ft
= &client_fcn_
.fcnTable
.ftable1
;
571 ft
->createSessionFcn
= OCSPCreateSession
;
572 ft
->keepAliveSessionFcn
= OCSPKeepAliveSession
;
573 ft
->freeSessionFcn
= OCSPFreeSession
;
574 ft
->createFcn
= OCSPCreate
;
575 ft
->setPostDataFcn
= OCSPSetPostData
;
576 ft
->addHeaderFcn
= OCSPAddHeader
;
577 ft
->trySendAndReceiveFcn
= OCSPTrySendAndReceive
;
578 ft
->cancelFcn
= NULL
;
579 ft
->freeFcn
= OCSPFree
;
580 SECStatus status
= SEC_RegisterDefaultHttpClient(&client_fcn_
);
581 if (status
!= SECSuccess
) {
582 NOTREACHED() << "Error initializing OCSP: " << PR_GetError();
585 // Work around NSS bugs 524013 and 564334. NSS incorrectly thinks the
586 // CRLs for Network Solutions Certificate Authority have bad signatures,
587 // which causes certificates issued by that CA to be reported as revoked.
588 // By using OCSP for those certificates, which don't have AIA extensions,
589 // we can work around these bugs. See http://crbug.com/41730.
590 CERT_StringFromCertFcn old_callback
= NULL
;
591 status
= CERT_RegisterAlternateOCSPAIAInfoCallBack(
592 GetAlternateOCSPAIAInfo
, &old_callback
);
593 if (status
== SECSuccess
) {
594 DCHECK(!old_callback
);
596 NOTREACHED() << "Error initializing OCSP: " << PR_GetError();
600 OCSPNSSInitialization::~OCSPNSSInitialization() {
601 SECStatus status
= CERT_RegisterAlternateOCSPAIAInfoCallBack(NULL
, NULL
);
602 if (status
!= SECSuccess
) {
603 LOG(ERROR
) << "Error unregistering OCSP: " << PR_GetError();
608 // OCSP Http Client functions.
609 // Our Http Client functions operate in blocking mode.
610 SECStatus
OCSPCreateSession(const char* host
, PRUint16 portnum
,
611 SEC_HTTP_SERVER_SESSION
* pSession
) {
612 VLOG(1) << "OCSP create session: host=" << host
<< " port=" << portnum
;
613 pthread_mutex_lock(&g_request_context_lock
);
614 URLRequestContext
* request_context
= g_request_context
;
615 pthread_mutex_unlock(&g_request_context_lock
);
616 if (request_context
== NULL
) {
617 LOG(ERROR
) << "No URLRequestContext for NSS HTTP handler. host: " << host
;
618 // The application failed to call SetURLRequestContextForNSSHttpIO or
619 // has already called ShutdownNSSHttpIO, so we can't create and use
620 // URLRequest. PR_NOT_IMPLEMENTED_ERROR is not an accurate error
621 // code for these error conditions, but is close enough.
622 PORT_SetError(PR_NOT_IMPLEMENTED_ERROR
);
625 *pSession
= new OCSPServerSession(host
, portnum
);
629 SECStatus
OCSPKeepAliveSession(SEC_HTTP_SERVER_SESSION session
,
630 PRPollDesc
**pPollDesc
) {
631 VLOG(1) << "OCSP keep alive";
637 SECStatus
OCSPFreeSession(SEC_HTTP_SERVER_SESSION session
) {
638 VLOG(1) << "OCSP free session";
639 delete reinterpret_cast<OCSPServerSession
*>(session
);
643 SECStatus
OCSPCreate(SEC_HTTP_SERVER_SESSION session
,
644 const char* http_protocol_variant
,
645 const char* path_and_query_string
,
646 const char* http_request_method
,
647 const PRIntervalTime timeout
,
648 SEC_HTTP_REQUEST_SESSION
* pRequest
) {
649 VLOG(1) << "OCSP create protocol=" << http_protocol_variant
650 << " path_and_query=" << path_and_query_string
651 << " http_request_method=" << http_request_method
652 << " timeout=" << timeout
;
653 OCSPServerSession
* ocsp_session
=
654 reinterpret_cast<OCSPServerSession
*>(session
);
656 OCSPRequestSession
* req
= ocsp_session
->CreateRequest(http_protocol_variant
,
657 path_and_query_string
,
660 SECStatus rv
= SECFailure
;
662 req
->AddRef(); // Release in OCSPFree().
669 SECStatus
OCSPSetPostData(SEC_HTTP_REQUEST_SESSION request
,
670 const char* http_data
,
671 const PRUint32 http_data_len
,
672 const char* http_content_type
) {
673 VLOG(1) << "OCSP set post data len=" << http_data_len
;
674 OCSPRequestSession
* req
= reinterpret_cast<OCSPRequestSession
*>(request
);
676 req
->SetPostData(http_data
, http_data_len
, http_content_type
);
680 SECStatus
OCSPAddHeader(SEC_HTTP_REQUEST_SESSION request
,
681 const char* http_header_name
,
682 const char* http_header_value
) {
683 VLOG(1) << "OCSP add header name=" << http_header_name
684 << " value=" << http_header_value
;
685 OCSPRequestSession
* req
= reinterpret_cast<OCSPRequestSession
*>(request
);
687 req
->AddHeader(http_header_name
, http_header_value
);
691 // Sets response of |req| in the output parameters.
692 // It is helper routine for OCSP trySendAndReceiveFcn.
693 // |http_response_data_len| could be used as input parameter. If it has
694 // non-zero value, it is considered as maximum size of |http_response_data|.
695 SECStatus
OCSPSetResponse(OCSPRequestSession
* req
,
696 PRUint16
* http_response_code
,
697 const char** http_response_content_type
,
698 const char** http_response_headers
,
699 const char** http_response_data
,
700 PRUint32
* http_response_data_len
) {
701 DCHECK(req
->Finished());
702 const std::string
& data
= req
->http_response_data();
703 if (http_response_data_len
&& *http_response_data_len
) {
704 if (*http_response_data_len
< data
.size()) {
705 LOG(ERROR
) << "response body too large: " << *http_response_data_len
706 << " < " << data
.size();
707 *http_response_data_len
= data
.size();
708 PORT_SetError(SEC_ERROR_BAD_HTTP_RESPONSE
);
712 VLOG(1) << "OCSP response "
713 << " response_code=" << req
->http_response_code()
714 << " content_type=" << req
->http_response_content_type()
715 << " header=" << req
->http_response_headers()
716 << " data_len=" << data
.size();
717 if (http_response_code
)
718 *http_response_code
= req
->http_response_code();
719 if (http_response_content_type
)
720 *http_response_content_type
= req
->http_response_content_type().c_str();
721 if (http_response_headers
)
722 *http_response_headers
= req
->http_response_headers().c_str();
723 if (http_response_data
)
724 *http_response_data
= data
.data();
725 if (http_response_data_len
)
726 *http_response_data_len
= data
.size();
730 SECStatus
OCSPTrySendAndReceive(SEC_HTTP_REQUEST_SESSION request
,
731 PRPollDesc
** pPollDesc
,
732 PRUint16
* http_response_code
,
733 const char** http_response_content_type
,
734 const char** http_response_headers
,
735 const char** http_response_data
,
736 PRUint32
* http_response_data_len
) {
737 if (http_response_data_len
) {
738 // We must always set an output value, even on failure. The output value 0
739 // means the failure was unrelated to the acceptable response data length.
740 *http_response_data_len
= 0;
743 VLOG(1) << "OCSP try send and receive";
744 OCSPRequestSession
* req
= reinterpret_cast<OCSPRequestSession
*>(request
);
745 // We support blocking mode only.
749 if (req
->Started() || req
->Finished()) {
750 // We support blocking mode only, so this function shouldn't be called
751 // again when req has stareted or finished.
753 PORT_SetError(SEC_ERROR_BAD_HTTP_RESPONSE
); // Simple approximation.
757 const base::Time start_time
= base::Time::Now();
758 bool request_ok
= true;
760 if (!req
->Wait() || req
->http_response_code() == static_cast<PRUint16
>(-1)) {
761 // If the response code is -1, the request failed and there is no response.
764 const base::TimeDelta duration
= base::Time::Now() - start_time
;
766 // For metrics, we want to know if the request was 'successful' or not.
767 // |request_ok| determines if we'll pass the response back to NSS and |ok|
768 // keep track of if we think the response was good.
771 (req
->http_response_code() >= 400 && req
->http_response_code() < 600) ||
772 req
->http_response_data().size() == 0 ||
773 // 0x30 is the ASN.1 DER encoding of a SEQUENCE. All valid OCSP/CRL/CRT
774 // responses must start with this. If we didn't check for this then a
775 // captive portal could provide an HTML reply that we would count as a
776 // 'success' (although it wouldn't count in NSS, of course).
777 req
->http_response_data().data()[0] != 0x30) {
781 // We want to know if this was:
782 // 1) An OCSP request
784 // 3) A request for a missing intermediate certificate
785 // There's no sure way to do this, so we use heuristics like MIME type and
787 const char* mime_type
= "";
789 mime_type
= req
->http_response_content_type().c_str();
791 strcasecmp(mime_type
, "application/ocsp-response") == 0;
792 bool is_crl
= strcasecmp(mime_type
, "application/x-pkcs7-crl") == 0 ||
793 strcasecmp(mime_type
, "application/x-x509-crl") == 0 ||
794 strcasecmp(mime_type
, "application/pkix-crl") == 0;
796 strcasecmp(mime_type
, "application/x-x509-ca-cert") == 0 ||
797 strcasecmp(mime_type
, "application/x-x509-server-cert") == 0 ||
798 strcasecmp(mime_type
, "application/pkix-cert") == 0 ||
799 strcasecmp(mime_type
, "application/pkcs7-mime") == 0;
801 if (!is_cert
&& !is_crl
&& !is_ocsp
) {
802 // We didn't get a hint from the MIME type, so do the best that we can.
803 const std::string path
= req
->url().path();
804 const std::string host
= req
->url().host();
805 is_crl
= strcasestr(path
.c_str(), ".crl") != NULL
;
806 is_cert
= strcasestr(path
.c_str(), ".crt") != NULL
||
807 strcasestr(path
.c_str(), ".p7c") != NULL
||
808 strcasestr(path
.c_str(), ".cer") != NULL
;
809 is_ocsp
= strcasestr(host
.c_str(), "ocsp") != NULL
||
810 req
->http_request_method() == "POST";
815 UMA_HISTOGRAM_TIMES("Net.OCSPRequestTimeMs", duration
);
816 UMA_HISTOGRAM_BOOLEAN("Net.OCSPRequestSuccess", true);
818 UMA_HISTOGRAM_TIMES("Net.OCSPRequestFailedTimeMs", duration
);
819 UMA_HISTOGRAM_BOOLEAN("Net.OCSPRequestSuccess", false);
823 UMA_HISTOGRAM_TIMES("Net.CRLRequestTimeMs", duration
);
824 UMA_HISTOGRAM_BOOLEAN("Net.CRLRequestSuccess", true);
826 UMA_HISTOGRAM_TIMES("Net.CRLRequestFailedTimeMs", duration
);
827 UMA_HISTOGRAM_BOOLEAN("Net.CRLRequestSuccess", false);
829 } else if (is_cert
) {
831 UMA_HISTOGRAM_TIMES("Net.CRTRequestTimeMs", duration
);
834 UMA_HISTOGRAM_TIMES("Net.UnknownTypeRequestTimeMs", duration
);
838 PORT_SetError(SEC_ERROR_BAD_HTTP_RESPONSE
); // Simple approximation.
842 return OCSPSetResponse(
843 req
, http_response_code
,
844 http_response_content_type
,
845 http_response_headers
,
847 http_response_data_len
);
850 SECStatus
OCSPFree(SEC_HTTP_REQUEST_SESSION request
) {
851 VLOG(1) << "OCSP free";
852 OCSPRequestSession
* req
= reinterpret_cast<OCSPRequestSession
*>(request
);
858 // Data for GetAlternateOCSPAIAInfo.
860 // CN=Network Solutions Certificate Authority,O=Network Solutions L.L.C.,C=US
862 // There are two CAs with this name. Their key IDs are listed next.
863 const unsigned char network_solutions_ca_name
[] = {
864 0x30, 0x62, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04,
865 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x21, 0x30, 0x1f, 0x06,
866 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x4e, 0x65, 0x74, 0x77,
867 0x6f, 0x72, 0x6b, 0x20, 0x53, 0x6f, 0x6c, 0x75, 0x74, 0x69,
868 0x6f, 0x6e, 0x73, 0x20, 0x4c, 0x2e, 0x4c, 0x2e, 0x43, 0x2e,
869 0x31, 0x30, 0x30, 0x2e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
870 0x27, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x53,
871 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x43,
872 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65,
873 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79
875 const unsigned int network_solutions_ca_name_len
= 100;
877 // This CA is an intermediate CA, subordinate to UTN-USERFirst-Hardware.
878 const unsigned char network_solutions_ca_key_id
[] = {
879 0x3c, 0x41, 0xe2, 0x8f, 0x08, 0x08, 0xa9, 0x4c, 0x25, 0x89,
880 0x8d, 0x6d, 0xc5, 0x38, 0xd0, 0xfc, 0x85, 0x8c, 0x62, 0x17
882 const unsigned int network_solutions_ca_key_id_len
= 20;
884 // This CA is a root CA. It is also cross-certified by
885 // UTN-USERFirst-Hardware.
886 const unsigned char network_solutions_ca_key_id2
[] = {
887 0x21, 0x30, 0xc9, 0xfb, 0x00, 0xd7, 0x4e, 0x98, 0xda, 0x87,
888 0xaa, 0x2a, 0xd0, 0xa7, 0x2e, 0xb1, 0x40, 0x31, 0xa7, 0x4c
890 const unsigned int network_solutions_ca_key_id2_len
= 20;
892 // An entry in our OCSP responder table. |issuer| and |issuer_key_id| are
893 // the key. |ocsp_url| is the value.
894 struct OCSPResponderTableEntry
{
896 SECItem issuer_key_id
;
897 const char *ocsp_url
;
900 const OCSPResponderTableEntry g_ocsp_responder_table
[] = {
904 const_cast<unsigned char*>(network_solutions_ca_name
),
905 network_solutions_ca_name_len
909 const_cast<unsigned char*>(network_solutions_ca_key_id
),
910 network_solutions_ca_key_id_len
912 "http://ocsp.netsolssl.com"
917 const_cast<unsigned char*>(network_solutions_ca_name
),
918 network_solutions_ca_name_len
922 const_cast<unsigned char*>(network_solutions_ca_key_id2
),
923 network_solutions_ca_key_id2_len
925 "http://ocsp.netsolssl.com"
929 char* GetAlternateOCSPAIAInfo(CERTCertificate
*cert
) {
930 if (cert
&& !cert
->isRoot
&& cert
->authKeyID
) {
931 for (unsigned int i
=0; i
< arraysize(g_ocsp_responder_table
); i
++) {
932 if (SECITEM_CompareItem(&g_ocsp_responder_table
[i
].issuer
,
933 &cert
->derIssuer
) == SECEqual
&&
934 SECITEM_CompareItem(&g_ocsp_responder_table
[i
].issuer_key_id
,
935 &cert
->authKeyID
->keyID
) == SECEqual
) {
936 return PORT_Strdup(g_ocsp_responder_table
[i
].ocsp_url
);
944 } // anonymous namespace
946 void SetMessageLoopForNSSHttpIO() {
947 // Must have a MessageLoopForIO.
948 DCHECK(base::MessageLoopForIO::current());
950 bool used
= g_ocsp_io_loop
.Get().used();
952 // Should not be called when g_ocsp_io_loop has already been used.
956 void EnsureNSSHttpIOInit() {
957 g_ocsp_io_loop
.Get().StartUsing();
958 g_ocsp_nss_initialization
.Get();
961 void ShutdownNSSHttpIO() {
962 g_ocsp_io_loop
.Get().Shutdown();
965 void ResetNSSHttpIOForTesting() {
966 g_ocsp_io_loop
.Get().ReuseForTesting();
969 // This function would be called before NSS initialization.
970 void SetURLRequestContextForNSSHttpIO(URLRequestContext
* request_context
) {
971 pthread_mutex_lock(&g_request_context_lock
);
972 if (request_context
) {
973 DCHECK(!g_request_context
);
975 g_request_context
= request_context
;
976 pthread_mutex_unlock(&g_request_context_lock
);