• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "crypto/crypto_buildflags.h"
15 #include "net/base/net_errors.h"
16 #include "net/base/trace_constants.h"
17 #include "net/base/tracing.h"
18 #include "net/cert/cert_verify_proc.h"
19 #include "net/cert/cert_verify_result.h"
20 #include "net/cert/crl_set.h"
21 #include "net/cert/x509_certificate.h"
22 #include "net/log/net_log_event_type.h"
23 #include "net/log/net_log_source_type.h"
24 #include "net/log/net_log_with_source.h"
25 
26 #if BUILDFLAG(USE_NSS_CERTS)
27 #include "net/cert/x509_util_nss.h"
28 #endif
29 
30 namespace net {
31 
32 // Allows DoVerifyOnWorkerThread to wait on a base::WaitableEvent.
33 // DoVerifyOnWorkerThread may wait on network operations done on a separate
34 // sequence. For instance when using the NSS-based implementation of certificate
35 // verification, the library requires a blocking callback for fetching OCSP and
36 // AIA responses.
37 class [[maybe_unused,
38         nodiscard]] MultiThreadedCertVerifierScopedAllowBaseSyncPrimitives
39     : public base::ScopedAllowBaseSyncPrimitives{};
40 
41 namespace {
42 
43 // Used to pass the result of DoVerifyOnWorkerThread() to
44 // MultiThreadedCertVerifier::InternalRequest::OnJobComplete().
45 struct ResultHelper {
46   int error;
47   CertVerifyResult result;
48   NetLogWithSource net_log;
49 };
50 
GetFlagsForConfig(const CertVerifier::Config & config)51 int GetFlagsForConfig(const CertVerifier::Config& config) {
52   int flags = 0;
53 
54   if (config.enable_rev_checking)
55     flags |= CertVerifyProc::VERIFY_REV_CHECKING_ENABLED;
56   if (config.require_rev_checking_local_anchors)
57     flags |= CertVerifyProc::VERIFY_REV_CHECKING_REQUIRED_LOCAL_ANCHORS;
58   if (config.enable_sha1_local_anchors)
59     flags |= CertVerifyProc::VERIFY_ENABLE_SHA1_LOCAL_ANCHORS;
60   if (config.disable_symantec_enforcement)
61     flags |= CertVerifyProc::VERIFY_DISABLE_SYMANTEC_ENFORCEMENT;
62 
63   return flags;
64 }
65 
66 // 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 CertificateList & additional_trust_anchors,const NetLogWithSource & net_log)67 std::unique_ptr<ResultHelper> DoVerifyOnWorkerThread(
68     const scoped_refptr<CertVerifyProc>& verify_proc,
69     const scoped_refptr<X509Certificate>& cert,
70     const std::string& hostname,
71     const std::string& ocsp_response,
72     const std::string& sct_list,
73     int flags,
74     const CertificateList& additional_trust_anchors,
75     const NetLogWithSource& net_log) {
76   TRACE_EVENT0(NetTracingCategory(), "DoVerifyOnWorkerThread");
77   auto verify_result = std::make_unique<ResultHelper>();
78   verify_result->net_log = net_log;
79   MultiThreadedCertVerifierScopedAllowBaseSyncPrimitives
80       allow_base_sync_primitives;
81   verify_result->error = verify_proc->Verify(
82       cert.get(), hostname, ocsp_response, sct_list, flags,
83       additional_trust_anchors, &verify_result->result, net_log);
84   // The CertVerifyResult is created and populated on the worker thread and
85   // then returned to the network thread. Detach now before returning the
86   // result, since any further access will be on the network thread.
87   verify_result->result.DetachFromSequence();
88   return verify_result;
89 }
90 
91 }  // namespace
92 
93 // Helper to allow callers to cancel pending CertVerifier::Verify requests.
94 // Note that because the CertVerifyProc is blocking, it's not actually
95 // possible to cancel the in-progress request; instead, this simply guarantees
96 // that the provided callback will not be invoked if the Request is deleted.
97 class MultiThreadedCertVerifier::InternalRequest
98     : public CertVerifier::Request,
99       public base::LinkNode<InternalRequest> {
100  public:
101   InternalRequest(CompletionOnceCallback callback,
102                   CertVerifyResult* caller_result);
103   ~InternalRequest() override;
104 
105   void Start(const scoped_refptr<CertVerifyProc>& verify_proc,
106              const CertVerifier::Config& config,
107              const CertVerifier::RequestParams& params,
108              const NetLogWithSource& caller_net_log);
109 
ResetCallback()110   void ResetCallback() { callback_.Reset(); }
111 
112  private:
113   // This is a static method with a |self| weak pointer instead of a regular
114   // method, so that PostTask will still run it even if the weakptr is no
115   // longer valid.
116   static void OnJobComplete(base::WeakPtr<InternalRequest> self,
117                             std::unique_ptr<ResultHelper> verify_result);
118 
119   CompletionOnceCallback callback_;
120   raw_ptr<CertVerifyResult> caller_result_;
121 
122   base::WeakPtrFactory<InternalRequest> weak_factory_{this};
123 };
124 
InternalRequest(CompletionOnceCallback callback,CertVerifyResult * caller_result)125 MultiThreadedCertVerifier::InternalRequest::InternalRequest(
126     CompletionOnceCallback callback,
127     CertVerifyResult* caller_result)
128     : callback_(std::move(callback)), caller_result_(caller_result) {}
129 
~InternalRequest()130 MultiThreadedCertVerifier::InternalRequest::~InternalRequest() {
131   if (callback_) {
132     // This InternalRequest was eagerly cancelled as the callback is still
133     // valid, so |this| needs to be removed from MultiThreadedCertVerifier's
134     // list.
135     RemoveFromList();
136   }
137 }
138 
Start(const scoped_refptr<CertVerifyProc> & verify_proc,const CertVerifier::Config & config,const CertVerifier::RequestParams & params,const NetLogWithSource & caller_net_log)139 void MultiThreadedCertVerifier::InternalRequest::Start(
140     const scoped_refptr<CertVerifyProc>& verify_proc,
141     const CertVerifier::Config& config,
142     const CertVerifier::RequestParams& params,
143     const NetLogWithSource& caller_net_log) {
144   const NetLogWithSource net_log(NetLogWithSource::Make(
145       caller_net_log.net_log(), NetLogSourceType::CERT_VERIFIER_TASK));
146   net_log.BeginEvent(NetLogEventType::CERT_VERIFIER_TASK);
147   caller_net_log.AddEventReferencingSource(
148       NetLogEventType::CERT_VERIFIER_TASK_BOUND, net_log.source());
149 
150   int flags = GetFlagsForConfig(config);
151   if (params.flags() & CertVerifier::VERIFY_DISABLE_NETWORK_FETCHES) {
152     flags |= CertVerifyProc::VERIFY_DISABLE_NETWORK_FETCHES;
153   }
154   base::ThreadPool::PostTaskAndReplyWithResult(
155       FROM_HERE,
156       {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
157       base::BindOnce(&DoVerifyOnWorkerThread, verify_proc, params.certificate(),
158                      params.hostname(), params.ocsp_response(),
159                      params.sct_list(), flags, config.additional_trust_anchors,
160                      net_log),
161       base::BindOnce(&MultiThreadedCertVerifier::InternalRequest::OnJobComplete,
162                      weak_factory_.GetWeakPtr()));
163 }
164 
165 // static
OnJobComplete(base::WeakPtr<InternalRequest> self,std::unique_ptr<ResultHelper> verify_result)166 void MultiThreadedCertVerifier::InternalRequest::OnJobComplete(
167     base::WeakPtr<InternalRequest> self,
168     std::unique_ptr<ResultHelper> verify_result) {
169   // Always log the EndEvent, even if the Request has been destroyed.
170   verify_result->net_log.EndEvent(NetLogEventType::CERT_VERIFIER_TASK);
171 
172   // Check |self| weakptr and don't continue if the Request was destroyed.
173   if (!self)
174     return;
175 
176   DCHECK(verify_result);
177 
178   // If the MultiThreadedCertVerifier has been deleted, the callback will have
179   // been reset to null.
180   if (!self->callback_)
181     return;
182 
183   // If ~MultiThreadedCertVerifier has not Reset() our callback, then this
184   // InternalRequest will not have been removed from MultiThreadedCertVerifier's
185   // list yet.
186   self->RemoveFromList();
187 
188   *self->caller_result_ = verify_result->result;
189   // Note: May delete |self|.
190   std::move(self->callback_).Run(verify_result->error);
191 }
192 
MultiThreadedCertVerifier(scoped_refptr<CertVerifyProc> verify_proc,scoped_refptr<CertVerifyProcFactory> verify_proc_factory)193 MultiThreadedCertVerifier::MultiThreadedCertVerifier(
194     scoped_refptr<CertVerifyProc> verify_proc,
195     scoped_refptr<CertVerifyProcFactory> verify_proc_factory)
196     : verify_proc_(std::move(verify_proc)),
197       verify_proc_factory_(std::move(verify_proc_factory)) {
198   CHECK(verify_proc_);
199   CHECK(verify_proc_factory_);
200 }
201 
~MultiThreadedCertVerifier()202 MultiThreadedCertVerifier::~MultiThreadedCertVerifier() {
203   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
204   // Reset the callbacks for each InternalRequest to fulfill the respective
205   // net::CertVerifier contract.
206   for (base::LinkNode<InternalRequest>* node = request_list_.head();
207        node != request_list_.end();) {
208     // Resetting the callback may delete the request, so save a pointer to the
209     // next node first.
210     base::LinkNode<InternalRequest>* next_node = node->next();
211     node->value()->ResetCallback();
212     node = next_node;
213   }
214 }
215 
Verify(const RequestParams & params,CertVerifyResult * verify_result,CompletionOnceCallback callback,std::unique_ptr<Request> * out_req,const NetLogWithSource & net_log)216 int MultiThreadedCertVerifier::Verify(const RequestParams& params,
217                                       CertVerifyResult* verify_result,
218                                       CompletionOnceCallback callback,
219                                       std::unique_ptr<Request>* out_req,
220                                       const NetLogWithSource& net_log) {
221   out_req->reset();
222 
223   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
224 
225   if (callback.is_null() || !verify_result || params.hostname().empty())
226     return ERR_INVALID_ARGUMENT;
227 
228   std::unique_ptr<InternalRequest> request =
229       std::make_unique<InternalRequest>(std::move(callback), verify_result);
230   request->Start(verify_proc_, config_, params, net_log);
231   request_list_.Append(request.get());
232   *out_req = std::move(request);
233   return ERR_IO_PENDING;
234 }
235 
UpdateVerifyProcData(scoped_refptr<CertNetFetcher> cert_net_fetcher,const net::CertVerifyProcFactory::ImplParams & impl_params)236 void MultiThreadedCertVerifier::UpdateVerifyProcData(
237     scoped_refptr<CertNetFetcher> cert_net_fetcher,
238     const net::CertVerifyProcFactory::ImplParams& impl_params) {
239   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
240   verify_proc_ = verify_proc_factory_->CreateCertVerifyProc(
241       std::move(cert_net_fetcher), impl_params);
242   NotifyCertVerifierChanged();
243 }
244 
SetConfig(const CertVerifier::Config & config)245 void MultiThreadedCertVerifier::SetConfig(const CertVerifier::Config& config) {
246   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
247   LOG_IF(DFATAL, verify_proc_ &&
248                      !verify_proc_->SupportsAdditionalTrustAnchors() &&
249                      !config.additional_trust_anchors.empty())
250       << "Attempted to set a CertVerifier::Config with additional trust "
251          "anchors, but |verify_proc_| does not support additional trust "
252          "anchors.";
253 
254 // TODO(https://crbug.com/978854): Pass these into the actual CertVerifyProc
255 // rather than relying on global side-effects.
256 #if !BUILDFLAG(USE_NSS_CERTS)
257   // Not yet implemented.
258   DCHECK(config.additional_untrusted_authorities.empty());
259 #else
260   // Construct a temporary list and then swap that into the member variable, to
261   // be polite to any verifications that might be in progress in a background
262   // thread. This ensures that, at least for certs that are present in both the
263   // old and new config, there will not be a time when the refcount drops to
264   // zero. For the case where a cert was in the old config and is not in the
265   // new config, it might be removed while a verification is still going on
266   // that might be able to use it. Oh well. Ideally the list should be passed
267   // into CertVerifyProc as noted by the TODO(https://crbug.com/978854), since
268   // the workers could then keep a reference to the appropriate certs as long
269   // as they need.
270   net::ScopedCERTCertificateList temp_certs;
271   for (const auto& cert : config.additional_untrusted_authorities) {
272     ScopedCERTCertificate nss_cert =
273         x509_util::CreateCERTCertificateFromX509Certificate(cert.get());
274     if (nss_cert)
275       temp_certs.push_back(std::move(nss_cert));
276   }
277   temp_certs_ = std::move(temp_certs);
278 #endif
279 
280   config_ = config;
281 }
282 
AddObserver(Observer * observer)283 void MultiThreadedCertVerifier::AddObserver(Observer* observer) {
284   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
285   observers_.AddObserver(observer);
286 }
287 
RemoveObserver(Observer * observer)288 void MultiThreadedCertVerifier::RemoveObserver(Observer* observer) {
289   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
290   observers_.RemoveObserver(observer);
291 }
292 
NotifyCertVerifierChanged()293 void MultiThreadedCertVerifier::NotifyCertVerifierChanged() {
294   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
295   for (Observer& observer : observers_) {
296     observer.OnCertVerifierChanged();
297   }
298 }
299 
300 }  // namespace net
301