blob: 1bc1d1dbf82e72e90653e31186f4893044742258 [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "remoting/base/certificate_helpers.h"
#include <string>
#include "base/logging.h"
#include "build/build_config.h"
#include "crypto/crypto_buildflags.h"
#include "net/cert/x509_certificate.h"
#include "net/ssl/client_cert_store.h"
#if BUILDFLAG(USE_NSS_CERTS)
#include "net/ssl/client_cert_store_nss.h"
#elif BUILDFLAG(IS_WIN)
#include "net/ssl/client_cert_store_win.h"
#elif BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_IOS)
#include "net/ssl/client_cert_store_mac.h"
#endif
namespace remoting {
namespace {
constexpr char kCertIssuerWildCard[] = "*";
// Returns true if certificate |c1| is a worse match than |c2|.
//
// Criteria:
// 1. An invalid certificate is always worse than a valid certificate.
// 2. Invalid certificates are equally bad, in which case false will be
// returned.
// 3. A certificate with earlier |valid_start| time is worse.
// 4. When |valid_start| are the same, the certificate with earlier
// |valid_expiry| is worse.
bool WorseThan(const std::string& issuer,
const base::Time& now,
const net::X509Certificate& c1,
const net::X509Certificate& c2) {
if (!IsCertificateValid(issuer, now, c2)) {
return false;
}
if (!IsCertificateValid(issuer, now, c1)) {
return true;
}
if (c1.valid_start() != c2.valid_start()) {
return c1.valid_start() < c2.valid_start();
}
return c1.valid_expiry() < c2.valid_expiry();
}
#if BUILDFLAG(IS_WIN)
crypto::ScopedHCERTSTORE OpenLocalMachineCertStore() {
return crypto::ScopedHCERTSTORE(::CertOpenStore(
CERT_STORE_PROV_SYSTEM, 0, NULL,
CERT_SYSTEM_STORE_LOCAL_MACHINE | CERT_STORE_READONLY_FLAG, L"MY"));
}
#endif
} // namespace
std::string GetPreferredIssuerFieldValue(const net::X509Certificate& cert) {
if (!cert.issuer().common_name.empty()) {
return cert.issuer().common_name;
}
if (!cert.issuer().organization_names.empty() &&
!cert.issuer().organization_names[0].empty()) {
return cert.issuer().organization_names[0];
}
if (!cert.issuer().organization_unit_names.empty() &&
!cert.issuer().organization_unit_names[0].empty()) {
return cert.issuer().organization_unit_names[0];
}
return std::string();
}
bool IsCertificateValid(const std::string& issuer,
const base::Time& now,
const net::X509Certificate& cert) {
return (issuer == kCertIssuerWildCard ||
issuer == GetPreferredIssuerFieldValue(cert)) &&
cert.valid_start() <= now && cert.valid_expiry() > now;
}
std::unique_ptr<net::ClientCertIdentity> GetBestMatchFromCertificateList(
const std::string& issuer,
const base::Time& now,
net::ClientCertIdentityList& client_certs) {
auto best_match_position = std::max_element(
client_certs.begin(), client_certs.end(),
[&issuer, now](std::unique_ptr<net::ClientCertIdentity>& i1,
std::unique_ptr<net::ClientCertIdentity>& i2) {
return WorseThan(issuer, now, *i1->certificate(), *i2->certificate());
});
if (best_match_position == client_certs.end()) {
LOG(ERROR) << "Failed to find a certificate from the list of candidates ("
<< client_certs.size() << ").";
return nullptr;
}
return std::move(*best_match_position);
}
std::unique_ptr<net::ClientCertStore> CreateClientCertStoreInstance() {
#if BUILDFLAG(USE_NSS_CERTS)
return std::make_unique<net::ClientCertStoreNSS>(
net::ClientCertStoreNSS::PasswordDelegateFactory());
#elif BUILDFLAG(IS_WIN)
// The network process is running as "Local Service" whose "Current User"
// cert store doesn't contain any certificates. Use the "Local Machine"
// store instead.
// The ACL on the private key of the machine certificate in the "Local
// Machine" cert store needs to allow access by "Local Service".
return std::make_unique<net::ClientCertStoreWin>(
base::BindRepeating(&OpenLocalMachineCertStore));
#elif BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_IOS)
return std::make_unique<net::ClientCertStoreMac>();
#else
// OpenSSL does not use the ClientCertStore infrastructure.
return nullptr;
#endif
}
} // namespace remoting