1 // Copyright 2016 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 <iostream>
6 #include <string_view>
7
8 #include "base/at_exit.h"
9 #include "base/command_line.h"
10 #include "base/functional/bind.h"
11 #include "base/functional/callback_helpers.h"
12 #include "base/logging.h"
13 #include "base/message_loop/message_pump_type.h"
14 #include "base/strings/string_split.h"
15 #include "base/synchronization/waitable_event.h"
16 #include "base/task/thread_pool/thread_pool_instance.h"
17 #include "base/threading/thread.h"
18 #include "base/time/time.h"
19 #include "build/build_config.h"
20 #include "net/cert/cert_net_fetcher.h"
21 #include "net/cert/cert_verify_proc.h"
22 #include "net/cert/cert_verify_proc_builtin.h"
23 #include "net/cert/crl_set.h"
24 #include "net/cert/do_nothing_ct_verifier.h"
25 #include "net/cert/internal/system_trust_store.h"
26 #include "net/cert/x509_util.h"
27 #include "net/cert_net/cert_net_fetcher_url_request.h"
28 #include "net/tools/cert_verify_tool/cert_verify_tool_util.h"
29 #include "net/tools/cert_verify_tool/verify_using_cert_verify_proc.h"
30 #include "net/tools/cert_verify_tool/verify_using_path_builder.h"
31 #include "net/url_request/url_request_context.h"
32 #include "net/url_request/url_request_context_builder.h"
33 #include "net/url_request/url_request_context_getter.h"
34 #include "third_party/boringssl/src/pki/trust_store.h"
35 #include "third_party/boringssl/src/pki/trust_store_collection.h"
36
37 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
38 #include "net/proxy_resolution/proxy_config.h"
39 #include "net/proxy_resolution/proxy_config_service_fixed.h"
40 #endif
41
42 #if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
43 #include "net/cert/internal/trust_store_chrome.h"
44 #endif
45
46 namespace {
47
48 enum class RootStoreType {
49 // No roots other than those explicitly passed in on the command line.
50 kEmpty,
51 #if !BUILDFLAG(CHROME_ROOT_STORE_ONLY)
52 // Use the system root store.
53 kSystem,
54 #endif
55 // Use the Chrome Root Store.
56 kChrome
57 };
58
GetUserAgent()59 std::string GetUserAgent() {
60 return "cert_verify_tool/0.1";
61 }
62
SetUpOnNetworkThread(std::unique_ptr<net::URLRequestContext> * context,scoped_refptr<net::CertNetFetcherURLRequest> * cert_net_fetcher,base::WaitableEvent * initialization_complete_event)63 void SetUpOnNetworkThread(
64 std::unique_ptr<net::URLRequestContext>* context,
65 scoped_refptr<net::CertNetFetcherURLRequest>* cert_net_fetcher,
66 base::WaitableEvent* initialization_complete_event) {
67 net::URLRequestContextBuilder url_request_context_builder;
68 url_request_context_builder.set_user_agent(GetUserAgent());
69 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
70 // On Linux, use a fixed ProxyConfigService, since the default one
71 // depends on glib.
72 //
73 // TODO(akalin): Remove this once http://crbug.com/146421 is fixed.
74 url_request_context_builder.set_proxy_config_service(
75 std::make_unique<net::ProxyConfigServiceFixed>(
76 net::ProxyConfigWithAnnotation()));
77 #endif
78 *context = url_request_context_builder.Build();
79
80 // TODO(mattm): add command line flag to configure using
81 // CertNetFetcher
82 *cert_net_fetcher = base::MakeRefCounted<net::CertNetFetcherURLRequest>();
83 (*cert_net_fetcher)->SetURLRequestContext(context->get());
84 initialization_complete_event->Signal();
85 }
86
ShutdownOnNetworkThread(std::unique_ptr<net::URLRequestContext> * context,scoped_refptr<net::CertNetFetcherURLRequest> * cert_net_fetcher)87 void ShutdownOnNetworkThread(
88 std::unique_ptr<net::URLRequestContext>* context,
89 scoped_refptr<net::CertNetFetcherURLRequest>* cert_net_fetcher) {
90 (*cert_net_fetcher)->Shutdown();
91 cert_net_fetcher->reset();
92 context->reset();
93 }
94
95 // Base class to abstract running a particular implementation of certificate
96 // verification.
97 class CertVerifyImpl {
98 public:
99 virtual ~CertVerifyImpl() = default;
100
101 virtual std::string GetName() const = 0;
102
103 // Does certificate verification.
104 //
105 // Note that |hostname| may be empty to indicate that no name validation is
106 // requested, and a null value of |verify_time| means to use the current time.
107 virtual bool VerifyCert(const CertInput& target_der_cert,
108 const std::string& hostname,
109 const std::vector<CertInput>& intermediate_der_certs,
110 const std::vector<CertInputWithTrustSetting>&
111 der_certs_with_trust_settings,
112 base::Time verify_time,
113 net::CRLSet* crl_set,
114 const base::FilePath& dump_prefix_path) = 0;
115 };
116
117 // Runs certificate verification using a particular CertVerifyProc.
118 class CertVerifyImplUsingProc : public CertVerifyImpl {
119 public:
CertVerifyImplUsingProc(const std::string & name,scoped_refptr<net::CertVerifyProc> proc)120 CertVerifyImplUsingProc(const std::string& name,
121 scoped_refptr<net::CertVerifyProc> proc)
122 : name_(name), proc_(std::move(proc)) {}
123
GetName() const124 std::string GetName() const override { return name_; }
125
VerifyCert(const CertInput & target_der_cert,const std::string & hostname,const std::vector<CertInput> & intermediate_der_certs,const std::vector<CertInputWithTrustSetting> & der_certs_with_trust_settings,base::Time verify_time,net::CRLSet * crl_set,const base::FilePath & dump_prefix_path)126 bool VerifyCert(const CertInput& target_der_cert,
127 const std::string& hostname,
128 const std::vector<CertInput>& intermediate_der_certs,
129 const std::vector<CertInputWithTrustSetting>&
130 der_certs_with_trust_settings,
131 base::Time verify_time,
132 net::CRLSet* crl_set,
133 const base::FilePath& dump_prefix_path) override {
134 if (!verify_time.is_null()) {
135 std::cerr << "WARNING: --time is not supported by " << GetName()
136 << ", will use current time.\n";
137 }
138
139 if (hostname.empty()) {
140 std::cerr << "ERROR: --hostname is required for " << GetName()
141 << ", skipping\n";
142 return true; // "skipping" is considered a successful return.
143 }
144
145 base::FilePath dump_path;
146 if (!dump_prefix_path.empty()) {
147 dump_path = dump_prefix_path.AddExtension(FILE_PATH_LITERAL(".pem"))
148 .InsertBeforeExtensionASCII("." + GetName());
149 }
150
151 return VerifyUsingCertVerifyProc(proc_.get(), target_der_cert, hostname,
152 intermediate_der_certs,
153 der_certs_with_trust_settings, dump_path);
154 }
155
156 private:
157 const std::string name_;
158 scoped_refptr<net::CertVerifyProc> proc_;
159 };
160
161 // Runs certificate verification using bssl::CertPathBuilder.
162 class CertVerifyImplUsingPathBuilder : public CertVerifyImpl {
163 public:
CertVerifyImplUsingPathBuilder(scoped_refptr<net::CertNetFetcher> cert_net_fetcher,std::unique_ptr<net::SystemTrustStore> system_trust_store)164 explicit CertVerifyImplUsingPathBuilder(
165 scoped_refptr<net::CertNetFetcher> cert_net_fetcher,
166 std::unique_ptr<net::SystemTrustStore> system_trust_store)
167 : cert_net_fetcher_(std::move(cert_net_fetcher)),
168 system_trust_store_(std::move(system_trust_store)) {}
169
GetName() const170 std::string GetName() const override { return "CertPathBuilder"; }
171
VerifyCert(const CertInput & target_der_cert,const std::string & hostname,const std::vector<CertInput> & intermediate_der_certs,const std::vector<CertInputWithTrustSetting> & der_certs_with_trust_settings,base::Time verify_time,net::CRLSet * crl_set,const base::FilePath & dump_prefix_path)172 bool VerifyCert(const CertInput& target_der_cert,
173 const std::string& hostname,
174 const std::vector<CertInput>& intermediate_der_certs,
175 const std::vector<CertInputWithTrustSetting>&
176 der_certs_with_trust_settings,
177 base::Time verify_time,
178 net::CRLSet* crl_set,
179 const base::FilePath& dump_prefix_path) override {
180 if (!hostname.empty()) {
181 std::cerr << "WARNING: --hostname is not verified with CertPathBuilder\n";
182 }
183
184 if (verify_time.is_null()) {
185 verify_time = base::Time::Now();
186 }
187
188 return VerifyUsingPathBuilder(target_der_cert, intermediate_der_certs,
189 der_certs_with_trust_settings, verify_time,
190 dump_prefix_path, cert_net_fetcher_,
191 system_trust_store_.get());
192 }
193
194 private:
195 scoped_refptr<net::CertNetFetcher> cert_net_fetcher_;
196 std::unique_ptr<net::SystemTrustStore> system_trust_store_;
197 };
198
199 class DummySystemTrustStore : public net::SystemTrustStore {
200 public:
GetTrustStore()201 bssl::TrustStore* GetTrustStore() override { return &trust_store_; }
202
IsKnownRoot(const bssl::ParsedCertificate * trust_anchor) const203 bool IsKnownRoot(const bssl::ParsedCertificate* trust_anchor) const override {
204 return false;
205 }
206
207 #if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
chrome_root_store_version() const208 int64_t chrome_root_store_version() const override { return 0; }
209
GetChromeRootConstraints(const bssl::ParsedCertificate * cert) const210 base::span<const net::ChromeRootCertConstraints> GetChromeRootConstraints(
211 const bssl::ParsedCertificate* cert) const override {
212 return {};
213 }
214 #endif
215
216 private:
217 bssl::TrustStoreCollection trust_store_;
218 };
219
CreateSystemTrustStore(std::string_view impl_name,RootStoreType root_store_type)220 std::unique_ptr<net::SystemTrustStore> CreateSystemTrustStore(
221 std::string_view impl_name,
222 RootStoreType root_store_type) {
223 switch (root_store_type) {
224 #if BUILDFLAG(IS_FUCHSIA)
225 case RootStoreType::kSystem:
226 std::cerr << impl_name
227 << ": using system roots (--roots are in addition).\n";
228 return net::CreateSslSystemTrustStore();
229 #endif
230 case RootStoreType::kChrome:
231 #if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
232 std::cerr << impl_name
233 << ": using Chrome Root Store (--roots are in addition).\n";
234 return net::CreateSslSystemTrustStoreChromeRoot(
235 std::make_unique<net::TrustStoreChrome>());
236 #else
237 std::cerr << impl_name << ": not supported.\n";
238 [[fallthrough]];
239 #endif
240
241 case RootStoreType::kEmpty:
242 default:
243 std::cerr << impl_name << ": only using --roots specified.\n";
244 return std::make_unique<DummySystemTrustStore>();
245 }
246 }
247
248 // Creates an subclass of CertVerifyImpl based on its name, or returns nullptr.
CreateCertVerifyImplFromName(std::string_view impl_name,scoped_refptr<net::CertNetFetcher> cert_net_fetcher,scoped_refptr<net::CRLSet> crl_set,RootStoreType root_store_type)249 std::unique_ptr<CertVerifyImpl> CreateCertVerifyImplFromName(
250 std::string_view impl_name,
251 scoped_refptr<net::CertNetFetcher> cert_net_fetcher,
252 scoped_refptr<net::CRLSet> crl_set,
253 RootStoreType root_store_type) {
254 #if !(BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(CHROME_ROOT_STORE_ONLY))
255 if (impl_name == "platform") {
256 if (root_store_type != RootStoreType::kSystem) {
257 std::cerr << "WARNING: platform verifier not supported with "
258 "--no-system-roots and --use-chrome-root-store, using "
259 "system roots (--roots are in addition).\n";
260 }
261
262 return std::make_unique<CertVerifyImplUsingProc>(
263 "CertVerifyProc (system)",
264 net::CertVerifyProc::CreateSystemVerifyProc(std::move(cert_net_fetcher),
265 std::move(crl_set)));
266 }
267 #endif
268
269 if (impl_name == "builtin") {
270 return std::make_unique<CertVerifyImplUsingProc>(
271 "CertVerifyProcBuiltin",
272 net::CreateCertVerifyProcBuiltin(
273 std::move(cert_net_fetcher), std::move(crl_set),
274 // TODO(https://crbug.com/848277): support CT.
275 std::make_unique<net::DoNothingCTVerifier>(),
276 base::MakeRefCounted<net::DefaultCTPolicyEnforcer>(),
277 CreateSystemTrustStore(impl_name, root_store_type), {}));
278 }
279
280 if (impl_name == "pathbuilder") {
281 return std::make_unique<CertVerifyImplUsingPathBuilder>(
282 std::move(cert_net_fetcher),
283 CreateSystemTrustStore(impl_name, root_store_type));
284 }
285
286 std::cerr << "WARNING: Unrecognized impl: " << impl_name << "\n";
287 return nullptr;
288 }
289
PrintCertHashAndSubject(CRYPTO_BUFFER * cert)290 void PrintCertHashAndSubject(CRYPTO_BUFFER* cert) {
291 std::cout << " " << FingerPrintCryptoBuffer(cert) << " "
292 << SubjectFromCryptoBuffer(cert) << "\n";
293 }
294
PrintInputChain(const CertInput & target,const std::vector<CertInput> & intermediates)295 void PrintInputChain(const CertInput& target,
296 const std::vector<CertInput>& intermediates) {
297 std::cout << "Input chain:\n";
298 PrintCertHashAndSubject(
299 net::x509_util::CreateCryptoBuffer(target.der_cert).get());
300 for (const auto& intermediate : intermediates) {
301 PrintCertHashAndSubject(
302 net::x509_util::CreateCryptoBuffer(intermediate.der_cert).get());
303 }
304 std::cout << "\n";
305 }
306
PrintAdditionalRoots(const std::vector<CertInputWithTrustSetting> & der_certs_with_trust_settings)307 void PrintAdditionalRoots(const std::vector<CertInputWithTrustSetting>&
308 der_certs_with_trust_settings) {
309 std::cout << "Additional roots:\n";
310 for (const auto& cert : der_certs_with_trust_settings) {
311 std::cout << " " << cert.trust.ToDebugString() << ":\n ";
312 PrintCertHashAndSubject(
313 net::x509_util::CreateCryptoBuffer(cert.cert_input.der_cert).get());
314 }
315 std::cout << "\n";
316 }
317
318 const char kUsage[] =
319 " [flags] <target/chain>\n"
320 "\n"
321 " <target/chain> is a file containing certificates [1]. Minimally it\n"
322 " contains the target certificate. Optionally it may subsequently list\n"
323 " additional certificates needed to build a chain (this is equivalent to\n"
324 " specifying them through --intermediates)\n"
325 "\n"
326 "Flags:\n"
327 "\n"
328 " --hostname=<hostname>\n"
329 " The hostname required to match the end-entity certificate.\n"
330 " Required for the CertVerifyProc implementation.\n"
331 "\n"
332 " --roots=<certs path>\n"
333 " <certs path> is a file containing certificates [1] to interpret as\n"
334 " trust anchors (without any anchor constraints).\n"
335 "\n"
336 " --no-system-roots\n"
337 " Do not use system provided trust roots, only trust roots specified\n"
338 " by --roots or --trust-last-cert will be used. Only supported by\n"
339 " the builtin and pathbuilter impls.\n"
340 "\n"
341 " --use-chrome-root-store\n"
342 " Use the Chrome Root Store. Only supported by the builtin and \n"
343 " pathbuilder impls; if set will override the --no-system-roots \n"
344 " flag.\n"
345 "\n"
346 " --intermediates=<certs path>\n"
347 " <certs path> is a file containing certificates [1] for use when\n"
348 " path building is looking for intermediates.\n"
349 "\n"
350 " --impls=<ordered list of implementations>\n"
351 " Ordered list of the verifier implementations to run. If omitted,\n"
352 " will default to: \"platform,builtin,pathbuilder\".\n"
353 " Changing this can lead to different results in cases where the\n"
354 " platform verifier affects global caches (as in the case of NSS).\n"
355 "\n"
356 " --trust-last-cert\n"
357 " Removes the final intermediate from the chain and instead adds it\n"
358 " as a root. This is useful when providing a <target/chain>\n"
359 " parameter whose final certificate is a trust anchor.\n"
360 "\n"
361 " --root-trust=<trust string>\n"
362 " Roots trusted by --roots and --trust-last-cert will be trusted\n"
363 " with the specified trust [2].\n"
364 "\n"
365 " --trust-leaf-cert=[trust string]\n"
366 " The leaf cert will be considered trusted with the specified\n"
367 " trust [2]. If [trust string] is omitted, defaults to TRUSTED_LEAF.\n"
368 "\n"
369 " --time=<time>\n"
370 " Use <time> instead of the current system time. <time> is\n"
371 " interpreted in local time if a timezone is not specified.\n"
372 " Many common formats are supported, including:\n"
373 " 1994-11-15 12:45:26 GMT\n"
374 " Tue, 15 Nov 1994 12:45:26 GMT\n"
375 " Nov 15 12:45:26 1994 GMT\n"
376 "\n"
377 " --crlset=<crlset path>\n"
378 " <crlset path> is a file containing a serialized CRLSet to use\n"
379 " during revocation checking. For example:\n"
380 " <chrome data dir>/CertificateRevocation/<number>/crl-set\n"
381 "\n"
382 " --dump=<file prefix>\n"
383 " Dumps the verified chain to PEM files starting with\n"
384 " <file prefix>.\n"
385 "\n"
386 "\n"
387 "[1] A \"file containing certificates\" means a path to a file that can\n"
388 " either be:\n"
389 " * A binary file containing a single DER-encoded RFC 5280 Certificate\n"
390 " * A PEM file containing one or more CERTIFICATE blocks (DER-encoded\n"
391 " RFC 5280 Certificate)\n"
392 "\n"
393 "[2] A \"trust string\" consists of a trust type and zero or more options\n"
394 " separated by '+' characters. Note that these trust settings are only\n"
395 " honored by the builtin & pathbuilder impls.\n"
396 " Trust types: UNSPECIFIED, DISTRUSTED, TRUSTED_ANCHOR,\n"
397 " TRUSTED_ANCHOR_OR_LEAF, TRUSTED_LEAF\n"
398 " Options: enforce_anchor_expiry, enforce_anchor_constraints,\n"
399 " require_anchor_basic_constraints, require_leaf_selfsigned\n"
400 " Ex: TRUSTED_ANCHOR+enforce_anchor_expiry+enforce_anchor_constraints\n";
401
PrintUsage(const char * argv0)402 void PrintUsage(const char* argv0) {
403 std::cerr << "Usage: " << argv0 << kUsage;
404
405 // TODO(mattm): allow <certs path> to be a directory containing DER/PEM files?
406 // TODO(mattm): allow target to specify an HTTPS URL to check the cert of?
407 // TODO(mattm): allow target to be a verify_certificate_chain_unittest .test
408 // file?
409 // TODO(mattm): allow specifying ocsp_response and sct_list inputs as well.
410 }
411
412 } // namespace
413
main(int argc,char ** argv)414 int main(int argc, char** argv) {
415 base::AtExitManager at_exit_manager;
416 if (!base::CommandLine::Init(argc, argv)) {
417 std::cerr << "ERROR in CommandLine::Init\n";
418 return 1;
419 }
420 base::ThreadPoolInstance::CreateAndStartWithDefaultParams("cert_verify_tool");
421 base::ScopedClosureRunner cleanup(
422 base::BindOnce([] { base::ThreadPoolInstance::Get()->Shutdown(); }));
423 base::CommandLine& command_line = *base::CommandLine::ForCurrentProcess();
424 logging::LoggingSettings settings;
425 settings.logging_dest =
426 logging::LOG_TO_SYSTEM_DEBUG_LOG | logging::LOG_TO_STDERR;
427 logging::InitLogging(settings);
428
429 base::CommandLine::StringVector args = command_line.GetArgs();
430 if (args.size() != 1U || command_line.HasSwitch("help")) {
431 PrintUsage(argv[0]);
432 return 1;
433 }
434
435 std::string hostname = command_line.GetSwitchValueASCII("hostname");
436
437 base::Time verify_time;
438 std::string time_flag = command_line.GetSwitchValueASCII("time");
439 if (!time_flag.empty()) {
440 if (!base::Time::FromString(time_flag.c_str(), &verify_time)) {
441 std::cerr << "Error parsing --time flag\n";
442 return 1;
443 }
444 }
445
446 #if BUILDFLAG(CHROME_ROOT_STORE_ONLY)
447 RootStoreType root_store_type = RootStoreType::kChrome;
448 #else
449 RootStoreType root_store_type = RootStoreType::kSystem;
450 #endif
451
452 if (command_line.HasSwitch("no-system-roots")) {
453 root_store_type = RootStoreType::kEmpty;
454 }
455 if (command_line.HasSwitch("use-chrome-root-store")) {
456 root_store_type = RootStoreType::kChrome;
457 }
458
459 base::FilePath roots_path = command_line.GetSwitchValuePath("roots");
460 base::FilePath intermediates_path =
461 command_line.GetSwitchValuePath("intermediates");
462 base::FilePath target_path = base::FilePath(args[0]);
463
464 base::FilePath crlset_path = command_line.GetSwitchValuePath("crlset");
465 scoped_refptr<net::CRLSet> crl_set = net::CRLSet::BuiltinCRLSet();
466 if (!crlset_path.empty()) {
467 std::string crl_set_bytes;
468 if (!ReadFromFile(crlset_path, &crl_set_bytes))
469 return 1;
470 if (!net::CRLSet::Parse(crl_set_bytes, &crl_set)) {
471 std::cerr << "Error parsing CRLSet\n";
472 return 1;
473 }
474 }
475
476 base::FilePath dump_prefix_path = command_line.GetSwitchValuePath("dump");
477
478 std::vector<CertInputWithTrustSetting> der_certs_with_trust_settings;
479 std::vector<CertInput> root_der_certs;
480 std::vector<CertInput> intermediate_der_certs;
481 CertInput target_der_cert;
482
483 if (!roots_path.empty())
484 ReadCertificatesFromFile(roots_path, &root_der_certs);
485 if (!intermediates_path.empty())
486 ReadCertificatesFromFile(intermediates_path, &intermediate_der_certs);
487
488 if (!ReadChainFromFile(target_path, &target_der_cert,
489 &intermediate_der_certs)) {
490 std::cerr << "ERROR: Couldn't read certificate chain\n";
491 return 1;
492 }
493
494 if (target_der_cert.der_cert.empty()) {
495 std::cerr << "ERROR: no target cert\n";
496 return 1;
497 }
498
499 // If --trust-last-cert was specified, move the final intermediate to the
500 // roots list.
501 if (command_line.HasSwitch("trust-last-cert")) {
502 if (intermediate_der_certs.empty()) {
503 std::cerr << "ERROR: no intermediate certificates\n";
504 return 1;
505 }
506
507 root_der_certs.push_back(intermediate_der_certs.back());
508 intermediate_der_certs.pop_back();
509 }
510
511 if (command_line.HasSwitch("trust-leaf-cert")) {
512 bssl::CertificateTrust trust = bssl::CertificateTrust::ForTrustedLeaf();
513 std::string trust_str = command_line.GetSwitchValueASCII("trust-leaf-cert");
514 if (!trust_str.empty()) {
515 std::optional<bssl::CertificateTrust> parsed_trust =
516 bssl::CertificateTrust::FromDebugString(trust_str);
517 if (!parsed_trust) {
518 std::cerr << "ERROR: invalid leaf trust string " << trust_str << "\n";
519 return 1;
520 }
521 trust = *parsed_trust;
522 }
523 der_certs_with_trust_settings.push_back({target_der_cert, trust});
524 }
525
526 // TODO(https://crbug.com/1408473): Maybe default to the trust setting that
527 // would be used for locally added anchors on the current platform?
528 bssl::CertificateTrust root_trust = bssl::CertificateTrust::ForTrustAnchor();
529
530 if (command_line.HasSwitch("root-trust")) {
531 std::string trust_str = command_line.GetSwitchValueASCII("root-trust");
532 std::optional<bssl::CertificateTrust> parsed_trust =
533 bssl::CertificateTrust::FromDebugString(trust_str);
534 if (!parsed_trust) {
535 std::cerr << "ERROR: invalid root trust string " << trust_str << "\n";
536 return 1;
537 }
538 root_trust = *parsed_trust;
539 }
540
541 for (const auto& cert_input : root_der_certs) {
542 der_certs_with_trust_settings.push_back({cert_input, root_trust});
543 }
544
545 PrintInputChain(target_der_cert, intermediate_der_certs);
546 if (!der_certs_with_trust_settings.empty()) {
547 PrintAdditionalRoots(der_certs_with_trust_settings);
548 }
549
550 // Create a network thread to be used for AIA fetches, and wait for a
551 // CertNetFetcher to be constructed on that thread.
552 base::Thread::Options options(base::MessagePumpType::IO, 0);
553 base::Thread thread("network_thread");
554 CHECK(thread.StartWithOptions(std::move(options)));
555 // Owned by this thread, but initialized, used, and shutdown on the network
556 // thread.
557 std::unique_ptr<net::URLRequestContext> context;
558 scoped_refptr<net::CertNetFetcherURLRequest> cert_net_fetcher;
559 base::WaitableEvent initialization_complete_event(
560 base::WaitableEvent::ResetPolicy::MANUAL,
561 base::WaitableEvent::InitialState::NOT_SIGNALED);
562 thread.task_runner()->PostTask(
563 FROM_HERE,
564 base::BindOnce(&SetUpOnNetworkThread, &context, &cert_net_fetcher,
565 &initialization_complete_event));
566 initialization_complete_event.Wait();
567
568 std::vector<std::unique_ptr<CertVerifyImpl>> impls;
569
570 // Parse the ordered list of CertVerifyImpl passed via command line flags into
571 // |impls|.
572 std::string impls_str = command_line.GetSwitchValueASCII("impls");
573 if (impls_str.empty()) {
574 // Default value.
575 #if !(BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(IS_LINUX) || \
576 BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(CHROME_ROOT_STORE_ONLY))
577 impls_str = "platform,";
578 #endif
579 impls_str += "builtin,pathbuilder";
580 }
581
582 std::vector<std::string> impl_names = base::SplitString(
583 impls_str, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
584
585 for (const std::string& impl_name : impl_names) {
586 auto verify_impl = CreateCertVerifyImplFromName(impl_name, cert_net_fetcher,
587 crl_set, root_store_type);
588 if (verify_impl)
589 impls.push_back(std::move(verify_impl));
590 }
591
592 // Sequentially run the chain with each of the selected verifier
593 // implementations.
594 bool all_impls_success = true;
595
596 for (size_t i = 0; i < impls.size(); ++i) {
597 if (i != 0)
598 std::cout << "\n";
599
600 std::cout << impls[i]->GetName() << ":\n";
601 if (!impls[i]->VerifyCert(target_der_cert, hostname, intermediate_der_certs,
602 der_certs_with_trust_settings, verify_time,
603 crl_set.get(), dump_prefix_path)) {
604 all_impls_success = false;
605 }
606 }
607
608 // Clean up on the network thread and stop it (which waits for the clean up
609 // task to run).
610 thread.task_runner()->PostTask(
611 FROM_HERE,
612 base::BindOnce(&ShutdownOnNetworkThread, &context, &cert_net_fetcher));
613 thread.Stop();
614
615 return all_impls_success ? 0 : 1;
616 }
617