Chromium Code Reviews| OLD | NEW |
|---|---|
| (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 | |
| OLD | NEW |