Lots of random cleanups, mostly for native_theme_win.cc:
[chromium-blink-merge.git] / net / ocsp / nss_ocsp.cc
blob33d1933f348ebbae152f5f5be3404e5f7e82958d
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"
7 #include <certt.h>
8 #include <certdb.h>
9 #include <ocsp.h>
10 #include <nspr.h>
11 #include <nss.h>
12 #include <pthread.h>
13 #include <secerr.h>
15 #include <algorithm>
16 #include <string>
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"
42 #include "url/gurl.h"
44 namespace net {
46 namespace {
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;
58 class OCSPIOLoop {
59 public:
60 void StartUsing() {
61 base::AutoLock autolock(lock_);
62 used_ = true;
63 io_loop_ = base::MessageLoopForIO::current();
64 DCHECK(io_loop_);
67 // Called on IO loop.
68 void Shutdown();
70 bool used() const {
71 base::AutoLock autolock(lock_);
72 return used_;
75 // Called from worker thread.
76 void PostTaskToIOLoop(const tracked_objects::Location& from_here,
77 const base::Closure& task);
79 void EnsureIOLoop();
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());
95 shutdown_ = false;
96 used_ = false;
98 StartUsing();
101 private:
102 friend struct base::DefaultLazyInstanceTraits<OCSPIOLoop>;
104 OCSPIOLoop();
105 ~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 {
160 private:
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
178 // on IO thread.
179 class OCSPRequestSession
180 : public base::RefCountedThreadSafe<OCSPRequestSession>,
181 public URLRequest::Delegate {
182 public:
183 OCSPRequestSession(const GURL& url,
184 const char* http_request_method,
185 base::TimeDelta timeout)
186 : url_(url),
187 http_request_method_(http_request_method),
188 timeout_(timeout),
189 request_(NULL),
190 buffer_(new IOBuffer(kRecvBufferSize)),
191 response_code_(-1),
192 cv_(&lock_),
193 io_loop_(NULL),
194 finished_(false) {}
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.
199 DCHECK(!request_);
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,
206 http_header_value);
209 void Start() {
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.
213 DCHECK(!io_loop_);
214 g_ocsp_io_loop.Get().PostTaskToIOLoop(
215 FROM_HERE,
216 base::Bind(&OCSPRequestSession::StartURLRequest, this));
219 bool Started() const {
220 return request_ != NULL;
223 void Cancel() {
224 // IO thread may set |io_loop_| to NULL, so protect by |lock_|.
225 base::AutoLock autolock(lock_);
226 CancelLocked();
229 bool Finished() const {
230 base::AutoLock autolock(lock_);
231 return finished_;
234 bool Wait() {
235 base::TimeDelta timeout = timeout_;
236 base::AutoLock autolock(lock_);
237 while (!finished_) {
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";
245 if (!finished_)
246 CancelLocked();
247 break;
250 return finished_;
253 const GURL& url() const {
254 return url_;
257 const std::string& http_request_method() const {
258 return http_request_method_;
261 base::TimeDelta timeout() const {
262 return timeout_;
265 PRUint16 http_response_code() const {
266 DCHECK(finished_);
267 return response_code_;
270 const std::string& http_response_content_type() const {
271 DCHECK(finished_);
272 return response_content_type_;
275 const std::string& http_response_headers() const {
276 DCHECK(finished_);
277 return response_headers_->raw_headers();
280 const std::string& http_response_data() const {
281 DCHECK(finished_);
282 return data_;
285 virtual void OnReceivedRedirect(URLRequest* request,
286 const GURL& new_url,
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().
294 CancelURLRequest();
298 virtual void OnResponseStarted(URLRequest* request) OVERRIDE {
299 DCHECK_EQ(request, request_);
300 DCHECK_EQ(base::MessageLoopForIO::current(), io_loop_);
302 int bytes_read = 0;
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_);
317 do {
318 if (!request_->status().is_success() || bytes_read <= 0)
319 break;
320 data_.append(buffer_->data(), bytes_read);
321 } while (request_->Read(buffer_.get(), kRecvBufferSize, &bytes_read));
323 if (!request_->status().is_io_pending()) {
324 delete request_;
325 request_ = NULL;
326 g_ocsp_io_loop.Get().RemoveRequest(this);
328 base::AutoLock autolock(lock_);
329 finished_ = true;
330 io_loop_ = NULL;
332 cv_.Signal();
333 Release(); // Balanced with StartURLRequest().
337 // Must be called on the IO loop thread.
338 void CancelURLRequest() {
339 #ifndef NDEBUG
341 base::AutoLock autolock(lock_);
342 if (io_loop_)
343 DCHECK_EQ(base::MessageLoopForIO::current(), io_loop_);
345 #endif
346 if (request_) {
347 request_->Cancel();
348 delete request_;
349 request_ = NULL;
350 g_ocsp_io_loop.Get().RemoveRequest(this);
352 base::AutoLock autolock(lock_);
353 finished_ = true;
354 io_loop_ = NULL;
356 cv_.Signal();
357 Release(); // Balanced with StartURLRequest().
361 private:
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
367 // |lock_| here.
368 DCHECK(!request_);
369 DCHECK(!io_loop_);
372 // Must call this method while holding |lock_|.
373 void CancelLocked() {
374 lock_.AssertAcquired();
375 if (io_loop_) {
376 io_loop_->PostTask(
377 FROM_HERE,
378 base::Bind(&OCSPRequestSession::CancelURLRequest, this));
382 // Runs on |g_ocsp_io_loop|'s IO loop.
383 void StartURLRequest() {
384 DCHECK(!request_);
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)
391 return;
394 base::AutoLock autolock(lock_);
395 DCHECK(!io_loop_);
396 io_loop_ = base::MessageLoopForIO::current();
397 g_ocsp_io_loop.Get().AddRequest(this);
400 request_ =
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_);
422 request_->Start();
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
447 bool finished_;
449 DISALLOW_COPY_AND_ASSIGN(OCSPRequestSession);
452 // Concrete class for SEC_HTTP_SERVER_SESSION.
453 class OCSPServerSession {
454 public:
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);
468 return NULL;
471 std::string url_string(base::StringPrintf(
472 "%s://%s%s",
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);
489 private:
490 HostPortPair host_and_port_;
492 DISALLOW_COPY_AND_ASSIGN(OCSPServerSession);
495 OCSPIOLoop::OCSPIOLoop()
496 : shutdown_(false),
497 used_(false),
498 io_loop_(NULL) {
501 OCSPIOLoop::~OCSPIOLoop() {
502 // IO thread was already deleted before the singleton is deleted
503 // in AtExitManager.
505 base::AutoLock autolock(lock_);
506 DCHECK(!io_loop_);
507 DCHECK(!used_);
508 DCHECK(shutdown_);
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_);
523 io_loop_ = NULL;
524 used_ = false;
525 shutdown_ = true;
528 CancelAllRequests();
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_);
538 if (io_loop_)
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);
595 } else {
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);
623 return SECFailure;
625 *pSession = new OCSPServerSession(host, portnum);
626 return SECSuccess;
629 SECStatus OCSPKeepAliveSession(SEC_HTTP_SERVER_SESSION session,
630 PRPollDesc **pPollDesc) {
631 VLOG(1) << "OCSP keep alive";
632 if (pPollDesc)
633 *pPollDesc = NULL;
634 return SECSuccess;
637 SECStatus OCSPFreeSession(SEC_HTTP_SERVER_SESSION session) {
638 VLOG(1) << "OCSP free session";
639 delete reinterpret_cast<OCSPServerSession*>(session);
640 return SECSuccess;
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,
658 http_request_method,
659 timeout);
660 SECStatus rv = SECFailure;
661 if (req) {
662 req->AddRef(); // Release in OCSPFree().
663 rv = SECSuccess;
665 *pRequest = req;
666 return rv;
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);
677 return SECSuccess;
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);
688 return SECSuccess;
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);
709 return SECFailure;
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();
727 return SECSuccess;
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.
746 if (pPollDesc)
747 *pPollDesc = NULL;
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.
752 NOTREACHED();
753 PORT_SetError(SEC_ERROR_BAD_HTTP_RESPONSE); // Simple approximation.
754 return SECFailure;
757 const base::Time start_time = base::Time::Now();
758 bool request_ok = true;
759 req->Start();
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.
762 request_ok = false;
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.
769 bool ok = true;
770 if (!request_ok ||
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) {
778 ok = false;
781 // We want to know if this was:
782 // 1) An OCSP request
783 // 2) A CRL 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
786 // URL.
787 const char* mime_type = "";
788 if (ok)
789 mime_type = req->http_response_content_type().c_str();
790 bool is_ocsp =
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;
795 bool is_cert =
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";
813 if (is_ocsp) {
814 if (ok) {
815 UMA_HISTOGRAM_TIMES("Net.OCSPRequestTimeMs", duration);
816 UMA_HISTOGRAM_BOOLEAN("Net.OCSPRequestSuccess", true);
817 } else {
818 UMA_HISTOGRAM_TIMES("Net.OCSPRequestFailedTimeMs", duration);
819 UMA_HISTOGRAM_BOOLEAN("Net.OCSPRequestSuccess", false);
821 } else if (is_crl) {
822 if (ok) {
823 UMA_HISTOGRAM_TIMES("Net.CRLRequestTimeMs", duration);
824 UMA_HISTOGRAM_BOOLEAN("Net.CRLRequestSuccess", true);
825 } else {
826 UMA_HISTOGRAM_TIMES("Net.CRLRequestFailedTimeMs", duration);
827 UMA_HISTOGRAM_BOOLEAN("Net.CRLRequestSuccess", false);
829 } else if (is_cert) {
830 if (ok)
831 UMA_HISTOGRAM_TIMES("Net.CRTRequestTimeMs", duration);
832 } else {
833 if (ok)
834 UMA_HISTOGRAM_TIMES("Net.UnknownTypeRequestTimeMs", duration);
837 if (!request_ok) {
838 PORT_SetError(SEC_ERROR_BAD_HTTP_RESPONSE); // Simple approximation.
839 return SECFailure;
842 return OCSPSetResponse(
843 req, http_response_code,
844 http_response_content_type,
845 http_response_headers,
846 http_response_data,
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);
853 req->Cancel();
854 req->Release();
855 return SECSuccess;
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 {
895 SECItem issuer;
896 SECItem issuer_key_id;
897 const char *ocsp_url;
900 const OCSPResponderTableEntry g_ocsp_responder_table[] = {
903 siBuffer,
904 const_cast<unsigned char*>(network_solutions_ca_name),
905 network_solutions_ca_name_len
908 siBuffer,
909 const_cast<unsigned char*>(network_solutions_ca_key_id),
910 network_solutions_ca_key_id_len
912 "http://ocsp.netsolssl.com"
916 siBuffer,
917 const_cast<unsigned char*>(network_solutions_ca_name),
918 network_solutions_ca_name_len
921 siBuffer,
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);
941 return NULL;
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.
953 DCHECK(!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);
979 } // namespace net