|
@@ -7,6 +7,9 @@
|
|
|
#include "atom/browser/browser.h"
|
|
|
#include "atom/browser/net/atom_ct_delegate.h"
|
|
|
#include "atom/common/native_mate_converters/net_converter.h"
|
|
|
+#include "base/containers/linked_list.h"
|
|
|
+#include "base/memory/ptr_util.h"
|
|
|
+#include "base/memory/weak_ptr.h"
|
|
|
#include "content/public/browser/browser_thread.h"
|
|
|
#include "net/base/net_errors.h"
|
|
|
#include "net/cert/cert_verify_result.h"
|
|
@@ -19,17 +22,119 @@ namespace atom {
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
-void OnResult(
|
|
|
- net::CertVerifyResult* verify_result,
|
|
|
- const net::CompletionCallback& callback,
|
|
|
- bool result) {
|
|
|
- BrowserThread::PostTask(
|
|
|
- BrowserThread::IO, FROM_HERE,
|
|
|
- base::Bind(callback, result ? net::OK : net::ERR_FAILED));
|
|
|
-}
|
|
|
+class Response : public base::LinkNode<Response> {
|
|
|
+ public:
|
|
|
+ Response(net::CertVerifyResult* verify_result,
|
|
|
+ const net::CompletionCallback& callback)
|
|
|
+ : verify_result_(verify_result), callback_(callback) {}
|
|
|
+ net::CertVerifyResult* verify_result() { return verify_result_; }
|
|
|
+ net::CompletionCallback callback() { return callback_; }
|
|
|
+
|
|
|
+ private:
|
|
|
+ net::CertVerifyResult* verify_result_;
|
|
|
+ net::CompletionCallback callback_;
|
|
|
+
|
|
|
+ DISALLOW_COPY_AND_ASSIGN(Response);
|
|
|
+};
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
+class CertVerifierRequest : public AtomCertVerifier::Request {
|
|
|
+ public:
|
|
|
+ CertVerifierRequest(const AtomCertVerifier::RequestParams& params,
|
|
|
+ AtomCertVerifier* cert_verifier)
|
|
|
+ : params_(params),
|
|
|
+ cert_verifier_(cert_verifier),
|
|
|
+ error_(net::ERR_IO_PENDING),
|
|
|
+ custom_response_(net::ERR_IO_PENDING),
|
|
|
+ first_response_(true),
|
|
|
+ weak_ptr_factory_(this) {}
|
|
|
+
|
|
|
+ ~CertVerifierRequest() {
|
|
|
+ cert_verifier_->RemoveRequest(params_);
|
|
|
+ default_verifier_request_.reset();
|
|
|
+ while (!response_list_.empty() && !first_response_) {
|
|
|
+ base::LinkNode<Response>* response_node = response_list_.head();
|
|
|
+ response_node->RemoveFromList();
|
|
|
+ Response* response = response_node->value();
|
|
|
+ RunResponse(response);
|
|
|
+ }
|
|
|
+ cert_verifier_ = nullptr;
|
|
|
+ weak_ptr_factory_.InvalidateWeakPtrs();
|
|
|
+ }
|
|
|
+
|
|
|
+ void RunResponse(Response* response) {
|
|
|
+ if (custom_response_ == net::ERR_ABORTED) {
|
|
|
+ *(response->verify_result()) = result_;
|
|
|
+ response->callback().Run(error_);
|
|
|
+ } else {
|
|
|
+ response->verify_result()->Reset();
|
|
|
+ response->verify_result()->verified_cert = params_.certificate();
|
|
|
+ cert_verifier_->ct_delegate()->AddCTExcludedHost(params_.hostname());
|
|
|
+ response->callback().Run(custom_response_);
|
|
|
+ }
|
|
|
+ delete response;
|
|
|
+ }
|
|
|
+
|
|
|
+ void Start(net::CRLSet* crl_set,
|
|
|
+ const net::BoundNetLog& net_log) {
|
|
|
+ int error = cert_verifier_->default_verifier()->Verify(
|
|
|
+ params_, crl_set, &result_,
|
|
|
+ base::Bind(&CertVerifierRequest::OnDefaultVerificationDone,
|
|
|
+ weak_ptr_factory_.GetWeakPtr()),
|
|
|
+ &default_verifier_request_, net_log);
|
|
|
+ if (error != net::ERR_IO_PENDING)
|
|
|
+ OnDefaultVerificationDone(error);
|
|
|
+ }
|
|
|
+
|
|
|
+ void OnDefaultVerificationDone(int error) {
|
|
|
+ error_ = error;
|
|
|
+ BrowserThread::PostTask(
|
|
|
+ BrowserThread::UI, FROM_HERE,
|
|
|
+ base::Bind(cert_verifier_->verify_proc(), params_.hostname(),
|
|
|
+ params_.certificate(), net::ErrorToString(error),
|
|
|
+ base::Bind(&CertVerifierRequest::OnResponseInUI,
|
|
|
+ weak_ptr_factory_.GetWeakPtr())));
|
|
|
+ }
|
|
|
+
|
|
|
+ void OnResponseInUI(int result) {
|
|
|
+ BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
|
|
|
+ base::Bind(&CertVerifierRequest::NotifyResponseInIO,
|
|
|
+ weak_ptr_factory_.GetWeakPtr(), result));
|
|
|
+ }
|
|
|
+
|
|
|
+ void NotifyResponseInIO(int result) {
|
|
|
+ custom_response_ = result;
|
|
|
+ first_response_ = false;
|
|
|
+ // Responding to first request in the list will initiate destruction of
|
|
|
+ // the class, respond to others in the list inside destructor.
|
|
|
+ base::LinkNode<Response>* response_node = response_list_.head();
|
|
|
+ response_node->RemoveFromList();
|
|
|
+ Response* response = response_node->value();
|
|
|
+ RunResponse(response);
|
|
|
+ }
|
|
|
+
|
|
|
+ void AddResponseListener(net::CertVerifyResult* verify_result,
|
|
|
+ const net::CompletionCallback& callback) {
|
|
|
+ response_list_.Append(new Response(verify_result, callback));
|
|
|
+ }
|
|
|
+
|
|
|
+ const AtomCertVerifier::RequestParams& params() const { return params_; }
|
|
|
+
|
|
|
+ private:
|
|
|
+ using ResponseList = base::LinkedList<Response>;
|
|
|
+
|
|
|
+ const AtomCertVerifier::RequestParams params_;
|
|
|
+ AtomCertVerifier* cert_verifier_;
|
|
|
+ int error_;
|
|
|
+ int custom_response_;
|
|
|
+ bool first_response_;
|
|
|
+ ResponseList response_list_;
|
|
|
+ net::CertVerifyResult result_;
|
|
|
+ std::unique_ptr<AtomCertVerifier::Request> default_verifier_request_;
|
|
|
+ base::WeakPtrFactory<CertVerifierRequest> weak_ptr_factory_;
|
|
|
+};
|
|
|
+
|
|
|
AtomCertVerifier::AtomCertVerifier(AtomCTDelegate* ct_delegate)
|
|
|
: default_cert_verifier_(net::CertVerifier::CreateDefault()),
|
|
|
ct_delegate_(ct_delegate) {}
|
|
@@ -51,23 +156,43 @@ int AtomCertVerifier::Verify(
|
|
|
|
|
|
if (verify_proc_.is_null()) {
|
|
|
ct_delegate_->ClearCTExcludedHostsList();
|
|
|
- return default_cert_verifier_->Verify(
|
|
|
- params, crl_set, verify_result, callback, out_req, net_log);
|
|
|
+ return default_cert_verifier_->Verify(params, crl_set, verify_result,
|
|
|
+ callback, out_req, net_log);
|
|
|
+ } else {
|
|
|
+ CertVerifierRequest* request = FindRequest(params);
|
|
|
+ if (!request) {
|
|
|
+ out_req->reset();
|
|
|
+ std::unique_ptr<CertVerifierRequest> new_request =
|
|
|
+ base::MakeUnique<CertVerifierRequest>(params, this);
|
|
|
+ new_request->Start(crl_set, net_log);
|
|
|
+ request = new_request.get();
|
|
|
+ *out_req = std::move(new_request);
|
|
|
+ inflight_requests_[params] = request;
|
|
|
+ }
|
|
|
+ request->AddResponseListener(verify_result, callback);
|
|
|
+
|
|
|
+ return net::ERR_IO_PENDING;
|
|
|
}
|
|
|
+}
|
|
|
|
|
|
- verify_result->Reset();
|
|
|
- verify_result->verified_cert = params.certificate();
|
|
|
- ct_delegate_->AddCTExcludedHost(params.hostname());
|
|
|
+bool AtomCertVerifier::SupportsOCSPStapling() {
|
|
|
+ if (verify_proc_.is_null())
|
|
|
+ return default_cert_verifier_->SupportsOCSPStapling();
|
|
|
+ return false;
|
|
|
+}
|
|
|
|
|
|
- BrowserThread::PostTask(
|
|
|
- BrowserThread::UI, FROM_HERE,
|
|
|
- base::Bind(verify_proc_, params.hostname(), params.certificate(),
|
|
|
- base::Bind(OnResult, verify_result, callback)));
|
|
|
- return net::ERR_IO_PENDING;
|
|
|
+void AtomCertVerifier::RemoveRequest(const RequestParams& params) {
|
|
|
+ auto it = inflight_requests_.find(params);
|
|
|
+ if (it != inflight_requests_.end())
|
|
|
+ inflight_requests_.erase(it);
|
|
|
}
|
|
|
|
|
|
-bool AtomCertVerifier::SupportsOCSPStapling() {
|
|
|
- return true;
|
|
|
+CertVerifierRequest* AtomCertVerifier::FindRequest(
|
|
|
+ const RequestParams& params) {
|
|
|
+ auto it = inflight_requests_.find(params);
|
|
|
+ if (it != inflight_requests_.end())
|
|
|
+ return it->second;
|
|
|
+ return nullptr;
|
|
|
}
|
|
|
|
|
|
} // namespace atom
|