Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(46)

Side by Side Diff: net/cert/cert_verify_proc_builtin.cc

Issue 2755483008: Add initial CertVerifyProcBuiltin. (Closed)
Patch Set: make dtor virtual Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « net/cert/cert_verify_proc_builtin.h ('k') | net/cert/cert_verify_proc_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2017 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/cert/cert_verify_proc_builtin.h"
6
7 #include <string>
8 #include <vector>
9
10 #if defined(USE_NSS_CERTS)
11 #include <cert.h>
12 #include <pk11pub.h>
13 #endif
14
15 #include "base/logging.h"
16 #include "base/memory/ptr_util.h"
17 #include "base/sha1.h"
18 #include "base/strings/string_piece.h"
19 #include "crypto/sha2.h"
20 #include "net/base/net_errors.h"
21 #include "net/cert/asn1_util.h"
22 #include "net/cert/cert_status_flags.h"
23 #include "net/cert/cert_verify_proc.h"
24 #include "net/cert/cert_verify_result.h"
25 #include "net/cert/internal/cert_errors.h"
26 #include "net/cert/internal/cert_issuer_source_static.h"
27 #include "net/cert/internal/parsed_certificate.h"
28 #include "net/cert/internal/path_builder.h"
29 #include "net/cert/internal/signature_policy.h"
30 #include "net/cert/internal/trust_store_collection.h"
31 #include "net/cert/internal/trust_store_in_memory.h"
32 #include "net/cert/internal/verify_certificate_chain.h"
33 #include "net/cert/x509_certificate.h"
34 #include "net/cert/x509_util.h"
35 #include "net/der/encode_values.h"
36
37 #if defined(USE_NSS_CERTS)
38 #include "crypto/nss_util.h"
39 #include "net/cert/internal/cert_issuer_source_nss.h"
40 #include "net/cert/internal/trust_store_nss.h"
41 #endif
42
43 namespace net {
44
45 namespace {
46
47 class CertVerifyProcBuiltin : public CertVerifyProc {
48 public:
49 CertVerifyProcBuiltin();
50
51 bool SupportsAdditionalTrustAnchors() const override;
52 bool SupportsOCSPStapling() const override;
53
54 protected:
55 ~CertVerifyProcBuiltin() override;
56
57 private:
58 int VerifyInternal(X509Certificate* cert,
59 const std::string& hostname,
60 const std::string& ocsp_response,
61 int flags,
62 CRLSet* crl_set,
63 const CertificateList& additional_trust_anchors,
64 CertVerifyResult* verify_result) override;
65 };
66
67 CertVerifyProcBuiltin::CertVerifyProcBuiltin() {}
68
69 CertVerifyProcBuiltin::~CertVerifyProcBuiltin() {}
70
71 bool CertVerifyProcBuiltin::SupportsAdditionalTrustAnchors() const {
72 return true;
73 }
74
75 bool CertVerifyProcBuiltin::SupportsOCSPStapling() const {
76 // TODO(crbug.com/649017): Implement.
77 return false;
78 }
79
80 scoped_refptr<ParsedCertificate> ParseCertificateFromOSHandle(
81 X509Certificate::OSCertHandle cert_handle,
82 CertErrors* errors) {
83 std::string cert_bytes;
84 if (!X509Certificate::GetDEREncoded(cert_handle, &cert_bytes))
85 return nullptr;
86 return ParsedCertificate::Create(x509_util::CreateCryptoBuffer(cert_bytes),
87 {}, errors);
88 }
89
90 void AddIntermediatesToIssuerSource(X509Certificate* cert,
91 CertIssuerSourceStatic* intermediates) {
92 auto cert_handles = cert->GetIntermediateCertificates();
mattm 2017/03/16 02:58:24 Overuse of auto? (I think it should be "const auto
eroman 2017/03/16 17:35:40 Done.
93 CertErrors errors;
94 for (auto it = cert_handles.begin(); it != cert_handles.end(); ++it) {
95 auto cert = ParseCertificateFromOSHandle(*it, &errors);
mattm 2017/03/16 02:58:24 I believe there was also concern over using auto f
mattm 2017/03/16 02:58:24 avoid rebinding of |cert| variable name
eroman 2017/03/16 17:35:41 Done.
eroman 2017/03/16 17:35:41 Done.
96 if (cert)
97 intermediates->AddCert(std::move(cert));
98 // TODO(crbug.com/634443): Surface these parsing errors?
99 }
100 }
101
102 // The SystemTrustStore interface augments the TrustStore interface with some
103 // additional functionality:
104 //
105 // * Determine if a trust anchor was one of the known roots
106 // * Determine if a trust anchor was one of the "extra" ones that
107 // was specified during verification.
108 //
109 // Implementations of SystemTrustStore create an effective trust
110 // store that is the composition of:
111 //
112 // (1) System trust store
113 // (2) |additional_trust_anchors|.
114 // (3) Test certificates (if they are separate from system trust store)
115 class SystemTrustStore {
116 public:
117 virtual ~SystemTrustStore() {}
118
119 virtual TrustStore* GetTrustStore() = 0;
120
121 // TODO(eroman): Can this be exposed through the TrustStore
122 // interface instead?
123 virtual CertIssuerSource* GetCertIssuerSource() = 0;
124
125 // IsKnownRoot returns true if the given trust anchor is a standard one (as
126 // opposed to a user-installed root)
127 virtual bool IsKnownRoot(
128 const scoped_refptr<TrustAnchor>& trust_anchor) const = 0;
129
130 virtual bool IsAdditionalTrustAnchor(
131 const scoped_refptr<TrustAnchor>& trust_anchor) const = 0;
132 };
133
134 #if defined(USE_NSS_CERTS)
135 class SystemTrustStoreNSS : public SystemTrustStore {
136 public:
137 explicit SystemTrustStoreNSS(const CertificateList& additional_trust_anchors)
138 : trust_store_nss_(trustSSL) {
139 CertErrors errors;
140
141 trust_store_.AddTrustStore(&additional_trust_store_);
142 for (const auto& x590_cert : additional_trust_anchors) {
mattm 2017/03/16 02:58:24 x509_cert ?
eroman 2017/03/16 17:35:41 Done.
143 scoped_refptr<ParsedCertificate> cert =
144 ParseCertificateFromOSHandle(x590_cert->os_cert_handle(), &errors);
145 if (cert) {
146 additional_trust_store_.AddTrustAnchor(
147 TrustAnchor::CreateFromCertificateNoConstraints(cert));
mattm 2017/03/16 02:58:24 std::move(cert) ?
eroman 2017/03/16 17:35:40 Done (however I wonder now if that should take a c
148 }
149 // TODO(eroman): Surface parsing errors of additional trust anchor.
150 }
151
152 trust_store_.AddTrustStore(&trust_store_nss_);
153 }
154
155 TrustStore* GetTrustStore() override { return &trust_store_; }
156
157 CertIssuerSource* GetCertIssuerSource() override {
158 return &cert_issuer_source_nss_;
159 }
160
161 // IsKnownRoot returns true if the given trust anchor is a standard one (as
162 // opposed to a user-installed root)
163 bool IsKnownRoot(
164 const scoped_refptr<TrustAnchor>& trust_anchor) const override {
165 // TODO(eroman): Based on how the TrustAnchors are created by this
166 // integration, there will always be an associated certificate. However this
167 // contradicts the API for TrustAnchor that states it is optional.
168 DCHECK(trust_anchor->cert());
169
170 // TODO(eroman): The overall approach of IsKnownRoot() is inefficient -- it
171 // requires searching for the trust anchor by name in NSS (path building
172 // already did this), and then testing matches for equality.
173 crypto::EnsureNSSInit();
174 SECItem name;
175 // Use the original issuer value instead of the normalized version. NSS does
176 // a less extensive normalization in its Name comparisons, so our normalized
177 // version may not match the unnormalized version.
178 name.len = trust_anchor->cert()->tbs().subject_tlv.Length();
179 name.data = const_cast<uint8_t*>(
180 trust_anchor->cert()->tbs().issuer_tlv.UnsafeData());
181 // |validOnly| in CERT_CreateSubjectCertList controls whether to return only
182 // certs that are valid at |sorttime|. Expiration isn't meaningful for trust
183 // anchors, so request all the matches.
184 CERTCertList* found_certs = CERT_CreateSubjectCertList(
185 nullptr /* certList */, CERT_GetDefaultCertDB(), &name,
186 PR_Now() /* sorttime */, PR_FALSE /* validOnly */);
187 if (!found_certs)
188 return false;
189
190 // Search through the matches and find the first one that matches the given
191 // |trust_anchor|. Equality is determined by having the two certificates'
192 // DER match.
193 CERTCertificate* root_cert = nullptr;
194 for (CERTCertListNode* node = CERT_LIST_HEAD(found_certs);
195 !CERT_LIST_END(node, found_certs); node = CERT_LIST_NEXT(node)) {
196 if (trust_anchor->cert()->der_cert() ==
197 der::Input(node->cert->derCert.data, node->cert->derCert.len)) {
198 root_cert = node->cert;
199 break;
200 }
201 }
202
203 return IsKnownRoot(root_cert);
204 }
205
206 bool IsAdditionalTrustAnchor(
207 const scoped_refptr<TrustAnchor>& trust_anchor) const override {
208 return additional_trust_store_.Contains(trust_anchor.get());
209 }
210
211 private:
212 // TODO(eroman): This function was copied verbatim from
213 // cert_verify_proc_nss.cc
214 //
215 // IsKnownRoot returns true if the given certificate is one that we believe
216 // is a standard (as opposed to user-installed) root.
217 bool IsKnownRoot(CERTCertificate* root) const {
218 if (!root || !root->slot)
219 return false;
220
221 // This magic name is taken from
222 // http://bonsai.mozilla.org/cvsblame.cgi?file=mozilla/security/nss/lib/ckfw /builtins/constants.c&rev=1.13&mark=86,89#79
223 return 0 == strcmp(PK11_GetSlotName(root->slot), "NSS Builtin Objects");
224 }
225
226 TrustStoreCollection trust_store_;
227 TrustStoreInMemory additional_trust_store_;
228
229 TrustStoreNSS trust_store_nss_;
230 CertIssuerSourceNSS cert_issuer_source_nss_;
231 };
232 #endif
233
234 std::unique_ptr<SystemTrustStore> CreateSystemTrustStore(
235 const CertificateList& additional_trust_anchors) {
236 #if defined(USE_NSS_CERTS)
237 return base::MakeUnique<SystemTrustStoreNSS>(additional_trust_anchors);
238 #else
239 LOG(ERROR)
240 << "TODO(crbug.com/649017): Integrate with other system trust stores.";
241 return nullptr;
242 #endif
243 }
244
245 // Appends the SHA1 and SHA256 hashes of |spki_bytes| to |*hashes|.
246 void AppendPublicKeyHashes(const der::Input& spki_bytes,
247 HashValueVector* hashes) {
248 HashValue sha1(HASH_VALUE_SHA1);
249 base::SHA1HashBytes(spki_bytes.UnsafeData(), spki_bytes.Length(),
250 sha1.data());
251 hashes->push_back(sha1);
252
253 HashValue sha256(HASH_VALUE_SHA256);
254 crypto::SHA256HashString(spki_bytes.AsStringPiece(), sha256.data(),
255 crypto::kSHA256Length);
256 hashes->push_back(sha256);
257 }
258
259 // Appends the SubjectPublicKeyInfo hashes for all certificates (and trust
260 // anchor) in |partial_path| to |*hashes|.
261 void AppendPublicKeyHashes(const CertPathBuilder::ResultPath& partial_path,
262 HashValueVector* hashes) {
263 for (const scoped_refptr<ParsedCertificate>& cert : partial_path.path.certs)
264 AppendPublicKeyHashes(cert->tbs().spki_tlv, hashes);
265
266 if (partial_path.path.trust_anchor)
267 AppendPublicKeyHashes(partial_path.path.trust_anchor->spki(), hashes);
268 }
269
270 // Sets the bits on |cert_status| for all the errors encountered during the path
271 // building of |partial_path|.
272 void MapPathBuilderErrorsToCertStatus(
273 const CertPathBuilder::ResultPath& partial_path,
274 CertStatus* cert_status) {
275 // If path building was successful, there are no errors to map (there may have
276 // been warnings but they do not map to CertStatus).
277 if (partial_path.valid)
278 return;
279
280 VLOG(1) << partial_path.errors.ToDebugString();
281
282 if (partial_path.errors.ContainsError(kRsaModulusTooSmall))
283 *cert_status |= CERT_STATUS_WEAK_KEY;
284
285 if (partial_path.errors.ContainsError(kValidityFailedNotAfter) ||
286 partial_path.errors.ContainsError(kValidityFailedNotBefore)) {
287 *cert_status |= CERT_STATUS_DATE_INVALID;
288 }
289
290 // IMPORTANT: If the path was invalid for a reason that was not
291 // explicity checked above, set a general error. This is important as
292 // |cert_status| is what ultimately indicates whether verification was
293 // successful or not (absense of errors implies success).
294 if (!IsCertStatusError(*cert_status))
295 *cert_status |= CERT_STATUS_INVALID;
296 }
297
298 X509Certificate::OSCertHandle CreateOSCertHandle(
299 const scoped_refptr<ParsedCertificate>& certificate) {
300 return X509Certificate::CreateOSCertHandleFromBytes(
301 reinterpret_cast<const char*>(certificate->der_cert().UnsafeData()),
302 certificate->der_cert().Length());
303 }
304
305 // Creates a X509Certificate (chain) to return as the verified result.
306 //
307 // * |target_cert|: The original X509Certificate that was passed in to
308 // VerifyInternal()
309 // * |path|: The result (possibly failed) from path building.
310 scoped_refptr<X509Certificate> CreateVerifiedCertChain(
311 X509Certificate* target_cert,
312 const CertPathBuilder::ResultPath& path) {
313 X509Certificate::OSCertHandles intermediates;
314
315 // Skip the first certificate in the path as that is the target certificate
316 for (size_t i = 1; i < path.path.certs.size(); ++i)
317 intermediates.push_back(CreateOSCertHandle(path.path.certs[i]));
318
319 if (path.path.trust_anchor) {
320 // TODO(eroman): This assumes that TrustAnchor::cert() cannot be null,
321 // which disagrees with the documentation.
322 intermediates.push_back(CreateOSCertHandle(path.path.trust_anchor->cert()));
323 }
324
325 scoped_refptr<X509Certificate> result = X509Certificate::CreateFromHandle(
326 target_cert->os_cert_handle(), intermediates);
327
328 for (const X509Certificate::OSCertHandle handle : intermediates)
329 X509Certificate::FreeOSCertHandle(handle);
330
331 return result;
332 }
333
334 // TODO(crbug.com/649017): Make use of |flags|, |crl_set|, and |ocsp_response|.
335 // Also handle key usages, policies and EV.
336 //
337 // Any failure short-circuits from the function must set
338 // |verify_result->cert_status|.
339 void DoVerify(X509Certificate* input_cert,
340 const std::string& hostname,
341 const std::string& ocsp_response,
342 int flags,
343 CRLSet* crl_set,
344 const CertificateList& additional_trust_anchors,
345 CertVerifyResult* verify_result) {
346 CertErrors errors;
347
348 // Parse the target certificate.
349 scoped_refptr<ParsedCertificate> target =
350 ParseCertificateFromOSHandle(input_cert->os_cert_handle(), &errors);
351 if (!target) {
352 // TODO(crbug.com/634443): Surface these parsing errors?
353 verify_result->cert_status |= CERT_STATUS_INVALID;
354 return;
355 }
356
357 std::unique_ptr<SystemTrustStore> trust_store =
358 CreateSystemTrustStore(additional_trust_anchors);
359
360 // TODO(eroman): The path building code in this file enforces its idea of weak
361 // keys, and separately cert_verify_proc.cc also checks the chains with its
362 // own policy. These policies should be aligned, to give path building the
363 // best chance of finding a good path.
364 // Another difference to resolve is the path building here does not check the
365 // target certificate's key strength, whereas cert_verify_proc.cc does.
366 SimpleSignaturePolicy signature_policy(1024);
367
368 // Use the current time.
369 der::GeneralizedTime verification_time;
370 if (!der::EncodeTimeAsGeneralizedTime(base::Time::Now(),
371 &verification_time)) {
372 // This really shouldn't be possible unless Time::Now() returned
373 // something crazy.
374 verify_result->cert_status |= CERT_STATUS_DATE_INVALID;
375 return;
376 }
377
378 // Initialize the path builder.
379 CertPathBuilder::Result result;
380 CertPathBuilder path_builder(target, trust_store->GetTrustStore(),
381 &signature_policy, verification_time, &result);
382
383 // Allow the path builder to discover intermediates from the trust store.
384 if (trust_store->GetCertIssuerSource())
385 path_builder.AddCertIssuerSource(trust_store->GetCertIssuerSource());
386
387 // Allow the path builder to discover the explicitly provided intermediates in
388 // |input_cert|.
389 CertIssuerSourceStatic intermediates;
390 AddIntermediatesToIssuerSource(input_cert, &intermediates);
391 path_builder.AddCertIssuerSource(&intermediates);
392
393 // TODO(crbug.com/649017): Allow the path builder to discover intermediates
394 // through AIA fetching.
395
396 path_builder.Run();
397
398 if (result.best_result_index >= result.paths.size()) {
399 // TODO(eroman): Is this case reachable?
mattm 2017/03/16 02:58:24 Yeah, the path builder currently only returns path
eroman 2017/03/16 17:35:41 Removed the TODO.
eroman 2017/03/16 17:36:26 Correction -- I didn't remove the TODO, i just rew
400 verify_result->cert_status |= CERT_STATUS_AUTHORITY_INVALID;
401 return;
402 }
403
404 // Use the best path that was built. This could be a partial path, or it could
405 // be a valid complete path.
406 const CertPathBuilder::ResultPath& partial_path =
407 *result.paths[result.best_result_index].get();
408
409 if (partial_path.path.trust_anchor) {
410 verify_result->is_issued_by_known_root =
411 trust_store->IsKnownRoot(partial_path.path.trust_anchor);
412
413 verify_result->is_issued_by_additional_trust_anchor =
414 trust_store->IsAdditionalTrustAnchor(partial_path.path.trust_anchor);
415 } else {
416 verify_result->cert_status |= CERT_STATUS_AUTHORITY_INVALID;
417 }
418
419 verify_result->verified_cert =
420 CreateVerifiedCertChain(input_cert, partial_path);
421
422 AppendPublicKeyHashes(partial_path, &verify_result->public_key_hashes);
423 MapPathBuilderErrorsToCertStatus(partial_path, &verify_result->cert_status);
424 }
425
426 int CertVerifyProcBuiltin::VerifyInternal(
427 X509Certificate* input_cert,
428 const std::string& hostname,
429 const std::string& ocsp_response,
430 int flags,
431 CRLSet* crl_set,
432 const CertificateList& additional_trust_anchors,
433 CertVerifyResult* verify_result) {
434 DoVerify(input_cert, hostname, ocsp_response, flags, crl_set,
435 additional_trust_anchors, verify_result);
436
437 return IsCertStatusError(verify_result->cert_status)
438 ? MapCertStatusToNetError(verify_result->cert_status)
439 : OK;
440 }
441
442 } // namespace
443
444 scoped_refptr<CertVerifyProc> CreateCertVerifyProcBuiltin() {
445 return scoped_refptr<CertVerifyProc>(new CertVerifyProcBuiltin());
446 }
447
448 } // namespace net
OLDNEW
« no previous file with comments | « net/cert/cert_verify_proc_builtin.h ('k') | net/cert/cert_verify_proc_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698