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