chromium/chrome/browser/certificate_manager_model.cc

// 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 "chrome/browser/certificate_manager_model.h"

#include <utility>

#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/i18n/time_formatting.h"
#include "base/logging.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/scoped_observation.h"
#include "base/sequence_checker.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/bind_post_task.h"
#include "base/task/sequenced_task_runner.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/net/nss_service.h"
#include "chrome/browser/net/nss_service_factory.h"
#include "chrome/browser/net/system_network_context_manager.h"
#include "chrome/browser/ui/crypto_module_password_dialog_nss.h"
#include "chrome/common/net/x509_certificate_model_nss.h"
#include "chrome/grit/generated_resources.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/resource_context.h"
#include "crypto/scoped_nss_types.h"
#include "net/base/net_errors.h"
#include "net/cert/cert_database.h"
#include "net/cert/nss_cert_database.h"
#include "net/cert/x509_certificate.h"
#include "net/cert/x509_util_nss.h"
#include "net/net_buildflags.h"
#include "ui/base/l10n/l10n_util.h"

#if BUILDFLAG(IS_CHROMEOS)
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/certificate_provider/certificate_provider.h"
#include "chrome/browser/certificate_provider/certificate_provider_service.h"
#include "chrome/browser/certificate_provider/certificate_provider_service_factory.h"
#include "chrome/browser/policy/networking/user_network_configuration_updater.h"
#include "chrome/browser/policy/networking/user_network_configuration_updater_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chromeos/ash/components/network/policy_certificate_provider.h"
#include "chromeos/constants/chromeos_features.h"
#endif  // BUILDFLAG(IS_CHROMEOS)

#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "chrome/browser/ash/kcer/kcer_factory_ash.h"
#include "chrome/browser/policy/networking/user_network_configuration_updater_ash.h"
#include "chromeos/components/kcer/kcer.h"
#include "chromeos/components/kcer/kcer_histograms.h"
#include "chromeos/components/onc/certificate_scope.h"
#endif  // BUILDFLAG(IS_CHROMEOS_ASH)

BrowserThread;

// CertificateManagerModel is created on the UI thread. It needs a
// NSSCertDatabase handle (and on ChromeOS it needs to get the TPM status) which
// needs to be done on the IO thread.
//
// The initialization flow is roughly:
//
//               UI thread                              IO Thread
//
//   CertificateManagerModel::Create
//                  \--------------------------------------v
//                                CertificateManagerModel::GetCertDBOnIOThread
//                                                         |
//                                               NssCertDatabaseGetter
//                                                         |
//                               CertificateManagerModel::DidGetCertDBOnIOThread
//                  v--------------------------------------/
// CertificateManagerModel::DidGetCertDBOnUIThread
//                  |
//     new CertificateManagerModel
//                  |
//               callback

namespace {

std::string GetCertificateOrg(CERTCertificate* cert) {}

#if BUILDFLAG(IS_CHROMEOS)
// Log message for an operation that can not be performed on a certificate of a
// given source.
constexpr char kOperationNotPermitted[] =
    "Operation not permitted on a certificate. Source: ";
#endif  // BUILDFLAG(IS_CHROMEOS)

}  // namespace

// A source of certificates that should be displayed on the certificate manager
// UI. Currently, a CertsSource yields CertInfo objects. Each CertInfo contains
// a NSS ScopedCERTCertificate.
class CertificateManagerModel::CertsSource {};

namespace {
// Provides certificates enumerable from a NSSCertDatabase.
class CertsSourcePlatformNSS : public CertificateManagerModel::CertsSource,
                               net::CertDatabase::Observer {};

#if BUILDFLAG(IS_CHROMEOS)
// Provides certificates installed through enterprise policy.
class CertsSourcePolicy : public CertificateManagerModel::CertsSource,
                          ash::PolicyCertificateProvider::Observer {
 public:
  // Defines which policy-provided certificates this CertsSourcePolicy instance
  // should yield.
  enum class Mode {
    // Only certificates which are installed by enterprise policy, but not Web
    // trusted.
    kPolicyCertsWithoutWebTrust,
    // Only certificates which are installed by enterprise policy and Web
    // trusted.
    kPolicyCertsWithWebTrust
  };

  CertsSourcePolicy(base::RepeatingClosure certs_source_updated_callback,
                    ash::PolicyCertificateProvider* policy_certs_provider,
                    Mode mode)
      : CertsSource(certs_source_updated_callback),
        policy_certs_provider_(policy_certs_provider),
        mode_(mode) {
    policy_certs_provider_->AddPolicyProvidedCertsObserver(this);
  }

  CertsSourcePolicy(const CertsSourcePolicy&) = delete;
  CertsSourcePolicy& operator=(const CertsSourcePolicy&) = delete;

  ~CertsSourcePolicy() override {
    policy_certs_provider_->RemovePolicyProvidedCertsObserver(this);
  }

  // ash::PolicyCertificateProvider::Observer
  void OnPolicyProvidedCertsChanged() override {
    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
    Refresh();
  }

  void Refresh() override {
    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
    switch (mode_) {
      case Mode::kPolicyCertsWithoutWebTrust:
        RefreshImpl(policy_certs_provider_->GetCertificatesWithoutWebTrust(
                        chromeos::onc::CertificateScope::Default()),
                    false /* policy_web_trusted */);
        break;
      case Mode::kPolicyCertsWithWebTrust:
        RefreshImpl(policy_certs_provider_->GetWebTrustedCertificates(
                        chromeos::onc::CertificateScope::Default()),
                    true /* policy_web_trusted */);
        break;
      default:
        NOTREACHED_IN_MIGRATION();
    }
  }

  bool SetCertTrust(CERTCertificate* cert,
                    net::CertType type,
                    net::NSSCertDatabase::TrustBits trust_bits) override {
    // Trust of policy-provided certificates can not be changed.
    LOG(WARNING) << kOperationNotPermitted << "Policy";
    return false;
  }

  void RemoveFromDatabase(net::ScopedCERTCertificate cert,
                          base::OnceCallback<void(bool)> callback) override {
    // Policy-provided certificates can not be deleted.
    LOG(WARNING) << kOperationNotPermitted << "Policy";
    base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
        FROM_HERE, base::BindOnce(std::move(callback), false));
  }

 private:
  void RefreshImpl(const net::CertificateList& certificates,
                   bool policy_web_trusted) {
    std::vector<std::unique_ptr<CertificateManagerModel::CertInfo>> cert_infos;
    cert_infos.reserve(certificates.size());

    for (const auto& policy_cert : certificates) {
      net::ScopedCERTCertificate nss_cert(
          net::x509_util::CreateCERTCertificateFromX509Certificate(
              policy_cert.get()));
      if (!nss_cert)
        continue;

      net::CertType type = x509_certificate_model::GetType(nss_cert.get());
      std::u16string cert_name = base::UTF8ToUTF16(
          x509_certificate_model::GetCertNameOrNickname(nss_cert.get()));
      cert_infos.push_back(std::make_unique<CertificateManagerModel::CertInfo>(
          std::move(nss_cert), type, std::move(cert_name),
          false /* can_be_deleted */, false /* untrusted */,
          CertificateManagerModel::CertInfo::Source::kPolicy,
          policy_web_trusted /* web_trust_anchor */,
          false /* hardware_backed */, false /* device_wide */));
    }

    SetCertInfos(std::move(cert_infos));
  }

  raw_ptr<ash::PolicyCertificateProvider> policy_certs_provider_;
  Mode mode_;
};

// Provides certificates made available by extensions through the
// chrome.certificateProvider API.
class CertsSourceExtensions : public CertificateManagerModel::CertsSource {
 public:
  CertsSourceExtensions(base::RepeatingClosure certs_source_updated_callback,
                        std::unique_ptr<chromeos::CertificateProvider>
                            certificate_provider_service)
      : CertsSource(certs_source_updated_callback),
        certificate_provider_service_(std::move(certificate_provider_service)) {
  }

  CertsSourceExtensions(const CertsSourceExtensions&) = delete;
  CertsSourceExtensions& operator=(const CertsSourceExtensions&) = delete;

  void Refresh() override {
    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
    certificate_provider_service_->GetCertificates(base::BindOnce(
        &CertsSourceExtensions::DidGetCerts, weak_ptr_factory_.GetWeakPtr()));
  }

  bool SetCertTrust(CERTCertificate* cert,
                    net::CertType type,
                    net::NSSCertDatabase::TrustBits trust_bits) override {
    // Extension-provided certificates are user certificates; changing trust
    // does not make sense here.
    LOG(WARNING) << kOperationNotPermitted << "Extension";
    return false;
  }

  void RemoveFromDatabase(net::ScopedCERTCertificate cert,
                          base::OnceCallback<void(bool)> callback) override {
    // Extension-provided certificates can not be deleted.
    LOG(WARNING) << kOperationNotPermitted << "Extension";
    base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
        FROM_HERE, base::BindOnce(std::move(callback), false));
  }

 private:
  void DidGetCerts(net::ClientCertIdentityList cert_identities) {
    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
    std::vector<std::unique_ptr<CertificateManagerModel::CertInfo>> cert_infos;

    cert_infos.reserve(cert_identities.size());
    for (const auto& identity : cert_identities) {
      net::ScopedCERTCertificate nss_cert(
          net::x509_util::CreateCERTCertificateFromX509Certificate(
              identity->certificate()));
      if (!nss_cert)
        continue;

      std::u16string cert_name = base::UTF8ToUTF16(
          x509_certificate_model::GetCertNameOrNickname(nss_cert.get()));
      std::u16string display_name = l10n_util::GetStringFUTF16(
          IDS_CERT_MANAGER_EXTENSION_PROVIDED_FORMAT, std::move(cert_name));

      cert_infos.push_back(std::make_unique<CertificateManagerModel::CertInfo>(
          std::move(nss_cert), net::CertType::USER_CERT /* type */,
          display_name, false /* can_be_deleted */, false /* untrusted */,
          CertificateManagerModel::CertInfo::Source::kExtension,
          false /* web_trust_anchor */, false /* hardware_backed */,
          false /* device_wide */));
    }

    SetCertInfos(std::move(cert_infos));
  }

  std::unique_ptr<chromeos::CertificateProvider> certificate_provider_service_;

  base::WeakPtrFactory<CertsSourceExtensions> weak_ptr_factory_{this};
};

#endif  // BUILDFLAG(IS_CHROMEOS)

#if BUILDFLAG(IS_CHROMEOS_ASH)

void RecordImportFromPKCS12KcerResult(
    int nss_import_result,
    base::OnceCallback<void(int nss_import_result)> callback,
    base::expected<void, kcer::Error> kcer_import_result) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);

  if (kcer_import_result.has_value()) {
    kcer::RecordPkcs12MigrationUmaEvent(
        kcer::Pkcs12MigrationUmaEvent::kPkcs12ImportKcerSuccess);
  } else {
    kcer::RecordPkcs12MigrationUmaEvent(
        kcer::Pkcs12MigrationUmaEvent::kPkcs12ImportKcerFailed);
    kcer::RecordKcerError(kcer_import_result.error());
  }

  // Just return the nss_import_result. Kcer will attempt to import only if NSS
  // succeeds and even if Kcer fails, the cert should be usable.
  return std::move(callback).Run(nss_import_result);
}

#endif  // BUILDFLAG(IS_CHROMEOS_ASH)

}  // namespace

CertificateManagerModel::CertInfo::CertInfo(net::ScopedCERTCertificate cert,
                                            net::CertType type,
                                            std::u16string name,
                                            bool can_be_deleted,
                                            bool untrusted,
                                            Source source,
                                            bool web_trust_anchor,
                                            bool hardware_backed,
                                            bool device_wide)
    :{}

CertificateManagerModel::CertInfo::~CertInfo() {}

// static
std::unique_ptr<CertificateManagerModel::CertInfo>
CertificateManagerModel::CertInfo::Clone(const CertInfo* cert_info) {}

CertificateManagerModel::Params::Params() = default;
CertificateManagerModel::Params::~Params() = default;
CertificateManagerModel::Params::Params(Params&& other) = default;

// static
void CertificateManagerModel::Create(
    content::BrowserContext* browser_context,
    CertificateManagerModel::Observer* observer,
    CreationCallback callback) {}

CertificateManagerModel::CertificateManagerModel(
    std::unique_ptr<Params> params,
    Observer* observer,
    net::NSSCertDatabase* nss_cert_database)
    :{}

CertificateManagerModel::~CertificateManagerModel() {}

void CertificateManagerModel::OnCertsSourceUpdated() {}

CertificateManagerModel::CertsSource*
CertificateManagerModel::FindCertsSourceForCert(CERTCertificate* cert) {}

void CertificateManagerModel::Refresh() {}

void CertificateManagerModel::FilterAndBuildOrgGroupingMap(
    net::CertType filter_type,
    CertificateManagerModel::OrgGroupingMap* out_org_grouping_map) const {}

void CertificateManagerModel::ImportFromPKCS12(
    PK11SlotInfo* slot_info,
    const std::string& data,
    const std::u16string& password,
    bool is_extractable,
    base::OnceCallback<void(int nss_import_result)> callback) {}

int CertificateManagerModel::ImportUserCert(const std::string& data) {}

bool CertificateManagerModel::ImportCACerts(
    const net::ScopedCERTCertificateList& certificates,
    net::NSSCertDatabase::TrustBits trust_bits,
    net::NSSCertDatabase::ImportCertFailureList* not_imported) {}

bool CertificateManagerModel::ImportServerCert(
    const net::ScopedCERTCertificateList& certificates,
    net::NSSCertDatabase::TrustBits trust_bits,
    net::NSSCertDatabase::ImportCertFailureList* not_imported) {}

bool CertificateManagerModel::SetCertTrust(
    CERTCertificate* cert,
    net::CertType type,
    net::NSSCertDatabase::TrustBits trust_bits) {}

void CertificateManagerModel::RemoveFromDatabase(
    net::ScopedCERTCertificate cert,
    base::OnceCallback<void(bool)> callback) {}

// static
void CertificateManagerModel::DidGetCertDBOnUIThread(
    std::unique_ptr<Params> params,
    CertificateManagerModel::Observer* observer,
    CreationCallback callback,
    net::NSSCertDatabase* cert_db) {}

// static
void CertificateManagerModel::DidGetCertDBOnIOThread(
    std::unique_ptr<Params> params,
    CertificateManagerModel::Observer* observer,
    CreationCallback callback,
    net::NSSCertDatabase* cert_db) {}

// static
void CertificateManagerModel::GetCertDBOnIOThread(
    std::unique_ptr<Params> params,
    NssCertDatabaseGetter database_getter,
    CertificateManagerModel::Observer* observer,
    CreationCallback callback) {}