1 // Copyright 2016 The Chromium Authors. All rights reserved.
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/tools/cert_verify_tool/verify_using_cert_verify_proc.h"
6 
7 #include <iostream>
8 
9 #include "base/stl_util.h"
10 #include "base/strings/strcat.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/string_util.h"
13 #include "base/strings/stringprintf.h"
14 #include "crypto/sha2.h"
15 #include "net/base/net_errors.h"
16 #include "net/cert/cert_verifier.h"
17 #include "net/cert/cert_verify_proc.h"
18 #include "net/cert/cert_verify_result.h"
19 #include "net/cert/test_root_certs.h"
20 #include "net/cert/x509_certificate.h"
21 #include "net/cert/x509_util.h"
22 #include "net/log/net_log_with_source.h"
23 #include "net/tools/cert_verify_tool/cert_verify_tool_util.h"
24 
25 namespace {
26 
27 // Associates a printable name with an integer constant. Useful for providing
28 // human-readable decoding of bitmask values.
29 struct StringToConstant {
30   const char* name;
31   const int constant;
32 };
33 
34 const StringToConstant kCertStatusFlags[] = {
35 #define CERT_STATUS_FLAG(label, value) {#label, value},
36 #include "net/cert/cert_status_flags_list.h"
37 #undef CERT_STATUS_FLAG
38 };
39 
40 // Writes a PEM-encoded file of |cert| and its chain.
DumpX509CertificateChain(const base::FilePath & file_path,const net::X509Certificate * cert)41 bool DumpX509CertificateChain(const base::FilePath& file_path,
42                               const net::X509Certificate* cert) {
43   std::vector<std::string> pem_encoded;
44   if (!cert->GetPEMEncodedChain(&pem_encoded)) {
45     std::cerr << "ERROR: X509Certificate::GetPEMEncodedChain failed.\n";
46     return false;
47   }
48   return WriteToFile(file_path, base::StrCat(pem_encoded));
49 }
50 
51 // Returns a hex-encoded sha256 of the DER-encoding of |cert_handle|.
FingerPrintCryptoBuffer(const CRYPTO_BUFFER * cert_handle)52 std::string FingerPrintCryptoBuffer(const CRYPTO_BUFFER* cert_handle) {
53   net::SHA256HashValue hash =
54       net::X509Certificate::CalculateFingerprint256(cert_handle);
55   return base::HexEncode(hash.data, base::size(hash.data));
56 }
57 
58 // Returns a textual representation of the Subject of |cert|.
SubjectFromX509Certificate(const net::X509Certificate * cert)59 std::string SubjectFromX509Certificate(const net::X509Certificate* cert) {
60   return cert->subject().GetDisplayName();
61 }
62 
63 // Returns a textual representation of the Subject of |cert_handle|.
SubjectFromCryptoBuffer(CRYPTO_BUFFER * cert_handle)64 std::string SubjectFromCryptoBuffer(CRYPTO_BUFFER* cert_handle) {
65   scoped_refptr<net::X509Certificate> cert =
66       net::X509Certificate::CreateFromBuffer(bssl::UpRef(cert_handle), {});
67   if (!cert)
68     return std::string();
69   return SubjectFromX509Certificate(cert.get());
70 }
71 
PrintCertStatus(int cert_status)72 void PrintCertStatus(int cert_status) {
73   std::cout << base::StringPrintf("CertStatus: 0x%x\n", cert_status);
74 
75   for (const auto& flag : kCertStatusFlags) {
76     if ((cert_status & flag.constant) == flag.constant)
77       std::cout << " " << flag.name << "\n";
78   }
79 }
80 
PrintCertVerifyResult(const net::CertVerifyResult & result)81 void PrintCertVerifyResult(const net::CertVerifyResult& result) {
82   PrintDebugData(&result);
83   PrintCertStatus(result.cert_status);
84   if (result.has_md2)
85     std::cout << "has_md2\n";
86   if (result.has_md4)
87     std::cout << "has_md4\n";
88   if (result.has_md5)
89     std::cout << "has_md5\n";
90   if (result.has_sha1)
91     std::cout << "has_sha1\n";
92   if (result.has_sha1_leaf)
93     std::cout << "has_sha1_leaf\n";
94   if (result.is_issued_by_known_root)
95     std::cout << "is_issued_by_known_root\n";
96   if (result.is_issued_by_additional_trust_anchor)
97     std::cout << "is_issued_by_additional_trust_anchor\n";
98 
99   if (result.verified_cert) {
100     std::cout << "chain:\n "
101               << FingerPrintCryptoBuffer(result.verified_cert->cert_buffer())
102               << " " << SubjectFromX509Certificate(result.verified_cert.get())
103               << "\n";
104     for (const auto& intermediate :
105          result.verified_cert->intermediate_buffers()) {
106       std::cout << " " << FingerPrintCryptoBuffer(intermediate.get()) << " "
107                 << SubjectFromCryptoBuffer(intermediate.get()) << "\n";
108     }
109   }
110 }
111 
112 }  // namespace
113 
VerifyUsingCertVerifyProc(net::CertVerifyProc * cert_verify_proc,const CertInput & target_der_cert,const std::string & hostname,const std::vector<CertInput> & intermediate_der_certs,const std::vector<CertInput> & root_der_certs,net::CRLSet * crl_set,const base::FilePath & dump_path)114 bool VerifyUsingCertVerifyProc(
115     net::CertVerifyProc* cert_verify_proc,
116     const CertInput& target_der_cert,
117     const std::string& hostname,
118     const std::vector<CertInput>& intermediate_der_certs,
119     const std::vector<CertInput>& root_der_certs,
120     net::CRLSet* crl_set,
121     const base::FilePath& dump_path) {
122   std::cout
123       << "NOTE: CertVerifyProc always uses OS trust settings (--roots are in "
124          "addition).\n";
125 
126   std::vector<base::StringPiece> der_cert_chain;
127   der_cert_chain.push_back(target_der_cert.der_cert);
128   for (const auto& cert : intermediate_der_certs)
129     der_cert_chain.push_back(cert.der_cert);
130 
131   scoped_refptr<net::X509Certificate> x509_target_and_intermediates =
132       net::X509Certificate::CreateFromDERCertChain(der_cert_chain);
133   if (!x509_target_and_intermediates) {
134     std::cerr
135         << "ERROR: X509Certificate::CreateFromDERCertChain failed on one or "
136            "more of:\n";
137     PrintCertError(" (target)", target_der_cert);
138     for (const auto& cert : intermediate_der_certs)
139       PrintCertError(" (intermediate)", cert);
140     return false;
141   }
142 
143   net::CertificateList x509_additional_trust_anchors;
144   for (const auto& cert : root_der_certs) {
145     scoped_refptr<net::X509Certificate> x509_root =
146         net::X509Certificate::CreateFromBytes(cert.der_cert.data(),
147                                               cert.der_cert.size());
148 
149     if (!x509_root)
150       PrintCertError("ERROR: X509Certificate::CreateFromBytes failed:", cert);
151     else
152       x509_additional_trust_anchors.push_back(x509_root);
153   }
154 
155   // TODO(mattm): add command line flags to configure VerifyFlags.
156   int flags = 0;
157 
158   // Not all platforms support providing additional trust anchors to the
159   // verifier. To workaround this, use TestRootCerts to modify the
160   // system trust store globally.
161   net::TestRootCerts* test_root_certs = net::TestRootCerts::GetInstance();
162   CHECK(test_root_certs->IsEmpty());
163 
164   if (!x509_additional_trust_anchors.empty() &&
165       !cert_verify_proc->SupportsAdditionalTrustAnchors()) {
166     std::cerr << "NOTE: Additional trust anchors not supported on this "
167                  "platform. Using TestRootCerts instead.\n";
168 
169     for (const auto& trust_anchor : x509_additional_trust_anchors)
170       test_root_certs->Add(trust_anchor.get());
171 
172     x509_additional_trust_anchors.clear();
173   }
174 
175   // TODO(crbug.com/634484): use a real netlog and print the results?
176   net::CertVerifyResult result;
177   int rv = cert_verify_proc->Verify(
178       x509_target_and_intermediates.get(), hostname,
179       /*ocsp_response=*/std::string(), /*sct_list=*/std::string(), flags,
180       crl_set, x509_additional_trust_anchors, &result, net::NetLogWithSource());
181 
182   // Remove any temporary trust anchors.
183   test_root_certs->Clear();
184 
185   std::cout << "CertVerifyProc result: " << net::ErrorToShortString(rv) << "\n";
186   PrintCertVerifyResult(result);
187   if (!dump_path.empty() && result.verified_cert) {
188     if (!DumpX509CertificateChain(dump_path, result.verified_cert.get())) {
189       return false;
190     }
191   }
192 
193   return rv == net::OK;
194 }
195