// 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 "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" #include "net/cert/crl_set.h" #include "net/cert/x509_certificate.h" using content::BrowserThread; namespace atom { namespace { class Response : public base::LinkNode { 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_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::NetLogWithSource& 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; std::unique_ptr request(new VerifyRequestParams()); request->hostname = params_.hostname(); request->default_result = net::ErrorToString(error); request->certificate = params_.certificate(); BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&CertVerifierRequest::OnVerifyRequestInUI, weak_ptr_factory_.GetWeakPtr(), cert_verifier_->verify_proc(), base::Passed(&request))); } void OnVerifyRequestInUI(const AtomCertVerifier::VerifyProc& verify_proc, std::unique_ptr request) { verify_proc.Run(*(request.get()), 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_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; const AtomCertVerifier::RequestParams params_; AtomCertVerifier* cert_verifier_; int error_; int custom_response_; bool first_response_; ResponseList response_list_; net::CertVerifyResult result_; std::unique_ptr default_verifier_request_; base::WeakPtrFactory weak_ptr_factory_; }; AtomCertVerifier::AtomCertVerifier(AtomCTDelegate* ct_delegate) : default_cert_verifier_(net::CertVerifier::CreateDefault()), ct_delegate_(ct_delegate) {} AtomCertVerifier::~AtomCertVerifier() {} void AtomCertVerifier::SetVerifyProc(const VerifyProc& proc) { verify_proc_ = proc; } int AtomCertVerifier::Verify( const RequestParams& params, net::CRLSet* crl_set, net::CertVerifyResult* verify_result, const net::CompletionCallback& 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, crl_set, verify_result, callback, out_req, net_log); } else { CertVerifierRequest* request = FindRequest(params); if (!request) { out_req->reset(); std::unique_ptr new_request = base::MakeUnique(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; } } bool AtomCertVerifier::SupportsOCSPStapling() { if (verify_proc_.is_null()) return default_cert_verifier_->SupportsOCSPStapling(); return false; } 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