1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390
|
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/cert/cert_verify_proc_android.h"
#include <set>
#include <string>
#include <string_view>
#include <vector>
#include "base/check_op.h"
#include "base/containers/adapters.h"
#include "base/containers/span.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/notreached.h"
#include "crypto/hash.h"
#include "crypto/sha2.h"
#include "net/android/cert_verify_result_android.h"
#include "net/android/network_library.h"
#include "net/base/net_errors.h"
#include "net/cert/asn1_util.h"
#include "net/cert/cert_net_fetcher.h"
#include "net/cert/cert_status_flags.h"
#include "net/cert/cert_verify_proc.h"
#include "net/cert/cert_verify_result.h"
#include "net/cert/crl_set.h"
#include "net/cert/known_roots.h"
#include "net/cert/test_root_certs.h"
#include "net/cert/x509_certificate.h"
#include "net/cert/x509_util.h"
#include "third_party/boringssl/src/pki/cert_errors.h"
#include "third_party/boringssl/src/pki/parsed_certificate.h"
#include "url/gurl.h"
namespace net {
namespace {
// Android ignores the authType parameter to
// X509TrustManager.checkServerTrusted, so pass in a dummy value. See
// https://crbug.com/627154.
const char kAuthType[] = "RSA";
// The maximum number of AIA fetches that TryVerifyWithAIAFetching() will
// attempt. If a valid chain cannot be built after this many fetches,
// TryVerifyWithAIAFetching() will give up and return
// CERT_VERIFY_STATUS_ANDROID_NO_TRUSTED_ROOT.
const unsigned int kMaxAIAFetches = 5;
// Starting at certs[start], this function searches |certs| for an issuer of
// certs[start], then for an issuer of that issuer, and so on until it finds a
// certificate |cert| for which |certs| does not contain an issuer of
// |cert|. Returns a pointer to this |cert|, or nullptr if all certificates
// while path-building from |start| have an issuer in |certs| (including if
// there is a loop). Note that the returned certificate will be equal to |start|
// if |start| does not have an issuer in |certs|.
//
// TODO(estark): when searching for an issuer, this always uses the first
// encountered issuer in |certs|, and does not handle the situation where
// |certs| contains more than one issuer for a given certificate.
std::shared_ptr<const bssl::ParsedCertificate> FindLastCertWithUnknownIssuer(
const bssl::ParsedCertificateList& certs,
const std::shared_ptr<const bssl::ParsedCertificate>& start) {
DCHECK_GE(certs.size(), 1u);
std::set<std::shared_ptr<const bssl::ParsedCertificate>> used_in_path;
std::shared_ptr<const bssl::ParsedCertificate> last = start;
while (true) {
used_in_path.insert(last);
std::shared_ptr<const bssl::ParsedCertificate> last_issuer;
// Find an issuer for |last| (which might be |last| itself if self-signed).
for (const auto& cert : certs) {
if (cert->normalized_subject() == last->normalized_issuer()) {
last_issuer = cert;
break;
}
}
if (!last_issuer) {
// There is no issuer for |last| in |certs|.
return last;
}
if (last_issuer->normalized_subject() == last_issuer->normalized_issuer()) {
// A chain can be built from |start| to a self-signed certificate, so
// return nullptr to indicate that there is no certificate with an unknown
// issuer.
return nullptr;
}
if (used_in_path.find(last_issuer) != used_in_path.end()) {
// |certs| contains a loop.
return nullptr;
}
// Continue the search for |last_issuer|'s issuer.
last = last_issuer;
}
NOTREACHED();
}
// Uses |fetcher| to fetch issuers from |uri|. If the fetch succeeds, the
// certificate is parsed and added to |cert_list|. Returns true if the fetch was
// successful and the result could be parsed as a certificate, and false
// otherwise.
bool PerformAIAFetchAndAddResultToVector(
scoped_refptr<CertNetFetcher> fetcher,
std::string_view uri,
bssl::ParsedCertificateList* cert_list) {
GURL url(uri);
if (!url.is_valid())
return false;
std::unique_ptr<CertNetFetcher::Request> request(fetcher->FetchCaIssuers(
url, CertNetFetcher::DEFAULT, CertNetFetcher::DEFAULT));
Error error;
std::vector<uint8_t> aia_fetch_bytes;
request->WaitForResult(&error, &aia_fetch_bytes);
if (error != OK)
return false;
bssl::CertErrors errors;
return bssl::ParsedCertificate::CreateAndAddToVector(
x509_util::CreateCryptoBuffer(aia_fetch_bytes),
x509_util::DefaultParseCertificateOptions(), cert_list, &errors);
}
// Uses android::VerifyX509CertChain() to verify the certificates in |certs| for
// |hostname| and returns the verification status. If the verification was
// successful, this function populates |verify_result| and |verified_chain|;
// otherwise it leaves them untouched.
android::CertVerifyStatusAndroid AttemptVerificationAfterAIAFetch(
const bssl::ParsedCertificateList& certs,
const std::string& hostname,
const std::string& ocsp_response,
const std::string& sct_list,
CertVerifyResult* verify_result,
std::vector<std::string>* verified_chain) {
std::vector<std::string> cert_bytes;
for (const auto& cert : certs) {
cert_bytes.push_back(cert->der_cert().AsString());
}
bool is_issued_by_known_root;
std::vector<std::string> candidate_verified_chain;
android::CertVerifyStatusAndroid status;
android::VerifyX509CertChain(cert_bytes, kAuthType, hostname, ocsp_response,
sct_list, &status, &is_issued_by_known_root,
&candidate_verified_chain);
if (status == android::CERT_VERIFY_STATUS_ANDROID_OK) {
verify_result->is_issued_by_known_root = is_issued_by_known_root;
*verified_chain = candidate_verified_chain;
}
return status;
}
// After a CERT_VERIFY_STATUS_ANDROID_NO_TRUSTED_ROOT error is encountered, this
// function can be called to fetch intermediates and retry verification.
//
// It will start from the first certificate in |cert_bytes| and construct a
// chain as far as it can using certificates in |cert_bytes|, and then
// iteratively fetch issuers from any AIA URLs in the last certificate in this
// chain. It will fetch issuers until it encounters a chain that verifies with
// status CERT_VERIFY_STATUS_ANDROID_OK, or it runs out of AIA URLs to fetch, or
// it has attempted |kMaxAIAFetches| fetches.
//
// If it finds a chain that verifies successfully, it returns
// CERT_VERIFY_STATUS_ANDROID_OK and sets |verify_result| and |verified_chain|
// correspondingly. Otherwise, it returns
// CERT_VERIFY_STATUS_ANDROID_NO_TRUSTED_ROOT and does not modify
// |verify_result| or |verified_chain|.
android::CertVerifyStatusAndroid TryVerifyWithAIAFetching(
const std::vector<std::string>& cert_bytes,
const std::string& hostname,
const std::string& ocsp_response,
const std::string& sct_list,
scoped_refptr<CertNetFetcher> cert_net_fetcher,
CertVerifyResult* verify_result,
std::vector<std::string>* verified_chain) {
if (!cert_net_fetcher)
return android::CERT_VERIFY_STATUS_ANDROID_NO_TRUSTED_ROOT;
// Convert the certificates into ParsedCertificates for ease of pulling out
// AIA URLs.
bssl::CertErrors errors;
bssl::ParsedCertificateList certs;
for (const auto& cert : cert_bytes) {
if (!bssl::ParsedCertificate::CreateAndAddToVector(
x509_util::CreateCryptoBuffer(cert),
x509_util::DefaultParseCertificateOptions(), &certs, &errors)) {
return android::CERT_VERIFY_STATUS_ANDROID_NO_TRUSTED_ROOT;
}
}
// Build a chain as far as possible from the target certificate at index 0,
// using the initially provided certificates.
std::shared_ptr<const bssl::ParsedCertificate> last_cert_with_unknown_issuer =
FindLastCertWithUnknownIssuer(certs, certs[0]);
if (!last_cert_with_unknown_issuer) {
// |certs| either contains a loop, or contains a full chain to a self-signed
// certificate. Do not attempt AIA fetches for such a chain.
return android::CERT_VERIFY_STATUS_ANDROID_NO_TRUSTED_ROOT;
}
unsigned int num_aia_fetches = 0;
while (true) {
// If chain-building has terminated in a certificate that does not have an
// AIA URL, give up.
//
// TODO(estark): Instead of giving up at this point, it would be more robust
// to go back to the certificate before |last_cert| in the chain and attempt
// an AIA fetch from that point (if one hasn't already been done). This
// would accomodate chains where the server serves Leaf -> I1 signed by a
// root not in the client's trust store, but AIA fetching would yield an
// intermediate I2 signed by a root that *is* in the client's trust store.
if (!last_cert_with_unknown_issuer->has_authority_info_access())
return android::CERT_VERIFY_STATUS_ANDROID_NO_TRUSTED_ROOT;
for (const auto& uri : last_cert_with_unknown_issuer->ca_issuers_uris()) {
num_aia_fetches++;
if (num_aia_fetches > kMaxAIAFetches)
return android::CERT_VERIFY_STATUS_ANDROID_NO_TRUSTED_ROOT;
if (!PerformAIAFetchAndAddResultToVector(cert_net_fetcher, uri, &certs))
continue;
android::CertVerifyStatusAndroid status =
AttemptVerificationAfterAIAFetch(certs, hostname, ocsp_response,
sct_list, verify_result,
verified_chain);
if (status == android::CERT_VERIFY_STATUS_ANDROID_OK)
return status;
}
// If verification still failed but the path expanded, continue to attempt
// AIA fetches.
std::shared_ptr<const bssl::ParsedCertificate>
new_last_cert_with_unknown_issuer =
FindLastCertWithUnknownIssuer(certs, last_cert_with_unknown_issuer);
if (!new_last_cert_with_unknown_issuer ||
new_last_cert_with_unknown_issuer == last_cert_with_unknown_issuer) {
// The last round of AIA fetches (if there were any) didn't expand the
// path, or it did such that |certs| now contains a full path to an
// (untrusted) root or a loop.
//
// TODO(estark): As above, it would be more robust to go back one
// certificate and attempt an AIA fetch from that point.
return android::CERT_VERIFY_STATUS_ANDROID_NO_TRUSTED_ROOT;
}
last_cert_with_unknown_issuer = new_last_cert_with_unknown_issuer;
}
NOTREACHED();
}
// Returns true if the certificate verification call was successful (regardless
// of its result), i.e. if |verify_result| was set. Otherwise returns false.
bool VerifyFromAndroidTrustManager(
const std::vector<std::string>& cert_bytes,
const std::string& hostname,
const std::string& ocsp_response,
const std::string& sct_list,
int flags,
scoped_refptr<CertNetFetcher> cert_net_fetcher,
CertVerifyResult* verify_result) {
android::CertVerifyStatusAndroid status;
std::vector<std::string> verified_chain;
android::VerifyX509CertChain(
cert_bytes, kAuthType, hostname, ocsp_response, sct_list, &status,
&verify_result->is_issued_by_known_root, &verified_chain);
// If verification resulted in a NO_TRUSTED_ROOT error, then fetch
// intermediates and retry.
if (status == android::CERT_VERIFY_STATUS_ANDROID_NO_TRUSTED_ROOT &&
!(flags & CertVerifyProc::VERIFY_DISABLE_NETWORK_FETCHES)) {
status = TryVerifyWithAIAFetching(cert_bytes, hostname, ocsp_response,
sct_list, std::move(cert_net_fetcher),
verify_result, &verified_chain);
}
switch (status) {
case android::CERT_VERIFY_STATUS_ANDROID_FAILED:
return false;
case android::CERT_VERIFY_STATUS_ANDROID_OK:
break;
case android::CERT_VERIFY_STATUS_ANDROID_NO_TRUSTED_ROOT:
verify_result->cert_status |= CERT_STATUS_AUTHORITY_INVALID;
break;
case android::CERT_VERIFY_STATUS_ANDROID_EXPIRED:
case android::CERT_VERIFY_STATUS_ANDROID_NOT_YET_VALID:
verify_result->cert_status |= CERT_STATUS_DATE_INVALID;
break;
case android::CERT_VERIFY_STATUS_ANDROID_UNABLE_TO_PARSE:
verify_result->cert_status |= CERT_STATUS_INVALID;
break;
case android::CERT_VERIFY_STATUS_ANDROID_INCORRECT_KEY_USAGE:
verify_result->cert_status |= CERT_STATUS_INVALID;
break;
default:
NOTREACHED();
}
// Save the verified chain.
if (!verified_chain.empty()) {
std::vector<std::string_view> verified_chain_pieces(verified_chain.size());
for (size_t i = 0; i < verified_chain.size(); i++) {
verified_chain_pieces[i] = std::string_view(verified_chain[i]);
}
scoped_refptr<X509Certificate> verified_cert =
X509Certificate::CreateFromDERCertChain(verified_chain_pieces);
if (verified_cert.get())
verify_result->verified_cert = std::move(verified_cert);
else
verify_result->cert_status |= CERT_STATUS_INVALID;
}
// Extract the public key hashes and check whether or not any are known
// roots. Walk from the end of the chain (root) to leaf, to optimize for
// known root checks.
for (const auto& cert : base::Reversed(verified_chain)) {
std::string_view spki_bytes;
if (!asn1::ExtractSPKIFromDERCert(cert, &spki_bytes)) {
verify_result->cert_status |= CERT_STATUS_INVALID;
continue;
}
HashValue sha256(crypto::hash::Sha256(base::as_byte_span(spki_bytes)));
verify_result->public_key_hashes.push_back(sha256);
if (!verify_result->is_issued_by_known_root) {
verify_result->is_issued_by_known_root =
GetNetTrustAnchorHistogramIdForSPKI(sha256) != 0;
}
}
// Reverse the hash list, to maintain the leaf->root ordering.
std::reverse(verify_result->public_key_hashes.begin(),
verify_result->public_key_hashes.end());
return true;
}
void GetChainDEREncodedBytes(X509Certificate* cert,
std::vector<std::string>* chain_bytes) {
chain_bytes->reserve(1 + cert->intermediate_buffers().size());
chain_bytes->emplace_back(
net::x509_util::CryptoBufferAsStringPiece(cert->cert_buffer()));
for (const auto& handle : cert->intermediate_buffers()) {
chain_bytes->emplace_back(
net::x509_util::CryptoBufferAsStringPiece(handle.get()));
}
}
} // namespace
CertVerifyProcAndroid::CertVerifyProcAndroid(
scoped_refptr<CertNetFetcher> cert_net_fetcher,
scoped_refptr<CRLSet> crl_set)
: CertVerifyProc(std::move(crl_set)),
cert_net_fetcher_(std::move(cert_net_fetcher)) {}
CertVerifyProcAndroid::~CertVerifyProcAndroid() = default;
int CertVerifyProcAndroid::VerifyInternal(X509Certificate* cert,
const std::string& hostname,
const std::string& ocsp_response,
const std::string& sct_list,
int flags,
CertVerifyResult* verify_result,
const NetLogWithSource& net_log) {
std::vector<std::string> cert_bytes;
GetChainDEREncodedBytes(cert, &cert_bytes);
if (!VerifyFromAndroidTrustManager(cert_bytes, hostname, ocsp_response,
sct_list, flags, cert_net_fetcher_,
verify_result)) {
return ERR_FAILED;
}
if (IsCertStatusError(verify_result->cert_status))
return MapCertStatusToNetError(verify_result->cert_status);
if (TestRootCerts::HasInstance() &&
!verify_result->verified_cert->intermediate_buffers().empty() &&
TestRootCerts::GetInstance()->IsKnownRoot(x509_util::CryptoBufferAsSpan(
verify_result->verified_cert->intermediate_buffers().back().get()))) {
verify_result->is_issued_by_known_root = true;
}
LogNameNormalizationMetrics(".Android", verify_result->verified_cert.get(),
verify_result->is_issued_by_known_root);
return OK;
}
} // namespace net
|