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

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

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