chromium/chrome/browser/lacros/cert/cert_db_initializer_impl.h

// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_BROWSER_LACROS_CERT_CERT_DB_INITIALIZER_IMPL_H_
#define CHROME_BROWSER_LACROS_CERT_CERT_DB_INITIALIZER_IMPL_H_

#include <optional>

#include "base/callback_list.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "chrome/browser/lacros/cert/cert_db_initializer.h"
#include "chrome/browser/lacros/cert/cert_db_initializer_io_impl.h"
#include "chromeos/crosapi/mojom/cert_database.mojom.h"
#include "chromeos/lacros/lacros_service.h"
#include "components/keyed_service/core/keyed_service.h"
#include "crypto/scoped_nss_types.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "net/cert/cert_database.h"

class Profile;

// Initializes the certificate database in Lacros-Chrome for the specified
// profile. This object should only be accessed on the UI thread, and requires
// the Mojo CertDatabase interface to be available on the `LacrosService`.
//
// On Lacros-Chrome, how a database is initialized depends on which profile is
// being used as well as the device capabilities.
//
// - For devices with a dedicated TPM, for the main profile the public slot will
//   be in the current user's cryptohome (a path is provided by Ash) and the
//   private slot will be on the TPM (its slot id is provided by Ash).
//
// - For devices with a dedicated TPM, Ash may also indicate that Lacros should
//   use the system slot stored on the TPM. If indicated, the main profile will
//   attempt to load the system slot (its slot id is provided by Ash). In case
//   of failure, the profile won't have access to certificates from the system
//   slot (it will log an error, but won't crash).
//
// - Devices without a dedicated TPM are currently not fully
//   supported. For the main profile the public slot be in the current user's
//   cryptohome (a path is provided by Ash), the private slot will be set to a
//   read-only slot that does not support modification or permanent objects.
//   TODO(b/197082753): Make the private slot reference the public slot (same as
//   in Ash).
//
// - For all devices, secondary profiles will be initialized with both
//   public and private slots set to read-only slots that do not
//   support modification or permanent objects.
//
// - If Ash provides a path/TPM slot, and Lacros is unable to load
//   it, Lacros will intentionally crash, to avoid ignoring
//   settings from Ash.
//
// - If Ash does not provide a path, this is an unexpected/invalid
//   state, but Lacros will fail into read-only mode.
class CertDbInitializerImpl : public CertDbInitializer,
                              public KeyedService,
                              public crosapi::mojom::AshCertDatabaseObserver {
 public:
  explicit CertDbInitializerImpl(Profile* profile);
  ~CertDbInitializerImpl() override;

  // Starts the initialization. For the main profile the database will be
  // initialized based on the information provided by Ash. Secondary profiles
  // will get a read-only database without any user certificates.
  void Start();

  // CertDbInitializer
  base::CallbackListSubscription WaitUntilReady(
      base::OnceClosure callback) override;
  NssCertDatabaseGetter CreateNssCertDatabaseGetterForIOThread() override;

  // Called when there's a change in certificate database in Ash.
  // Forwards the notification to the CertDatabase.
  void OnCertsChangedInAsh(
      crosapi::mojom::CertDatabaseChangeType change_type) override;

 private:
  void InitializeForMainProfile();

  // Initializes a read-only cert database. It only has access to the built-in
  // certs and doesn't allow any modifications.
  void InitializeReadOnlyCertDb();

  // Queries Ash-Chrome for the user and system slots to use for the profile.
  // Should only be called after the software database has been loaded.
  void DidLoadSoftwareNssDb();

  // Receives information for initializing the main cert database and forwards
  // it to `cert_db_initializer_io_`.
  void OnCertDbInfoReceived(
      crosapi::mojom::GetCertDatabaseInfoResultPtr cert_db_info);

  // A part of the legacy initialization flow. Triggers legacy initialization in
  // `cert_db_initializer_io_`.
  void OnLegacyCertDbInfoReceived(
      crosapi::mojom::GetCertDatabaseInfoResultPtr cert_db_info);

  // Receives the signal that initialization is finished and notifies observers
  // about that.
  void OnCertDbInitializationFinished();

  // This class is a `KeyedService` based on the `Profile`. An instance is
  // created together with a new profile and never outlives it.`
  raw_ptr<Profile> profile_ = nullptr;
  bool is_ready_ = false;
  base::OnceClosureList callbacks_;

  // Created on the UI thread, but after that, initialized, accessed, and
  // destroyed exclusively on the IO thread. It is safe to pass unretained
  // pointers to this object into IO thread tasks because the earliest it can be
  // destroyed is in the following task posted from the destructor.
  std::unique_ptr<CertDbInitializerIOImpl> cert_db_initializer_io_;

  // Receives mojo messages from ash-chrome (under Streaming mode).
  mojo::Receiver<crosapi::mojom::AshCertDatabaseObserver> receiver_{this};

  base::WeakPtrFactory<CertDbInitializerImpl> weak_factory_{this};
};

#endif  // CHROME_BROWSER_LACROS_CERT_CERT_DB_INITIALIZER_IMPL_H_