#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[] = "*";
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
}
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)
return std::make_unique<net::ClientCertStoreWin>(
base::BindRepeating(&OpenLocalMachineCertStore));
#elif BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_IOS)
return std::make_unique<net::ClientCertStoreMac>();
#else
return nullptr;
#endif
}
}