Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(68)

Side by Side Diff: net/ocsp/nss_ocsp.cc

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

Powered by Google App Engine
This is Rietveld 408576698