xref: /aosp_15_r20/external/cronet/net/cert/multi_threaded_cert_verifier.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2012 The Chromium Authors
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/cert/multi_threaded_cert_verifier.h"
6 
7 #include "base/check_op.h"
8 #include "base/functional/bind.h"
9 #include "base/functional/callback_helpers.h"
10 #include "base/memory/raw_ptr.h"
11 #include "base/memory/weak_ptr.h"
12 #include "base/task/thread_pool.h"
13 #include "base/threading/thread_restrictions.h"
14 #include "net/base/net_errors.h"
15 #include "net/base/trace_constants.h"
16 #include "net/base/tracing.h"
17 #include "net/cert/cert_verify_proc.h"
18 #include "net/cert/cert_verify_result.h"
19 #include "net/cert/x509_certificate.h"
20 #include "net/log/net_log_event_type.h"
21 #include "net/log/net_log_source_type.h"
22 #include "net/log/net_log_with_source.h"
23 
24 namespace net {
25 
26 // Allows DoVerifyOnWorkerThread to wait on a base::WaitableEvent.
27 // DoVerifyOnWorkerThread may wait on network operations done on a separate
28 // sequence. For instance when using the NSS-based implementation of certificate
29 // verification, the library requires a blocking callback for fetching OCSP and
30 // AIA responses.
31 class [[maybe_unused,
32         nodiscard]] MultiThreadedCertVerifierScopedAllowBaseSyncPrimitives
33     : public base::ScopedAllowBaseSyncPrimitives{};
34 
35 namespace {
36 
37 // Used to pass the result of DoVerifyOnWorkerThread() to
38 // MultiThreadedCertVerifier::InternalRequest::OnJobComplete().
39 struct ResultHelper {
40   int error;
41   CertVerifyResult result;
42   NetLogWithSource net_log;
43 };
44 
GetFlagsForConfig(const CertVerifier::Config & config)45 int GetFlagsForConfig(const CertVerifier::Config& config) {
46   int flags = 0;
47 
48   if (config.enable_rev_checking)
49     flags |= CertVerifyProc::VERIFY_REV_CHECKING_ENABLED;
50   if (config.require_rev_checking_local_anchors)
51     flags |= CertVerifyProc::VERIFY_REV_CHECKING_REQUIRED_LOCAL_ANCHORS;
52   if (config.enable_sha1_local_anchors)
53     flags |= CertVerifyProc::VERIFY_ENABLE_SHA1_LOCAL_ANCHORS;
54   if (config.disable_symantec_enforcement)
55     flags |= CertVerifyProc::VERIFY_DISABLE_SYMANTEC_ENFORCEMENT;
56 
57   return flags;
58 }
59 
60 // Runs the verification synchronously on a worker thread.
DoVerifyOnWorkerThread(const scoped_refptr<CertVerifyProc> & verify_proc,const scoped_refptr<X509Certificate> & cert,const std::string & hostname,const std::string & ocsp_response,const std::string & sct_list,int flags,const NetLogWithSource & net_log)61 std::unique_ptr<ResultHelper> DoVerifyOnWorkerThread(
62     const scoped_refptr<CertVerifyProc>& verify_proc,
63     const scoped_refptr<X509Certificate>& cert,
64     const std::string& hostname,
65     const std::string& ocsp_response,
66     const std::string& sct_list,
67     int flags,
68     const NetLogWithSource& net_log) {
69   TRACE_EVENT0(NetTracingCategory(), "DoVerifyOnWorkerThread");
70   auto verify_result = std::make_unique<ResultHelper>();
71   verify_result->net_log = net_log;
72   MultiThreadedCertVerifierScopedAllowBaseSyncPrimitives
73       allow_base_sync_primitives;
74   verify_result->error =
75       verify_proc->Verify(cert.get(), hostname, ocsp_response, sct_list, flags,
76                           &verify_result->result, net_log);
77   return verify_result;
78 }
79 
80 }  // namespace
81 
82 // Helper to allow callers to cancel pending CertVerifier::Verify requests.
83 // Note that because the CertVerifyProc is blocking, it's not actually
84 // possible to cancel the in-progress request; instead, this simply guarantees
85 // that the provided callback will not be invoked if the Request is deleted.
86 class MultiThreadedCertVerifier::InternalRequest
87     : public CertVerifier::Request,
88       public base::LinkNode<InternalRequest> {
89  public:
90   InternalRequest(CompletionOnceCallback callback,
91                   CertVerifyResult* caller_result);
92   ~InternalRequest() override;
93 
94   void Start(const scoped_refptr<CertVerifyProc>& verify_proc,
95              const CertVerifier::Config& config,
96              const CertVerifier::RequestParams& params,
97              const NetLogWithSource& caller_net_log);
98 
ResetCallback()99   void ResetCallback() { callback_.Reset(); }
100 
101  private:
102   // This is a static method with a |self| weak pointer instead of a regular
103   // method, so that PostTask will still run it even if the weakptr is no
104   // longer valid.
105   static void OnJobComplete(base::WeakPtr<InternalRequest> self,
106                             std::unique_ptr<ResultHelper> verify_result);
107 
108   CompletionOnceCallback callback_;
109   raw_ptr<CertVerifyResult> caller_result_;
110 
111   base::WeakPtrFactory<InternalRequest> weak_factory_{this};
112 };
113 
InternalRequest(CompletionOnceCallback callback,CertVerifyResult * caller_result)114 MultiThreadedCertVerifier::InternalRequest::InternalRequest(
115     CompletionOnceCallback callback,
116     CertVerifyResult* caller_result)
117     : callback_(std::move(callback)), caller_result_(caller_result) {}
118 
~InternalRequest()119 MultiThreadedCertVerifier::InternalRequest::~InternalRequest() {
120   if (callback_) {
121     // This InternalRequest was eagerly cancelled as the callback is still
122     // valid, so |this| needs to be removed from MultiThreadedCertVerifier's
123     // list.
124     RemoveFromList();
125   }
126 }
127 
Start(const scoped_refptr<CertVerifyProc> & verify_proc,const CertVerifier::Config & config,const CertVerifier::RequestParams & params,const NetLogWithSource & caller_net_log)128 void MultiThreadedCertVerifier::InternalRequest::Start(
129     const scoped_refptr<CertVerifyProc>& verify_proc,
130     const CertVerifier::Config& config,
131     const CertVerifier::RequestParams& params,
132     const NetLogWithSource& caller_net_log) {
133   const NetLogWithSource net_log(NetLogWithSource::Make(
134       caller_net_log.net_log(), NetLogSourceType::CERT_VERIFIER_TASK));
135   net_log.BeginEvent(NetLogEventType::CERT_VERIFIER_TASK);
136   caller_net_log.AddEventReferencingSource(
137       NetLogEventType::CERT_VERIFIER_TASK_BOUND, net_log.source());
138 
139   int flags = GetFlagsForConfig(config);
140   if (params.flags() & CertVerifier::VERIFY_DISABLE_NETWORK_FETCHES) {
141     flags |= CertVerifyProc::VERIFY_DISABLE_NETWORK_FETCHES;
142   }
143   base::ThreadPool::PostTaskAndReplyWithResult(
144       FROM_HERE,
145       {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
146       base::BindOnce(&DoVerifyOnWorkerThread, verify_proc, params.certificate(),
147                      params.hostname(), params.ocsp_response(),
148                      params.sct_list(), flags, net_log),
149       base::BindOnce(&MultiThreadedCertVerifier::InternalRequest::OnJobComplete,
150                      weak_factory_.GetWeakPtr()));
151 }
152 
153 // static
OnJobComplete(base::WeakPtr<InternalRequest> self,std::unique_ptr<ResultHelper> verify_result)154 void MultiThreadedCertVerifier::InternalRequest::OnJobComplete(
155     base::WeakPtr<InternalRequest> self,
156     std::unique_ptr<ResultHelper> verify_result) {
157   // Always log the EndEvent, even if the Request has been destroyed.
158   verify_result->net_log.EndEvent(NetLogEventType::CERT_VERIFIER_TASK);
159 
160   // Check |self| weakptr and don't continue if the Request was destroyed.
161   if (!self)
162     return;
163 
164   DCHECK(verify_result);
165 
166   // If the MultiThreadedCertVerifier has been deleted, the callback will have
167   // been reset to null.
168   if (!self->callback_)
169     return;
170 
171   // If ~MultiThreadedCertVerifier has not Reset() our callback, then this
172   // InternalRequest will not have been removed from MultiThreadedCertVerifier's
173   // list yet.
174   self->RemoveFromList();
175 
176   *self->caller_result_ = verify_result->result;
177   // Note: May delete |self|.
178   std::move(self->callback_).Run(verify_result->error);
179 }
180 
MultiThreadedCertVerifier(scoped_refptr<CertVerifyProc> verify_proc,scoped_refptr<CertVerifyProcFactory> verify_proc_factory)181 MultiThreadedCertVerifier::MultiThreadedCertVerifier(
182     scoped_refptr<CertVerifyProc> verify_proc,
183     scoped_refptr<CertVerifyProcFactory> verify_proc_factory)
184     : verify_proc_(std::move(verify_proc)),
185       verify_proc_factory_(std::move(verify_proc_factory)) {
186   CHECK(verify_proc_);
187   CHECK(verify_proc_factory_);
188 }
189 
~MultiThreadedCertVerifier()190 MultiThreadedCertVerifier::~MultiThreadedCertVerifier() {
191   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
192   // Reset the callbacks for each InternalRequest to fulfill the respective
193   // net::CertVerifier contract.
194   for (base::LinkNode<InternalRequest>* node = request_list_.head();
195        node != request_list_.end();) {
196     // Resetting the callback may delete the request, so save a pointer to the
197     // next node first.
198     base::LinkNode<InternalRequest>* next_node = node->next();
199     node->value()->ResetCallback();
200     node = next_node;
201   }
202 }
203 
Verify(const RequestParams & params,CertVerifyResult * verify_result,CompletionOnceCallback callback,std::unique_ptr<Request> * out_req,const NetLogWithSource & net_log)204 int MultiThreadedCertVerifier::Verify(const RequestParams& params,
205                                       CertVerifyResult* verify_result,
206                                       CompletionOnceCallback callback,
207                                       std::unique_ptr<Request>* out_req,
208                                       const NetLogWithSource& net_log) {
209   CHECK(params.certificate());
210   out_req->reset();
211 
212   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
213 
214   if (callback.is_null() || !verify_result || params.hostname().empty())
215     return ERR_INVALID_ARGUMENT;
216 
217   std::unique_ptr<InternalRequest> request =
218       std::make_unique<InternalRequest>(std::move(callback), verify_result);
219   request->Start(verify_proc_, config_, params, net_log);
220   request_list_.Append(request.get());
221   *out_req = std::move(request);
222   return ERR_IO_PENDING;
223 }
224 
UpdateVerifyProcData(scoped_refptr<CertNetFetcher> cert_net_fetcher,const net::CertVerifyProc::ImplParams & impl_params,const net::CertVerifyProc::InstanceParams & instance_params)225 void MultiThreadedCertVerifier::UpdateVerifyProcData(
226     scoped_refptr<CertNetFetcher> cert_net_fetcher,
227     const net::CertVerifyProc::ImplParams& impl_params,
228     const net::CertVerifyProc::InstanceParams& instance_params) {
229   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
230   verify_proc_ = verify_proc_factory_->CreateCertVerifyProc(
231       std::move(cert_net_fetcher), impl_params, instance_params);
232   CHECK(verify_proc_);
233   NotifyCertVerifierChanged();
234 }
235 
SetConfig(const CertVerifier::Config & config)236 void MultiThreadedCertVerifier::SetConfig(const CertVerifier::Config& config) {
237   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
238 
239   config_ = config;
240 }
241 
AddObserver(Observer * observer)242 void MultiThreadedCertVerifier::AddObserver(Observer* observer) {
243   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
244   observers_.AddObserver(observer);
245 }
246 
RemoveObserver(Observer * observer)247 void MultiThreadedCertVerifier::RemoveObserver(Observer* observer) {
248   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
249   observers_.RemoveObserver(observer);
250 }
251 
NotifyCertVerifierChanged()252 void MultiThreadedCertVerifier::NotifyCertVerifierChanged() {
253   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
254   for (Observer& observer : observers_) {
255     observer.OnCertVerifierChanged();
256   }
257 }
258 
259 }  // namespace net
260