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.
4
5 #include "net/ocsp/nss_ocsp.h"
6
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>
14
15 #include <algorithm>
16 #include <string>
17
18 #include "base/basictypes.h"
19 #include "base/callback.h"
20 #include "base/compiler_specific.h"
21 #include "base/lazy_instance.h"
22 #include "base/logging.h"
23 #include "base/memory/scoped_ptr.h"
24 #include "base/message_loop/message_loop.h"
25 #include "base/metrics/histogram.h"
26 #include "base/stl_util.h"
27 #include "base/strings/string_util.h"
28 #include "base/strings/stringprintf.h"
29 #include "base/synchronization/condition_variable.h"
30 #include "base/synchronization/lock.h"
31 #include "base/threading/thread_checker.h"
32 #include "base/time/time.h"
33 #include "net/base/host_port_pair.h"
34 #include "net/base/io_buffer.h"
35 #include "net/base/load_flags.h"
36 #include "net/base/request_priority.h"
37 #include "net/base/upload_bytes_element_reader.h"
38 #include "net/base/upload_data_stream.h"
39 #include "net/http/http_request_headers.h"
40 #include "net/http/http_response_headers.h"
41 #include "net/url_request/redirect_info.h"
42 #include "net/url_request/url_request.h"
43 #include "net/url_request/url_request_context.h"
44 #include "url/gurl.h"
45
46 namespace net {
47
48 namespace {
49
50 // Protects |g_request_context|.
51 pthread_mutex_t g_request_context_lock = PTHREAD_MUTEX_INITIALIZER;
52 URLRequestContext* g_request_context = NULL;
53
54 // The default timeout for network fetches in NSS is 60 seconds. Choose a
55 // saner upper limit for OCSP/CRL/AIA fetches.
56 const int kNetworkFetchTimeoutInSecs = 15;
57
58 class OCSPRequestSession;
59
60 class OCSPIOLoop {
61 public:
StartUsing()62 void StartUsing() {
63 base::AutoLock autolock(lock_);
64 used_ = true;
65 io_loop_ = base::MessageLoopForIO::current();
66 DCHECK(io_loop_);
67 }
68
69 // Called on IO loop.
70 void Shutdown();
71
used() const72 bool used() const {
73 base::AutoLock autolock(lock_);
74 return used_;
75 }
76
77 // Called from worker thread.
78 void PostTaskToIOLoop(const tracked_objects::Location& from_here,
79 const base::Closure& task);
80
81 void EnsureIOLoop();
82
83 void AddRequest(OCSPRequestSession* request);
84 void RemoveRequest(OCSPRequestSession* request);
85
86 // Clears internal state and calls |StartUsing()|. Should be called only in
87 // the context of testing.
ReuseForTesting()88 void ReuseForTesting() {
89 {
90 base::AutoLock autolock(lock_);
91 DCHECK(base::MessageLoopForIO::current());
92 thread_checker_.DetachFromThread();
93
94 // CalledOnValidThread is the only available API to reassociate
95 // thread_checker_ with the current thread. Result ignored intentionally.
96 ignore_result(thread_checker_.CalledOnValidThread());
97 shutdown_ = false;
98 used_ = false;
99 }
100 StartUsing();
101 }
102
103 private:
104 friend struct base::DefaultLazyInstanceTraits<OCSPIOLoop>;
105
106 OCSPIOLoop();
107 ~OCSPIOLoop();
108
109 void CancelAllRequests();
110
111 mutable base::Lock lock_;
112 bool shutdown_; // Protected by |lock_|.
113 std::set<OCSPRequestSession*> requests_; // Protected by |lock_|.
114 bool used_; // Protected by |lock_|.
115 // This should not be modified after |used_|.
116 base::MessageLoopForIO* io_loop_; // Protected by |lock_|.
117 base::ThreadChecker thread_checker_;
118
119 DISALLOW_COPY_AND_ASSIGN(OCSPIOLoop);
120 };
121
122 base::LazyInstance<OCSPIOLoop>::Leaky
123 g_ocsp_io_loop = LAZY_INSTANCE_INITIALIZER;
124
125 const int kRecvBufferSize = 4096;
126
127 // All OCSP handlers should be called in the context of
128 // CertVerifier's thread (i.e. worker pool, not on the I/O thread).
129 // It supports blocking mode only.
130
131 SECStatus OCSPCreateSession(const char* host, PRUint16 portnum,
132 SEC_HTTP_SERVER_SESSION* pSession);
133 SECStatus OCSPKeepAliveSession(SEC_HTTP_SERVER_SESSION session,
134 PRPollDesc **pPollDesc);
135 SECStatus OCSPFreeSession(SEC_HTTP_SERVER_SESSION session);
136
137 SECStatus OCSPCreate(SEC_HTTP_SERVER_SESSION session,
138 const char* http_protocol_variant,
139 const char* path_and_query_string,
140 const char* http_request_method,
141 const PRIntervalTime timeout,
142 SEC_HTTP_REQUEST_SESSION* pRequest);
143 SECStatus OCSPSetPostData(SEC_HTTP_REQUEST_SESSION request,
144 const char* http_data,
145 const PRUint32 http_data_len,
146 const char* http_content_type);
147 SECStatus OCSPAddHeader(SEC_HTTP_REQUEST_SESSION request,
148 const char* http_header_name,
149 const char* http_header_value);
150 SECStatus OCSPTrySendAndReceive(SEC_HTTP_REQUEST_SESSION request,
151 PRPollDesc** pPollDesc,
152 PRUint16* http_response_code,
153 const char** http_response_content_type,
154 const char** http_response_headers,
155 const char** http_response_data,
156 PRUint32* http_response_data_len);
157 SECStatus OCSPFree(SEC_HTTP_REQUEST_SESSION request);
158
159 char* GetAlternateOCSPAIAInfo(CERTCertificate *cert);
160
161 class OCSPNSSInitialization {
162 private:
163 friend struct base::DefaultLazyInstanceTraits<OCSPNSSInitialization>;
164
165 OCSPNSSInitialization();
166 ~OCSPNSSInitialization();
167
168 SEC_HttpClientFcn client_fcn_;
169
170 DISALLOW_COPY_AND_ASSIGN(OCSPNSSInitialization);
171 };
172
173 base::LazyInstance<OCSPNSSInitialization> g_ocsp_nss_initialization =
174 LAZY_INSTANCE_INITIALIZER;
175
176 // Concrete class for SEC_HTTP_REQUEST_SESSION.
177 // Public methods except virtual methods of URLRequest::Delegate
178 // (On* methods) run on certificate verifier thread (worker thread).
179 // Virtual methods of URLRequest::Delegate and private methods run
180 // on IO thread.
181 class OCSPRequestSession
182 : public base::RefCountedThreadSafe<OCSPRequestSession>,
183 public URLRequest::Delegate {
184 public:
OCSPRequestSession(const GURL & url,const char * http_request_method,base::TimeDelta timeout)185 OCSPRequestSession(const GURL& url,
186 const char* http_request_method,
187 base::TimeDelta timeout)
188 : url_(url),
189 http_request_method_(http_request_method),
190 timeout_(timeout),
191 buffer_(new IOBuffer(kRecvBufferSize)),
192 response_code_(-1),
193 cv_(&lock_),
194 io_loop_(NULL),
195 finished_(false) {}
196
SetPostData(const char * http_data,PRUint32 http_data_len,const char * http_content_type)197 void SetPostData(const char* http_data, PRUint32 http_data_len,
198 const char* http_content_type) {
199 // |upload_content_| should not be modified if |request_| is active.
200 DCHECK(!request_);
201 upload_content_.assign(http_data, http_data_len);
202 upload_content_type_.assign(http_content_type);
203 }
204
AddHeader(const char * http_header_name,const char * http_header_value)205 void AddHeader(const char* http_header_name, const char* http_header_value) {
206 extra_request_headers_.SetHeader(http_header_name,
207 http_header_value);
208 }
209
Start()210 void Start() {
211 // At this point, it runs on worker thread.
212 // |io_loop_| was initialized to be NULL in constructor, and
213 // set only in StartURLRequest, so no need to lock |lock_| here.
214 DCHECK(!io_loop_);
215 g_ocsp_io_loop.Get().PostTaskToIOLoop(
216 FROM_HERE,
217 base::Bind(&OCSPRequestSession::StartURLRequest, this));
218 }
219
Started() const220 bool Started() const {
221 return request_.get() != NULL;
222 }
223
Cancel()224 void Cancel() {
225 // IO thread may set |io_loop_| to NULL, so protect by |lock_|.
226 base::AutoLock autolock(lock_);
227 CancelLocked();
228 }
229
Finished() const230 bool Finished() const {
231 base::AutoLock autolock(lock_);
232 return finished_;
233 }
234
Wait()235 bool Wait() {
236 base::TimeDelta timeout = timeout_;
237 base::AutoLock autolock(lock_);
238 while (!finished_) {
239 base::TimeTicks last_time = base::TimeTicks::Now();
240 cv_.TimedWait(timeout);
241 // Check elapsed time
242 base::TimeDelta elapsed_time = base::TimeTicks::Now() - last_time;
243 timeout -= elapsed_time;
244 if (timeout < base::TimeDelta()) {
245 VLOG(1) << "OCSP Timed out";
246 if (!finished_)
247 CancelLocked();
248 break;
249 }
250 }
251 return finished_;
252 }
253
url() const254 const GURL& url() const {
255 return url_;
256 }
257
http_request_method() const258 const std::string& http_request_method() const {
259 return http_request_method_;
260 }
261
timeout() const262 base::TimeDelta timeout() const {
263 return timeout_;
264 }
265
http_response_code() const266 PRUint16 http_response_code() const {
267 DCHECK(finished_);
268 return response_code_;
269 }
270
http_response_content_type() const271 const std::string& http_response_content_type() const {
272 DCHECK(finished_);
273 return response_content_type_;
274 }
275
http_response_headers() const276 const std::string& http_response_headers() const {
277 DCHECK(finished_);
278 return response_headers_->raw_headers();
279 }
280
http_response_data() const281 const std::string& http_response_data() const {
282 DCHECK(finished_);
283 return data_;
284 }
285
OnReceivedRedirect(URLRequest * request,const RedirectInfo & redirect_info,bool * defer_redirect)286 virtual void OnReceivedRedirect(URLRequest* request,
287 const RedirectInfo& redirect_info,
288 bool* defer_redirect) OVERRIDE {
289 DCHECK_EQ(request_.get(), request);
290 DCHECK_EQ(base::MessageLoopForIO::current(), io_loop_);
291
292 if (!redirect_info.new_url.SchemeIs("http")) {
293 // Prevent redirects to non-HTTP schemes, including HTTPS. This matches
294 // the initial check in OCSPServerSession::CreateRequest().
295 CancelURLRequest();
296 }
297 }
298
OnResponseStarted(URLRequest * request)299 virtual void OnResponseStarted(URLRequest* request) OVERRIDE {
300 DCHECK_EQ(request_.get(), request);
301 DCHECK_EQ(base::MessageLoopForIO::current(), io_loop_);
302
303 int bytes_read = 0;
304 if (request->status().is_success()) {
305 response_code_ = request_->GetResponseCode();
306 response_headers_ = request_->response_headers();
307 response_headers_->GetMimeType(&response_content_type_);
308 request_->Read(buffer_.get(), kRecvBufferSize, &bytes_read);
309 }
310 OnReadCompleted(request_.get(), bytes_read);
311 }
312
OnReadCompleted(URLRequest * request,int bytes_read)313 virtual void OnReadCompleted(URLRequest* request,
314 int bytes_read) OVERRIDE {
315 DCHECK_EQ(request_.get(), request);
316 DCHECK_EQ(base::MessageLoopForIO::current(), io_loop_);
317
318 do {
319 if (!request_->status().is_success() || bytes_read <= 0)
320 break;
321 data_.append(buffer_->data(), bytes_read);
322 } while (request_->Read(buffer_.get(), kRecvBufferSize, &bytes_read));
323
324 if (!request_->status().is_io_pending()) {
325 request_.reset();
326 g_ocsp_io_loop.Get().RemoveRequest(this);
327 {
328 base::AutoLock autolock(lock_);
329 finished_ = true;
330 io_loop_ = NULL;
331 }
332 cv_.Signal();
333 Release(); // Balanced with StartURLRequest().
334 }
335 }
336
337 // Must be called on the IO loop thread.
CancelURLRequest()338 void CancelURLRequest() {
339 #ifndef NDEBUG
340 {
341 base::AutoLock autolock(lock_);
342 if (io_loop_)
343 DCHECK_EQ(base::MessageLoopForIO::current(), io_loop_);
344 }
345 #endif
346 if (request_) {
347 request_.reset();
348 g_ocsp_io_loop.Get().RemoveRequest(this);
349 {
350 base::AutoLock autolock(lock_);
351 finished_ = true;
352 io_loop_ = NULL;
353 }
354 cv_.Signal();
355 Release(); // Balanced with StartURLRequest().
356 }
357 }
358
359 private:
360 friend class base::RefCountedThreadSafe<OCSPRequestSession>;
361
~OCSPRequestSession()362 virtual ~OCSPRequestSession() {
363 // When this destructor is called, there should be only one thread that has
364 // a reference to this object, and so that thread doesn't need to lock
365 // |lock_| here.
366 DCHECK(!request_);
367 DCHECK(!io_loop_);
368 }
369
370 // Must call this method while holding |lock_|.
CancelLocked()371 void CancelLocked() {
372 lock_.AssertAcquired();
373 if (io_loop_) {
374 io_loop_->PostTask(
375 FROM_HERE,
376 base::Bind(&OCSPRequestSession::CancelURLRequest, this));
377 }
378 }
379
380 // Runs on |g_ocsp_io_loop|'s IO loop.
StartURLRequest()381 void StartURLRequest() {
382 DCHECK(!request_);
383
384 pthread_mutex_lock(&g_request_context_lock);
385 URLRequestContext* url_request_context = g_request_context;
386 pthread_mutex_unlock(&g_request_context_lock);
387
388 if (url_request_context == NULL)
389 return;
390
391 {
392 base::AutoLock autolock(lock_);
393 DCHECK(!io_loop_);
394 io_loop_ = base::MessageLoopForIO::current();
395 g_ocsp_io_loop.Get().AddRequest(this);
396 }
397
398 request_ = url_request_context->CreateRequest(
399 url_, DEFAULT_PRIORITY, this, NULL);
400 // To meet the privacy requirements of incognito mode.
401 request_->SetLoadFlags(LOAD_DISABLE_CACHE | LOAD_DO_NOT_SAVE_COOKIES |
402 LOAD_DO_NOT_SEND_COOKIES);
403
404 if (http_request_method_ == "POST") {
405 DCHECK(!upload_content_.empty());
406 DCHECK(!upload_content_type_.empty());
407
408 request_->set_method("POST");
409 extra_request_headers_.SetHeader(
410 HttpRequestHeaders::kContentType, upload_content_type_);
411
412 scoped_ptr<UploadElementReader> reader(new UploadBytesElementReader(
413 upload_content_.data(), upload_content_.size()));
414 request_->set_upload(make_scoped_ptr(
415 UploadDataStream::CreateWithReader(reader.Pass(), 0)));
416 }
417 if (!extra_request_headers_.IsEmpty())
418 request_->SetExtraRequestHeaders(extra_request_headers_);
419
420 request_->Start();
421 AddRef(); // Release after |request_| deleted.
422 }
423
424 GURL url_; // The URL we eventually wound up at
425 std::string http_request_method_;
426 base::TimeDelta timeout_; // The timeout for OCSP
427 scoped_ptr<URLRequest> request_; // The actual request this wraps
428 scoped_refptr<IOBuffer> buffer_; // Read buffer
429 HttpRequestHeaders extra_request_headers_;
430
431 // HTTP POST payload. |request_| reads bytes from this.
432 std::string upload_content_;
433 std::string upload_content_type_; // MIME type of POST payload
434
435 int response_code_; // HTTP status code for the request
436 std::string response_content_type_;
437 scoped_refptr<HttpResponseHeaders> response_headers_;
438 std::string data_; // Results of the request
439
440 // |lock_| protects |finished_| and |io_loop_|.
441 mutable base::Lock lock_;
442 base::ConditionVariable cv_;
443
444 base::MessageLoop* io_loop_; // Message loop of the IO thread
445 bool finished_;
446
447 DISALLOW_COPY_AND_ASSIGN(OCSPRequestSession);
448 };
449
450 // Concrete class for SEC_HTTP_SERVER_SESSION.
451 class OCSPServerSession {
452 public:
OCSPServerSession(const char * host,PRUint16 port)453 OCSPServerSession(const char* host, PRUint16 port)
454 : host_and_port_(host, port) {}
~OCSPServerSession()455 ~OCSPServerSession() {}
456
CreateRequest(const char * http_protocol_variant,const char * path_and_query_string,const char * http_request_method,const PRIntervalTime timeout)457 OCSPRequestSession* CreateRequest(const char* http_protocol_variant,
458 const char* path_and_query_string,
459 const char* http_request_method,
460 const PRIntervalTime timeout) {
461 // We dont' support "https" because we haven't thought about
462 // whether it's safe to re-enter this code from talking to an OCSP
463 // responder over SSL.
464 if (strcmp(http_protocol_variant, "http") != 0) {
465 PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
466 return NULL;
467 }
468
469 std::string url_string(base::StringPrintf(
470 "%s://%s%s",
471 http_protocol_variant,
472 host_and_port_.ToString().c_str(),
473 path_and_query_string));
474 VLOG(1) << "URL [" << url_string << "]";
475 GURL url(url_string);
476
477 // NSS does not expose public functions to adjust the fetch timeout when
478 // using libpkix, so hardcode the upper limit for network fetches.
479 base::TimeDelta actual_timeout = std::min(
480 base::TimeDelta::FromSeconds(kNetworkFetchTimeoutInSecs),
481 base::TimeDelta::FromMilliseconds(PR_IntervalToMilliseconds(timeout)));
482
483 return new OCSPRequestSession(url, http_request_method, actual_timeout);
484 }
485
486
487 private:
488 HostPortPair host_and_port_;
489
490 DISALLOW_COPY_AND_ASSIGN(OCSPServerSession);
491 };
492
OCSPIOLoop()493 OCSPIOLoop::OCSPIOLoop()
494 : shutdown_(false),
495 used_(false),
496 io_loop_(NULL) {
497 }
498
~OCSPIOLoop()499 OCSPIOLoop::~OCSPIOLoop() {
500 // IO thread was already deleted before the singleton is deleted
501 // in AtExitManager.
502 {
503 base::AutoLock autolock(lock_);
504 DCHECK(!io_loop_);
505 DCHECK(!used_);
506 DCHECK(shutdown_);
507 }
508
509 pthread_mutex_lock(&g_request_context_lock);
510 DCHECK(!g_request_context);
511 pthread_mutex_unlock(&g_request_context_lock);
512 }
513
Shutdown()514 void OCSPIOLoop::Shutdown() {
515 // Safe to read outside lock since we only write on IO thread anyway.
516 DCHECK(thread_checker_.CalledOnValidThread());
517
518 // Prevent the worker thread from trying to access |io_loop_|.
519 {
520 base::AutoLock autolock(lock_);
521 io_loop_ = NULL;
522 used_ = false;
523 shutdown_ = true;
524 }
525
526 CancelAllRequests();
527
528 pthread_mutex_lock(&g_request_context_lock);
529 g_request_context = NULL;
530 pthread_mutex_unlock(&g_request_context_lock);
531 }
532
PostTaskToIOLoop(const tracked_objects::Location & from_here,const base::Closure & task)533 void OCSPIOLoop::PostTaskToIOLoop(
534 const tracked_objects::Location& from_here, const base::Closure& task) {
535 base::AutoLock autolock(lock_);
536 if (io_loop_)
537 io_loop_->PostTask(from_here, task);
538 }
539
EnsureIOLoop()540 void OCSPIOLoop::EnsureIOLoop() {
541 base::AutoLock autolock(lock_);
542 DCHECK_EQ(base::MessageLoopForIO::current(), io_loop_);
543 }
544
AddRequest(OCSPRequestSession * request)545 void OCSPIOLoop::AddRequest(OCSPRequestSession* request) {
546 DCHECK(!ContainsKey(requests_, request));
547 requests_.insert(request);
548 }
549
RemoveRequest(OCSPRequestSession * request)550 void OCSPIOLoop::RemoveRequest(OCSPRequestSession* request) {
551 DCHECK(ContainsKey(requests_, request));
552 requests_.erase(request);
553 }
554
CancelAllRequests()555 void OCSPIOLoop::CancelAllRequests() {
556 // CancelURLRequest() always removes the request from the requests_
557 // set synchronously.
558 while (!requests_.empty())
559 (*requests_.begin())->CancelURLRequest();
560 }
561
OCSPNSSInitialization()562 OCSPNSSInitialization::OCSPNSSInitialization() {
563 // NSS calls the functions in the function table to download certificates
564 // or CRLs or talk to OCSP responders over HTTP. These functions must
565 // set an NSS/NSPR error code when they fail. Otherwise NSS will get the
566 // residual error code from an earlier failed function call.
567 client_fcn_.version = 1;
568 SEC_HttpClientFcnV1Struct *ft = &client_fcn_.fcnTable.ftable1;
569 ft->createSessionFcn = OCSPCreateSession;
570 ft->keepAliveSessionFcn = OCSPKeepAliveSession;
571 ft->freeSessionFcn = OCSPFreeSession;
572 ft->createFcn = OCSPCreate;
573 ft->setPostDataFcn = OCSPSetPostData;
574 ft->addHeaderFcn = OCSPAddHeader;
575 ft->trySendAndReceiveFcn = OCSPTrySendAndReceive;
576 ft->cancelFcn = NULL;
577 ft->freeFcn = OCSPFree;
578 SECStatus status = SEC_RegisterDefaultHttpClient(&client_fcn_);
579 if (status != SECSuccess) {
580 NOTREACHED() << "Error initializing OCSP: " << PR_GetError();
581 }
582
583 // Work around NSS bugs 524013 and 564334. NSS incorrectly thinks the
584 // CRLs for Network Solutions Certificate Authority have bad signatures,
585 // which causes certificates issued by that CA to be reported as revoked.
586 // By using OCSP for those certificates, which don't have AIA extensions,
587 // we can work around these bugs. See http://crbug.com/41730.
588 CERT_StringFromCertFcn old_callback = NULL;
589 status = CERT_RegisterAlternateOCSPAIAInfoCallBack(
590 GetAlternateOCSPAIAInfo, &old_callback);
591 if (status == SECSuccess) {
592 DCHECK(!old_callback);
593 } else {
594 NOTREACHED() << "Error initializing OCSP: " << PR_GetError();
595 }
596 }
597
~OCSPNSSInitialization()598 OCSPNSSInitialization::~OCSPNSSInitialization() {
599 SECStatus status = CERT_RegisterAlternateOCSPAIAInfoCallBack(NULL, NULL);
600 if (status != SECSuccess) {
601 LOG(ERROR) << "Error unregistering OCSP: " << PR_GetError();
602 }
603 }
604
605
606 // OCSP Http Client functions.
607 // Our Http Client functions operate in blocking mode.
OCSPCreateSession(const char * host,PRUint16 portnum,SEC_HTTP_SERVER_SESSION * pSession)608 SECStatus OCSPCreateSession(const char* host, PRUint16 portnum,
609 SEC_HTTP_SERVER_SESSION* pSession) {
610 VLOG(1) << "OCSP create session: host=" << host << " port=" << portnum;
611 pthread_mutex_lock(&g_request_context_lock);
612 URLRequestContext* request_context = g_request_context;
613 pthread_mutex_unlock(&g_request_context_lock);
614 if (request_context == NULL) {
615 LOG(ERROR) << "No URLRequestContext for NSS HTTP handler. host: " << host;
616 // The application failed to call SetURLRequestContextForNSSHttpIO or
617 // has already called ShutdownNSSHttpIO, so we can't create and use
618 // URLRequest. PR_NOT_IMPLEMENTED_ERROR is not an accurate error
619 // code for these error conditions, but is close enough.
620 PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
621 return SECFailure;
622 }
623 *pSession = new OCSPServerSession(host, portnum);
624 return SECSuccess;
625 }
626
OCSPKeepAliveSession(SEC_HTTP_SERVER_SESSION session,PRPollDesc ** pPollDesc)627 SECStatus OCSPKeepAliveSession(SEC_HTTP_SERVER_SESSION session,
628 PRPollDesc **pPollDesc) {
629 VLOG(1) << "OCSP keep alive";
630 if (pPollDesc)
631 *pPollDesc = NULL;
632 return SECSuccess;
633 }
634
OCSPFreeSession(SEC_HTTP_SERVER_SESSION session)635 SECStatus OCSPFreeSession(SEC_HTTP_SERVER_SESSION session) {
636 VLOG(1) << "OCSP free session";
637 delete reinterpret_cast<OCSPServerSession*>(session);
638 return SECSuccess;
639 }
640
OCSPCreate(SEC_HTTP_SERVER_SESSION session,const char * http_protocol_variant,const char * path_and_query_string,const char * http_request_method,const PRIntervalTime timeout,SEC_HTTP_REQUEST_SESSION * pRequest)641 SECStatus OCSPCreate(SEC_HTTP_SERVER_SESSION session,
642 const char* http_protocol_variant,
643 const char* path_and_query_string,
644 const char* http_request_method,
645 const PRIntervalTime timeout,
646 SEC_HTTP_REQUEST_SESSION* pRequest) {
647 VLOG(1) << "OCSP create protocol=" << http_protocol_variant
648 << " path_and_query=" << path_and_query_string
649 << " http_request_method=" << http_request_method
650 << " timeout=" << timeout;
651 OCSPServerSession* ocsp_session =
652 reinterpret_cast<OCSPServerSession*>(session);
653
654 OCSPRequestSession* req = ocsp_session->CreateRequest(http_protocol_variant,
655 path_and_query_string,
656 http_request_method,
657 timeout);
658 SECStatus rv = SECFailure;
659 if (req) {
660 req->AddRef(); // Release in OCSPFree().
661 rv = SECSuccess;
662 }
663 *pRequest = req;
664 return rv;
665 }
666
OCSPSetPostData(SEC_HTTP_REQUEST_SESSION request,const char * http_data,const PRUint32 http_data_len,const char * http_content_type)667 SECStatus OCSPSetPostData(SEC_HTTP_REQUEST_SESSION request,
668 const char* http_data,
669 const PRUint32 http_data_len,
670 const char* http_content_type) {
671 VLOG(1) << "OCSP set post data len=" << http_data_len;
672 OCSPRequestSession* req = reinterpret_cast<OCSPRequestSession*>(request);
673
674 req->SetPostData(http_data, http_data_len, http_content_type);
675 return SECSuccess;
676 }
677
OCSPAddHeader(SEC_HTTP_REQUEST_SESSION request,const char * http_header_name,const char * http_header_value)678 SECStatus OCSPAddHeader(SEC_HTTP_REQUEST_SESSION request,
679 const char* http_header_name,
680 const char* http_header_value) {
681 VLOG(1) << "OCSP add header name=" << http_header_name
682 << " value=" << http_header_value;
683 OCSPRequestSession* req = reinterpret_cast<OCSPRequestSession*>(request);
684
685 req->AddHeader(http_header_name, http_header_value);
686 return SECSuccess;
687 }
688
689 // Sets response of |req| in the output parameters.
690 // It is helper routine for OCSP trySendAndReceiveFcn.
691 // |http_response_data_len| could be used as input parameter. If it has
692 // non-zero value, it is considered as maximum size of |http_response_data|.
OCSPSetResponse(OCSPRequestSession * req,PRUint16 * http_response_code,const char ** http_response_content_type,const char ** http_response_headers,const char ** http_response_data,PRUint32 * http_response_data_len)693 SECStatus OCSPSetResponse(OCSPRequestSession* req,
694 PRUint16* http_response_code,
695 const char** http_response_content_type,
696 const char** http_response_headers,
697 const char** http_response_data,
698 PRUint32* http_response_data_len) {
699 DCHECK(req->Finished());
700 const std::string& data = req->http_response_data();
701 if (http_response_data_len && *http_response_data_len) {
702 if (*http_response_data_len < data.size()) {
703 LOG(ERROR) << "response body too large: " << *http_response_data_len
704 << " < " << data.size();
705 *http_response_data_len = data.size();
706 PORT_SetError(SEC_ERROR_BAD_HTTP_RESPONSE);
707 return SECFailure;
708 }
709 }
710 VLOG(1) << "OCSP response "
711 << " response_code=" << req->http_response_code()
712 << " content_type=" << req->http_response_content_type()
713 << " header=" << req->http_response_headers()
714 << " data_len=" << data.size();
715 if (http_response_code)
716 *http_response_code = req->http_response_code();
717 if (http_response_content_type)
718 *http_response_content_type = req->http_response_content_type().c_str();
719 if (http_response_headers)
720 *http_response_headers = req->http_response_headers().c_str();
721 if (http_response_data)
722 *http_response_data = data.data();
723 if (http_response_data_len)
724 *http_response_data_len = data.size();
725 return SECSuccess;
726 }
727
OCSPTrySendAndReceive(SEC_HTTP_REQUEST_SESSION request,PRPollDesc ** pPollDesc,PRUint16 * http_response_code,const char ** http_response_content_type,const char ** http_response_headers,const char ** http_response_data,PRUint32 * http_response_data_len)728 SECStatus OCSPTrySendAndReceive(SEC_HTTP_REQUEST_SESSION request,
729 PRPollDesc** pPollDesc,
730 PRUint16* http_response_code,
731 const char** http_response_content_type,
732 const char** http_response_headers,
733 const char** http_response_data,
734 PRUint32* http_response_data_len) {
735 if (http_response_data_len) {
736 // We must always set an output value, even on failure. The output value 0
737 // means the failure was unrelated to the acceptable response data length.
738 *http_response_data_len = 0;
739 }
740
741 VLOG(1) << "OCSP try send and receive";
742 OCSPRequestSession* req = reinterpret_cast<OCSPRequestSession*>(request);
743 // We support blocking mode only.
744 if (pPollDesc)
745 *pPollDesc = NULL;
746
747 if (req->Started() || req->Finished()) {
748 // We support blocking mode only, so this function shouldn't be called
749 // again when req has stareted or finished.
750 NOTREACHED();
751 PORT_SetError(SEC_ERROR_BAD_HTTP_RESPONSE); // Simple approximation.
752 return SECFailure;
753 }
754
755 const base::Time start_time = base::Time::Now();
756 bool request_ok = true;
757 req->Start();
758 if (!req->Wait() || req->http_response_code() == static_cast<PRUint16>(-1)) {
759 // If the response code is -1, the request failed and there is no response.
760 request_ok = false;
761 }
762 const base::TimeDelta duration = base::Time::Now() - start_time;
763
764 // For metrics, we want to know if the request was 'successful' or not.
765 // |request_ok| determines if we'll pass the response back to NSS and |ok|
766 // keep track of if we think the response was good.
767 bool ok = true;
768 if (!request_ok ||
769 (req->http_response_code() >= 400 && req->http_response_code() < 600) ||
770 req->http_response_data().size() == 0 ||
771 // 0x30 is the ASN.1 DER encoding of a SEQUENCE. All valid OCSP/CRL/CRT
772 // responses must start with this. If we didn't check for this then a
773 // captive portal could provide an HTML reply that we would count as a
774 // 'success' (although it wouldn't count in NSS, of course).
775 req->http_response_data().data()[0] != 0x30) {
776 ok = false;
777 }
778
779 // We want to know if this was:
780 // 1) An OCSP request
781 // 2) A CRL request
782 // 3) A request for a missing intermediate certificate
783 // There's no sure way to do this, so we use heuristics like MIME type and
784 // URL.
785 const char* mime_type = "";
786 if (ok)
787 mime_type = req->http_response_content_type().c_str();
788 bool is_ocsp =
789 strcasecmp(mime_type, "application/ocsp-response") == 0;
790 bool is_crl = strcasecmp(mime_type, "application/x-pkcs7-crl") == 0 ||
791 strcasecmp(mime_type, "application/x-x509-crl") == 0 ||
792 strcasecmp(mime_type, "application/pkix-crl") == 0;
793 bool is_cert =
794 strcasecmp(mime_type, "application/x-x509-ca-cert") == 0 ||
795 strcasecmp(mime_type, "application/x-x509-server-cert") == 0 ||
796 strcasecmp(mime_type, "application/pkix-cert") == 0 ||
797 strcasecmp(mime_type, "application/pkcs7-mime") == 0;
798
799 if (!is_cert && !is_crl && !is_ocsp) {
800 // We didn't get a hint from the MIME type, so do the best that we can.
801 const std::string path = req->url().path();
802 const std::string host = req->url().host();
803 is_crl = strcasestr(path.c_str(), ".crl") != NULL;
804 is_cert = strcasestr(path.c_str(), ".crt") != NULL ||
805 strcasestr(path.c_str(), ".p7c") != NULL ||
806 strcasestr(path.c_str(), ".cer") != NULL;
807 is_ocsp = strcasestr(host.c_str(), "ocsp") != NULL ||
808 req->http_request_method() == "POST";
809 }
810
811 if (is_ocsp) {
812 if (ok) {
813 UMA_HISTOGRAM_TIMES("Net.OCSPRequestTimeMs", duration);
814 UMA_HISTOGRAM_BOOLEAN("Net.OCSPRequestSuccess", true);
815 } else {
816 UMA_HISTOGRAM_TIMES("Net.OCSPRequestFailedTimeMs", duration);
817 UMA_HISTOGRAM_BOOLEAN("Net.OCSPRequestSuccess", false);
818 }
819 } else if (is_crl) {
820 if (ok) {
821 UMA_HISTOGRAM_TIMES("Net.CRLRequestTimeMs", duration);
822 UMA_HISTOGRAM_BOOLEAN("Net.CRLRequestSuccess", true);
823 } else {
824 UMA_HISTOGRAM_TIMES("Net.CRLRequestFailedTimeMs", duration);
825 UMA_HISTOGRAM_BOOLEAN("Net.CRLRequestSuccess", false);
826 }
827 } else if (is_cert) {
828 if (ok)
829 UMA_HISTOGRAM_TIMES("Net.CRTRequestTimeMs", duration);
830 } else {
831 if (ok)
832 UMA_HISTOGRAM_TIMES("Net.UnknownTypeRequestTimeMs", duration);
833 }
834
835 if (!request_ok) {
836 PORT_SetError(SEC_ERROR_BAD_HTTP_RESPONSE); // Simple approximation.
837 return SECFailure;
838 }
839
840 return OCSPSetResponse(
841 req, http_response_code,
842 http_response_content_type,
843 http_response_headers,
844 http_response_data,
845 http_response_data_len);
846 }
847
OCSPFree(SEC_HTTP_REQUEST_SESSION request)848 SECStatus OCSPFree(SEC_HTTP_REQUEST_SESSION request) {
849 VLOG(1) << "OCSP free";
850 OCSPRequestSession* req = reinterpret_cast<OCSPRequestSession*>(request);
851 req->Cancel();
852 req->Release();
853 return SECSuccess;
854 }
855
856 // Data for GetAlternateOCSPAIAInfo.
857
858 // CN=Network Solutions Certificate Authority,O=Network Solutions L.L.C.,C=US
859 //
860 // There are two CAs with this name. Their key IDs are listed next.
861 const unsigned char network_solutions_ca_name[] = {
862 0x30, 0x62, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04,
863 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x21, 0x30, 0x1f, 0x06,
864 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x4e, 0x65, 0x74, 0x77,
865 0x6f, 0x72, 0x6b, 0x20, 0x53, 0x6f, 0x6c, 0x75, 0x74, 0x69,
866 0x6f, 0x6e, 0x73, 0x20, 0x4c, 0x2e, 0x4c, 0x2e, 0x43, 0x2e,
867 0x31, 0x30, 0x30, 0x2e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
868 0x27, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x53,
869 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x43,
870 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65,
871 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79
872 };
873 const unsigned int network_solutions_ca_name_len = 100;
874
875 // This CA is an intermediate CA, subordinate to UTN-USERFirst-Hardware.
876 const unsigned char network_solutions_ca_key_id[] = {
877 0x3c, 0x41, 0xe2, 0x8f, 0x08, 0x08, 0xa9, 0x4c, 0x25, 0x89,
878 0x8d, 0x6d, 0xc5, 0x38, 0xd0, 0xfc, 0x85, 0x8c, 0x62, 0x17
879 };
880 const unsigned int network_solutions_ca_key_id_len = 20;
881
882 // This CA is a root CA. It is also cross-certified by
883 // UTN-USERFirst-Hardware.
884 const unsigned char network_solutions_ca_key_id2[] = {
885 0x21, 0x30, 0xc9, 0xfb, 0x00, 0xd7, 0x4e, 0x98, 0xda, 0x87,
886 0xaa, 0x2a, 0xd0, 0xa7, 0x2e, 0xb1, 0x40, 0x31, 0xa7, 0x4c
887 };
888 const unsigned int network_solutions_ca_key_id2_len = 20;
889
890 // An entry in our OCSP responder table. |issuer| and |issuer_key_id| are
891 // the key. |ocsp_url| is the value.
892 struct OCSPResponderTableEntry {
893 SECItem issuer;
894 SECItem issuer_key_id;
895 const char *ocsp_url;
896 };
897
898 const OCSPResponderTableEntry g_ocsp_responder_table[] = {
899 {
900 {
901 siBuffer,
902 const_cast<unsigned char*>(network_solutions_ca_name),
903 network_solutions_ca_name_len
904 },
905 {
906 siBuffer,
907 const_cast<unsigned char*>(network_solutions_ca_key_id),
908 network_solutions_ca_key_id_len
909 },
910 "http://ocsp.netsolssl.com"
911 },
912 {
913 {
914 siBuffer,
915 const_cast<unsigned char*>(network_solutions_ca_name),
916 network_solutions_ca_name_len
917 },
918 {
919 siBuffer,
920 const_cast<unsigned char*>(network_solutions_ca_key_id2),
921 network_solutions_ca_key_id2_len
922 },
923 "http://ocsp.netsolssl.com"
924 }
925 };
926
GetAlternateOCSPAIAInfo(CERTCertificate * cert)927 char* GetAlternateOCSPAIAInfo(CERTCertificate *cert) {
928 if (cert && !cert->isRoot && cert->authKeyID) {
929 for (unsigned int i=0; i < arraysize(g_ocsp_responder_table); i++) {
930 if (SECITEM_CompareItem(&g_ocsp_responder_table[i].issuer,
931 &cert->derIssuer) == SECEqual &&
932 SECITEM_CompareItem(&g_ocsp_responder_table[i].issuer_key_id,
933 &cert->authKeyID->keyID) == SECEqual) {
934 return PORT_Strdup(g_ocsp_responder_table[i].ocsp_url);
935 }
936 }
937 }
938
939 return NULL;
940 }
941
942 } // anonymous namespace
943
SetMessageLoopForNSSHttpIO()944 void SetMessageLoopForNSSHttpIO() {
945 // Must have a MessageLoopForIO.
946 DCHECK(base::MessageLoopForIO::current());
947
948 bool used = g_ocsp_io_loop.Get().used();
949
950 // Should not be called when g_ocsp_io_loop has already been used.
951 DCHECK(!used);
952 }
953
EnsureNSSHttpIOInit()954 void EnsureNSSHttpIOInit() {
955 g_ocsp_io_loop.Get().StartUsing();
956 g_ocsp_nss_initialization.Get();
957 }
958
ShutdownNSSHttpIO()959 void ShutdownNSSHttpIO() {
960 g_ocsp_io_loop.Get().Shutdown();
961 }
962
ResetNSSHttpIOForTesting()963 void ResetNSSHttpIOForTesting() {
964 g_ocsp_io_loop.Get().ReuseForTesting();
965 }
966
967 // This function would be called before NSS initialization.
SetURLRequestContextForNSSHttpIO(URLRequestContext * request_context)968 void SetURLRequestContextForNSSHttpIO(URLRequestContext* request_context) {
969 pthread_mutex_lock(&g_request_context_lock);
970 if (request_context) {
971 DCHECK(!g_request_context);
972 }
973 g_request_context = request_context;
974 pthread_mutex_unlock(&g_request_context_lock);
975 }
976
977 } // namespace net
978