| // Copyright (c) 2008 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "net/base/cert_verifier.h" |
| |
| #if defined(USE_NSS) |
| #include <private/pprthred.h> // PR_DetatchThread |
| #endif |
| |
| #include "base/message_loop.h" |
| #include "base/worker_pool.h" |
| #include "net/base/cert_verify_result.h" |
| #include "net/base/net_errors.h" |
| #include "net/base/x509_certificate.h" |
| |
| namespace net { |
| |
| class CertVerifier::Request : |
| public base::RefCountedThreadSafe<CertVerifier::Request> { |
| public: |
| Request(CertVerifier* verifier, |
| X509Certificate* cert, |
| const std::string& hostname, |
| int flags, |
| CertVerifyResult* verify_result, |
| CompletionCallback* callback) |
| : cert_(cert), |
| hostname_(hostname), |
| flags_(flags), |
| verifier_(verifier), |
| verify_result_(verify_result), |
| callback_(callback), |
| origin_loop_(MessageLoop::current()), |
| error_(OK) { |
| } |
| |
| void DoVerify() { |
| // Running on the worker thread |
| error_ = cert_->Verify(hostname_, flags_, &result_); |
| #if defined(USE_NSS) |
| // Detach the thread from NSPR. |
| // Calling NSS functions attaches the thread to NSPR, which stores |
| // the NSPR thread ID in thread-specific data. |
| // The threads in our thread pool terminate after we have called |
| // PR_Cleanup. Unless we detach them from NSPR, net_unittests gets |
| // segfaults on shutdown when the threads' thread-specific data |
| // destructors run. |
| PR_DetachThread(); |
| #endif |
| |
| Task* reply = NewRunnableMethod(this, &Request::DoCallback); |
| |
| // The origin loop could go away while we are trying to post to it, so we |
| // need to call its PostTask method inside a lock. See ~CertVerifier. |
| { |
| AutoLock locked(origin_loop_lock_); |
| if (origin_loop_) { |
| origin_loop_->PostTask(FROM_HERE, reply); |
| reply = NULL; |
| } |
| } |
| |
| // Does nothing if it got posted. |
| delete reply; |
| } |
| |
| void DoCallback() { |
| // Running on the origin thread. |
| |
| // We may have been cancelled! |
| if (!verifier_) |
| return; |
| |
| *verify_result_ = result_; |
| |
| // Drop the verifier's reference to us. Do this before running the |
| // callback since the callback might result in the verifier being |
| // destroyed. |
| verifier_->request_ = NULL; |
| |
| callback_->Run(error_); |
| } |
| |
| void Cancel() { |
| verifier_ = NULL; |
| |
| AutoLock locked(origin_loop_lock_); |
| origin_loop_ = NULL; |
| } |
| |
| private: |
| friend class base::RefCountedThreadSafe<CertVerifier::Request>; |
| |
| ~Request() {} |
| |
| // Set on the origin thread, read on the worker thread. |
| scoped_refptr<X509Certificate> cert_; |
| std::string hostname_; |
| // bitwise OR'd of X509Certificate::VerifyFlags. |
| int flags_; |
| |
| // Only used on the origin thread (where Verify was called). |
| CertVerifier* verifier_; |
| CertVerifyResult* verify_result_; |
| CompletionCallback* callback_; |
| |
| // Used to post ourselves onto the origin thread. |
| Lock origin_loop_lock_; |
| MessageLoop* origin_loop_; |
| |
| // Assigned on the worker thread, read on the origin thread. |
| int error_; |
| CertVerifyResult result_; |
| }; |
| |
| //----------------------------------------------------------------------------- |
| |
| CertVerifier::CertVerifier() { |
| } |
| |
| CertVerifier::~CertVerifier() { |
| if (request_) |
| request_->Cancel(); |
| } |
| |
| int CertVerifier::Verify(X509Certificate* cert, |
| const std::string& hostname, |
| int flags, |
| CertVerifyResult* verify_result, |
| CompletionCallback* callback) { |
| DCHECK(!request_) << "verifier already in use"; |
| |
| // Do a synchronous verification. |
| if (!callback) { |
| CertVerifyResult result; |
| int rv = cert->Verify(hostname, flags, &result); |
| *verify_result = result; |
| return rv; |
| } |
| |
| request_ = new Request(this, cert, hostname, flags, verify_result, callback); |
| |
| // Dispatch to worker thread... |
| if (!WorkerPool::PostTask(FROM_HERE, |
| NewRunnableMethod(request_.get(), &Request::DoVerify), true)) { |
| NOTREACHED(); |
| request_ = NULL; |
| return ERR_FAILED; |
| } |
| |
| return ERR_IO_PENDING; |
| } |
| |
| } // namespace net |