// Copyright (c) 2015 GitHub, Inc. // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. #include "atom/browser/net/atom_cert_verifier.h" #include #include "atom/browser/browser.h" #include "atom/browser/net/require_ct_delegate.h" #include "atom/common/native_mate_converters/net_converter.h" #include "base/containers/linked_list.h" #include "base/memory/weak_ptr.h" #include "base/task/post_task.h" #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" #include "net/base/net_errors.h" #include "net/cert/cert_verify_result.h" #include "net/cert/x509_certificate.h" using content::BrowserThread; namespace atom { VerifyRequestParams::VerifyRequestParams() = default; VerifyRequestParams::~VerifyRequestParams() = default; VerifyRequestParams::VerifyRequestParams(const VerifyRequestParams&) = default; namespace { class Response : public base::LinkNode { public: Response(net::CertVerifyResult* verify_result, net::CompletionOnceCallback callback) : verify_result_(verify_result), callback_(std::move(callback)) {} net::CertVerifyResult* verify_result() { return verify_result_; } net::CompletionOnceCallback callback() { return std::move(callback_); } private: net::CertVerifyResult* verify_result_; net::CompletionOnceCallback 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), weak_ptr_factory_(this) {} ~CertVerifierRequest() override { cert_verifier_->RemoveRequest(params_); default_verifier_request_.reset(); while (!response_list_.empty() && !first_response_) { base::LinkNode* 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(const net::NetLogWithSource& net_log) { int error = cert_verifier_->default_verifier()->Verify( params_, &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; auto request = std::make_unique(); request->hostname = params_.hostname(); request->default_result = net::ErrorToString(error); request->error_code = error; request->certificate = params_.certificate(); auto response_callback = base::Bind(&CertVerifierRequest::OnResponseInUI, weak_ptr_factory_.GetWeakPtr()); base::PostTaskWithTraits( FROM_HERE, {BrowserThread::UI}, base::BindOnce(&CertVerifierRequest::OnVerifyRequestInUI, cert_verifier_->verify_proc(), std::move(request), response_callback)); } static void OnVerifyRequestInUI( const AtomCertVerifier::VerifyProc& verify_proc, std::unique_ptr request, const base::Callback& response_callback) { verify_proc.Run(*(request.get()), response_callback); } static void OnResponseInUI(base::WeakPtr self, int result) { base::PostTaskWithTraits( FROM_HERE, {BrowserThread::IO}, base::BindOnce(&CertVerifierRequest::NotifyResponseInIO, self, 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_node = response_list_.head(); response_node->RemoveFromList(); Response* response = response_node->value(); RunResponse(response); } void AddResponseListener(net::CertVerifyResult* verify_result, net::CompletionOnceCallback callback) { response_list_.Append(new Response(verify_result, std::move(callback))); } const AtomCertVerifier::RequestParams& params() const { return params_; } private: using ResponseList = base::LinkedList; const AtomCertVerifier::RequestParams params_; AtomCertVerifier* cert_verifier_; int error_ = net::ERR_IO_PENDING; int custom_response_ = net::ERR_IO_PENDING; bool first_response_ = true; ResponseList response_list_; net::CertVerifyResult result_; std::unique_ptr default_verifier_request_; base::WeakPtrFactory weak_ptr_factory_; }; AtomCertVerifier::AtomCertVerifier(RequireCTDelegate* ct_delegate) : default_cert_verifier_(net::CertVerifier::CreateDefault(nullptr)), ct_delegate_(ct_delegate) {} AtomCertVerifier::~AtomCertVerifier() {} void AtomCertVerifier::SetVerifyProc(const VerifyProc& proc) { verify_proc_ = proc; } int AtomCertVerifier::Verify(const RequestParams& params, net::CertVerifyResult* verify_result, net::CompletionOnceCallback callback, std::unique_ptr* out_req, const net::NetLogWithSource& net_log) { DCHECK_CURRENTLY_ON(BrowserThread::IO); if (verify_proc_.is_null()) { ct_delegate_->ClearCTExcludedHostsList(); return default_cert_verifier_->Verify( params, verify_result, std::move(callback), out_req, net_log); } else { CertVerifierRequest* request = FindRequest(params); if (!request) { out_req->reset(); auto new_request = std::make_unique(params, this); new_request->Start(net_log); request = new_request.get(); *out_req = std::move(new_request); inflight_requests_[params] = request; } request->AddResponseListener(verify_result, std::move(callback)); return net::ERR_IO_PENDING; } } void AtomCertVerifier::SetConfig(const Config& config) { default_cert_verifier_->SetConfig(config); } void AtomCertVerifier::RemoveRequest(const RequestParams& params) { auto it = inflight_requests_.find(params); if (it != inflight_requests_.end()) inflight_requests_.erase(it); } CertVerifierRequest* AtomCertVerifier::FindRequest( const RequestParams& params) { auto it = inflight_requests_.find(params); if (it != inflight_requests_.end()) return it->second; return nullptr; } } // namespace atom