123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677 |
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "crypto/nss_util.h"
- #include <nss.h>
- #include <pk11pub.h>
- #include <plarena.h>
- #include <prerror.h>
- #include <prinit.h>
- #include <prtime.h>
- #include <secmod.h>
- #include <map>
- #include <memory>
- #include <utility>
- #include "base/bind.h"
- #include "base/callback_list.h"
- #include "base/debug/stack_trace.h"
- #include "base/files/file_enumerator.h"
- #include "base/files/file_path.h"
- #include "base/files/file_util.h"
- #include "base/lazy_instance.h"
- #include "base/location.h"
- #include "base/logging.h"
- #include "base/no_destructor.h"
- #include "base/path_service.h"
- #include "base/strings/string_piece.h"
- #include "base/strings/stringprintf.h"
- #include "base/task/thread_pool.h"
- #include "base/threading/scoped_blocking_call.h"
- #include "base/threading/thread_checker.h"
- #include "base/threading/thread_restrictions.h"
- #include "base/threading/thread_task_runner_handle.h"
- #include "build/build_config.h"
- #include "crypto/chaps_support.h"
- #include "crypto/nss_util_internal.h"
- namespace crypto {
- namespace {
- const char kUserNSSDatabaseName[] = "UserNSSDB";
- class ChromeOSUserData {
- public:
- using SlotReadyCallback = base::OnceCallback<void(ScopedPK11Slot)>;
- explicit ChromeOSUserData(ScopedPK11Slot public_slot)
- : public_slot_(std::move(public_slot)) {}
- ~ChromeOSUserData() {
- if (public_slot_) {
- SECStatus status = CloseSoftwareNSSDB(public_slot_.get());
- if (status != SECSuccess)
- PLOG(ERROR) << "CloseSoftwareNSSDB failed: " << PORT_GetError();
- }
- }
- ScopedPK11Slot GetPublicSlot() {
- return ScopedPK11Slot(public_slot_ ? PK11_ReferenceSlot(public_slot_.get())
- : nullptr);
- }
- ScopedPK11Slot GetPrivateSlot(SlotReadyCallback callback) {
- if (private_slot_)
- return ScopedPK11Slot(PK11_ReferenceSlot(private_slot_.get()));
- if (!callback.is_null()) {
- // Callback lists cannot hold callbacks that take move-only args, since
- // Notify()ing such a list would move the arg into the first callback,
- // leaving it null or unspecified for remaining callbacks. Instead, adapt
- // the provided callbacks to accept a raw pointer, which can be copied,
- // and then wrap in a separate scoping object for each callback.
- tpm_ready_callback_list_.AddUnsafe(base::BindOnce(
- [](SlotReadyCallback callback, PK11SlotInfo* info) {
- std::move(callback).Run(ScopedPK11Slot(PK11_ReferenceSlot(info)));
- },
- std::move(callback)));
- }
- return ScopedPK11Slot();
- }
- void SetPrivateSlot(ScopedPK11Slot private_slot) {
- DCHECK(!private_slot_);
- private_slot_ = std::move(private_slot);
- tpm_ready_callback_list_.Notify(private_slot_.get());
- }
- bool private_slot_initialization_started() const {
- return private_slot_initialization_started_;
- }
- void set_private_slot_initialization_started() {
- private_slot_initialization_started_ = true;
- }
- private:
- using SlotReadyCallbackList = base::OnceCallbackList<void(PK11SlotInfo*)>;
- ScopedPK11Slot public_slot_;
- ScopedPK11Slot private_slot_;
- bool private_slot_initialization_started_ = false;
- SlotReadyCallbackList tpm_ready_callback_list_;
- };
- // Contains state used for the ChromeOSTokenManager. Unlike the
- // ChromeOSTokenManager, which is thread-checked, this object may live
- // and be accessed on multiple threads. While this is normally dangerous,
- // this is done to support callers initializing early in process startup,
- // where the threads using the objects may not be created yet, and the
- // thread startup may depend on these objects.
- // Put differently: They may be written to from any thread, if, and only
- // if, the thread they will be read from has not yet been created;
- // otherwise, this should be treated as thread-affine/thread-hostile.
- struct ChromeOSTokenManagerDataForTesting {
- static ChromeOSTokenManagerDataForTesting& GetInstance() {
- static base::NoDestructor<ChromeOSTokenManagerDataForTesting> instance;
- return *instance;
- }
- // System slot that will be used for the system slot initialization.
- ScopedPK11Slot test_system_slot;
- };
- class ChromeOSTokenManager {
- public:
- enum class State {
- // Initial state.
- kInitializationNotStarted,
- // Initialization of the TPM token was started.
- kInitializationStarted,
- // TPM token was successfully initialized, but not available to the class'
- // users yet.
- kTpmTokenInitialized,
- // TPM token was successfully enabled. It is a final state.
- kTpmTokenEnabled,
- // TPM token will never be enabled. It is a final state.
- kTpmTokenDisabled,
- };
- // Used with PostTaskAndReply to pass handles to worker thread and back.
- struct TPMModuleAndSlot {
- explicit TPMModuleAndSlot(SECMODModule* init_chaps_module)
- : chaps_module(init_chaps_module) {}
- SECMODModule* chaps_module;
- ScopedPK11Slot tpm_slot;
- };
- ScopedPK11Slot OpenPersistentNSSDBForPath(const std::string& db_name,
- const base::FilePath& path) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- // NSS is allowed to do IO on the current thread since dispatching
- // to a dedicated thread would still have the affect of blocking
- // the current thread, due to NSS's internal locking requirements
- base::ThreadRestrictions::ScopedAllowIO allow_io;
- base::FilePath nssdb_path = GetSoftwareNSSDBPath(path);
- if (!base::CreateDirectory(nssdb_path)) {
- LOG(ERROR) << "Failed to create " << nssdb_path.value() << " directory.";
- return ScopedPK11Slot();
- }
- return OpenSoftwareNSSDB(nssdb_path, db_name);
- }
- void InitializeTPMTokenAndSystemSlot(
- int system_slot_id,
- base::OnceCallback<void(bool)> callback) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- DCHECK_EQ(state_, State::kInitializationNotStarted);
- state_ = State::kInitializationStarted;
- // Note that a reference is not taken to chaps_module_. This is safe since
- // ChromeOSTokenManager is Leaky, so the reference it holds is never
- // released.
- std::unique_ptr<TPMModuleAndSlot> tpm_args(
- new TPMModuleAndSlot(chaps_module_));
- TPMModuleAndSlot* tpm_args_ptr = tpm_args.get();
- base::ThreadPool::PostTaskAndReply(
- FROM_HERE,
- {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
- base::BindOnce(&ChromeOSTokenManager::InitializeTPMTokenInThreadPool,
- system_slot_id, tpm_args_ptr),
- base::BindOnce(
- &ChromeOSTokenManager::OnInitializedTPMTokenAndSystemSlot,
- base::Unretained(this), // ChromeOSTokenManager is leaky
- std::move(callback), std::move(tpm_args)));
- }
- void FinishInitializingTPMTokenAndSystemSlot() {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- DCHECK(!IsInitializationFinished());
- // If `OnInitializedTPMTokenAndSystemSlot` was not called, but a test system
- // slot is prepared, start using it now. Can happen in tests that don't fake
- // enable TPM.
- if (!system_slot_ &&
- ChromeOSTokenManagerDataForTesting::GetInstance().test_system_slot) {
- system_slot_ = ScopedPK11Slot(
- PK11_ReferenceSlot(ChromeOSTokenManagerDataForTesting::GetInstance()
- .test_system_slot.get()));
- }
- state_ = (state_ == State::kTpmTokenInitialized) ? State::kTpmTokenEnabled
- : State::kTpmTokenDisabled;
- tpm_ready_callback_list_->Notify();
- }
- static void InitializeTPMTokenInThreadPool(CK_SLOT_ID token_slot_id,
- TPMModuleAndSlot* tpm_args) {
- // NSS functions may reenter //net via extension hooks. If the reentered
- // code needs to synchronously wait for a task to run but the thread pool in
- // which that task must run doesn't have enough threads to schedule it, a
- // deadlock occurs. To prevent that, the base::ScopedBlockingCall below
- // increments the thread pool capacity for the duration of the TPM
- // initialization.
- base::ScopedBlockingCall scoped_blocking_call(
- FROM_HERE, base::BlockingType::WILL_BLOCK);
- if (!tpm_args->chaps_module) {
- tpm_args->chaps_module = LoadChaps();
- }
- if (tpm_args->chaps_module) {
- tpm_args->tpm_slot = GetChapsSlot(tpm_args->chaps_module, token_slot_id);
- }
- }
- void OnInitializedTPMTokenAndSystemSlot(
- base::OnceCallback<void(bool)> callback,
- std::unique_ptr<TPMModuleAndSlot> tpm_args) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- DVLOG(2) << "Loaded chaps: " << !!tpm_args->chaps_module
- << ", got tpm slot: " << !!tpm_args->tpm_slot;
- chaps_module_ = tpm_args->chaps_module;
- if (ChromeOSTokenManagerDataForTesting::GetInstance().test_system_slot) {
- // chromeos_unittests try to test the TPM initialization process. If we
- // have a test DB open, pretend that it is the system slot.
- system_slot_ = ScopedPK11Slot(
- PK11_ReferenceSlot(ChromeOSTokenManagerDataForTesting::GetInstance()
- .test_system_slot.get()));
- } else {
- system_slot_ = std::move(tpm_args->tpm_slot);
- }
- if (system_slot_) {
- state_ = State::kTpmTokenInitialized;
- }
- std::move(callback).Run(!!system_slot_);
- }
- void IsTPMTokenEnabled(base::OnceCallback<void(bool)> callback) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- DCHECK(!callback.is_null());
- if (!IsInitializationFinished()) {
- // Call back to this method when initialization is finished.
- tpm_ready_callback_list_->AddUnsafe(
- base::BindOnce(&ChromeOSTokenManager::IsTPMTokenEnabled,
- base::Unretained(this) /* singleton is leaky */,
- std::move(callback)));
- return;
- }
- DCHECK(base::SequencedTaskRunnerHandle::IsSet());
- base::SequencedTaskRunnerHandle::Get()->PostTask(
- FROM_HERE,
- base::BindOnce(std::move(callback),
- /*is_tpm_enabled=*/(state_ == State::kTpmTokenEnabled)));
- }
- bool InitializeNSSForChromeOSUser(const std::string& username_hash,
- const base::FilePath& path) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- if (chromeos_user_map_.find(username_hash) != chromeos_user_map_.end()) {
- // This user already exists in our mapping.
- DVLOG(2) << username_hash << " already initialized.";
- return false;
- }
- DVLOG(2) << "Opening NSS DB " << path.value();
- std::string db_name = base::StringPrintf("%s %s", kUserNSSDatabaseName,
- username_hash.c_str());
- ScopedPK11Slot public_slot(OpenPersistentNSSDBForPath(db_name, path));
- chromeos_user_map_[username_hash] =
- std::make_unique<ChromeOSUserData>(std::move(public_slot));
- return true;
- }
- bool ShouldInitializeTPMForChromeOSUser(const std::string& username_hash) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- DCHECK(chromeos_user_map_.find(username_hash) != chromeos_user_map_.end());
- return !chromeos_user_map_[username_hash]
- ->private_slot_initialization_started();
- }
- void WillInitializeTPMForChromeOSUser(const std::string& username_hash) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- DCHECK(chromeos_user_map_.find(username_hash) != chromeos_user_map_.end());
- chromeos_user_map_[username_hash]
- ->set_private_slot_initialization_started();
- }
- void InitializeTPMForChromeOSUser(const std::string& username_hash,
- CK_SLOT_ID slot_id) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- DCHECK(chromeos_user_map_.find(username_hash) != chromeos_user_map_.end());
- DCHECK(chromeos_user_map_[username_hash]
- ->private_slot_initialization_started());
- if (!chaps_module_)
- return;
- // Note that a reference is not taken to chaps_module_. This is safe since
- // ChromeOSTokenManager is Leaky, so the reference it holds is never
- // released.
- std::unique_ptr<TPMModuleAndSlot> tpm_args(
- new TPMModuleAndSlot(chaps_module_));
- TPMModuleAndSlot* tpm_args_ptr = tpm_args.get();
- base::ThreadPool::PostTaskAndReply(
- FROM_HERE,
- {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
- base::BindOnce(&ChromeOSTokenManager::InitializeTPMTokenInThreadPool,
- slot_id, tpm_args_ptr),
- base::BindOnce(&ChromeOSTokenManager::OnInitializedTPMForChromeOSUser,
- base::Unretained(this), // ChromeOSTokenManager is leaky
- username_hash, std::move(tpm_args)));
- }
- void OnInitializedTPMForChromeOSUser(
- const std::string& username_hash,
- std::unique_ptr<TPMModuleAndSlot> tpm_args) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- DVLOG(2) << "Got tpm slot for " << username_hash << " "
- << !!tpm_args->tpm_slot;
- chromeos_user_map_[username_hash]->SetPrivateSlot(
- std::move(tpm_args->tpm_slot));
- }
- void InitializePrivateSoftwareSlotForChromeOSUser(
- const std::string& username_hash) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- VLOG(1) << "using software private slot for " << username_hash;
- DCHECK(chromeos_user_map_.find(username_hash) != chromeos_user_map_.end());
- DCHECK(chromeos_user_map_[username_hash]
- ->private_slot_initialization_started());
- if (prepared_test_private_slot_) {
- chromeos_user_map_[username_hash]->SetPrivateSlot(
- std::move(prepared_test_private_slot_));
- return;
- }
- chromeos_user_map_[username_hash]->SetPrivateSlot(
- chromeos_user_map_[username_hash]->GetPublicSlot());
- }
- ScopedPK11Slot GetPublicSlotForChromeOSUser(
- const std::string& username_hash) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- if (username_hash.empty()) {
- DVLOG(2) << "empty username_hash";
- return ScopedPK11Slot();
- }
- if (chromeos_user_map_.find(username_hash) == chromeos_user_map_.end()) {
- LOG(ERROR) << username_hash << " not initialized.";
- return ScopedPK11Slot();
- }
- return chromeos_user_map_[username_hash]->GetPublicSlot();
- }
- ScopedPK11Slot GetPrivateSlotForChromeOSUser(
- const std::string& username_hash,
- base::OnceCallback<void(ScopedPK11Slot)> callback) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- if (username_hash.empty()) {
- DVLOG(2) << "empty username_hash";
- if (!callback.is_null()) {
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(std::move(callback), ScopedPK11Slot()));
- }
- return ScopedPK11Slot();
- }
- DCHECK(chromeos_user_map_.find(username_hash) != chromeos_user_map_.end());
- return chromeos_user_map_[username_hash]->GetPrivateSlot(
- std::move(callback));
- }
- void CloseChromeOSUserForTesting(const std::string& username_hash) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- auto i = chromeos_user_map_.find(username_hash);
- DCHECK(i != chromeos_user_map_.end());
- chromeos_user_map_.erase(i);
- }
- void GetSystemNSSKeySlot(base::OnceCallback<void(ScopedPK11Slot)> callback) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- if (!IsInitializationFinished()) {
- // Call back to this method when initialization is finished.
- tpm_ready_callback_list_->AddUnsafe(
- base::BindOnce(&ChromeOSTokenManager::GetSystemNSSKeySlot,
- base::Unretained(this) /* singleton is leaky */,
- std::move(callback)));
- return;
- }
- base::SequencedTaskRunnerHandle::Get()->PostTask(
- FROM_HERE,
- base::BindOnce(std::move(callback),
- /*system_slot=*/ScopedPK11Slot(
- system_slot_ ? PK11_ReferenceSlot(system_slot_.get())
- : nullptr)));
- }
- void ResetSystemSlotForTesting() { system_slot_.reset(); }
- void ResetTokenManagerForTesting() {
- // Prevent test failures when two tests in the same process use the same
- // ChromeOSTokenManager from different threads.
- DETACH_FROM_THREAD(thread_checker_);
- state_ = State::kInitializationNotStarted;
- // Configuring chaps_module_ here is not supported yet.
- CHECK(!chaps_module_);
- // Make sure there are no outstanding callbacks between tests.
- // OnceClosureList doesn't provide a way to clear the callback list.
- tpm_ready_callback_list_ = std::make_unique<base::OnceClosureList>();
- chromeos_user_map_.clear();
- ResetSystemSlotForTesting(); // IN-TEST
- prepared_test_private_slot_.reset();
- }
- void SetPrivateSoftwareSlotForChromeOSUserForTesting(ScopedPK11Slot slot) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- // Ensure that a previous value of prepared_test_private_slot_ is not
- // overwritten. Unsetting, i.e. setting a nullptr, however is allowed.
- DCHECK(!slot || !prepared_test_private_slot_);
- prepared_test_private_slot_ = std::move(slot);
- }
- bool IsInitializationStarted() {
- return (state_ != State::kInitializationNotStarted);
- }
- private:
- friend struct base::LazyInstanceTraitsBase<ChromeOSTokenManager>;
- ChromeOSTokenManager() { EnsureNSSInit(); }
- // NOTE(willchan): We don't actually cleanup on destruction since we leak NSS
- // to prevent non-joinable threads from using NSS after it's already been
- // shut down.
- ~ChromeOSTokenManager() = delete;
- bool IsInitializationFinished() {
- switch (state_) {
- case State::kTpmTokenEnabled:
- case State::kTpmTokenDisabled:
- return true;
- case State::kInitializationNotStarted:
- case State::kInitializationStarted:
- case State::kTpmTokenInitialized:
- return false;
- }
- }
- State state_ = State::kInitializationNotStarted;
- std::unique_ptr<base::OnceClosureList> tpm_ready_callback_list_ =
- std::make_unique<base::OnceClosureList>();
- SECMODModule* chaps_module_ = nullptr;
- ScopedPK11Slot system_slot_;
- std::map<std::string, std::unique_ptr<ChromeOSUserData>> chromeos_user_map_;
- ScopedPK11Slot prepared_test_private_slot_;
- THREAD_CHECKER(thread_checker_);
- };
- base::LazyInstance<ChromeOSTokenManager>::Leaky g_token_manager =
- LAZY_INSTANCE_INITIALIZER;
- } // namespace
- base::FilePath GetSoftwareNSSDBPath(
- const base::FilePath& profile_directory_path) {
- return profile_directory_path.AppendASCII(".pki").AppendASCII("nssdb");
- }
- void GetSystemNSSKeySlot(base::OnceCallback<void(ScopedPK11Slot)> callback) {
- g_token_manager.Get().GetSystemNSSKeySlot(std::move(callback));
- }
- void PrepareSystemSlotForTesting(ScopedPK11Slot slot) {
- DCHECK(!ChromeOSTokenManagerDataForTesting::GetInstance().test_system_slot);
- DCHECK(!g_token_manager.IsCreated() ||
- !g_token_manager.Get().IsInitializationStarted())
- << "PrepareSystemSlotForTesting is called after initialization started";
- ChromeOSTokenManagerDataForTesting::GetInstance().test_system_slot =
- std::move(slot);
- }
- void ResetSystemSlotForTesting() {
- if (g_token_manager.IsCreated()) {
- g_token_manager.Get().ResetSystemSlotForTesting(); // IN-TEST
- }
- ChromeOSTokenManagerDataForTesting::GetInstance().test_system_slot.reset();
- }
- void ResetTokenManagerForTesting() {
- if (g_token_manager.IsCreated()) {
- g_token_manager.Get().ResetTokenManagerForTesting(); // IN-TEST
- }
- ResetSystemSlotForTesting(); // IN-TEST
- }
- void IsTPMTokenEnabled(base::OnceCallback<void(bool)> callback) {
- g_token_manager.Get().IsTPMTokenEnabled(std::move(callback));
- }
- void InitializeTPMTokenAndSystemSlot(int token_slot_id,
- base::OnceCallback<void(bool)> callback) {
- g_token_manager.Get().InitializeTPMTokenAndSystemSlot(token_slot_id,
- std::move(callback));
- }
- void FinishInitializingTPMTokenAndSystemSlot() {
- g_token_manager.Get().FinishInitializingTPMTokenAndSystemSlot();
- }
- bool InitializeNSSForChromeOSUser(const std::string& username_hash,
- const base::FilePath& path) {
- return g_token_manager.Get().InitializeNSSForChromeOSUser(username_hash,
- path);
- }
- bool ShouldInitializeTPMForChromeOSUser(const std::string& username_hash) {
- return g_token_manager.Get().ShouldInitializeTPMForChromeOSUser(
- username_hash);
- }
- void WillInitializeTPMForChromeOSUser(const std::string& username_hash) {
- g_token_manager.Get().WillInitializeTPMForChromeOSUser(username_hash);
- }
- void InitializeTPMForChromeOSUser(const std::string& username_hash,
- CK_SLOT_ID slot_id) {
- g_token_manager.Get().InitializeTPMForChromeOSUser(username_hash, slot_id);
- }
- void InitializePrivateSoftwareSlotForChromeOSUser(
- const std::string& username_hash) {
- g_token_manager.Get().InitializePrivateSoftwareSlotForChromeOSUser(
- username_hash);
- }
- ScopedPK11Slot GetPublicSlotForChromeOSUser(const std::string& username_hash) {
- return g_token_manager.Get().GetPublicSlotForChromeOSUser(username_hash);
- }
- ScopedPK11Slot GetPrivateSlotForChromeOSUser(
- const std::string& username_hash,
- base::OnceCallback<void(ScopedPK11Slot)> callback) {
- return g_token_manager.Get().GetPrivateSlotForChromeOSUser(
- username_hash, std::move(callback));
- }
- void CloseChromeOSUserForTesting(const std::string& username_hash) {
- g_token_manager.Get().CloseChromeOSUserForTesting(username_hash);
- }
- void SetPrivateSoftwareSlotForChromeOSUserForTesting(ScopedPK11Slot slot) {
- g_token_manager.Get().SetPrivateSoftwareSlotForChromeOSUserForTesting(
- std::move(slot));
- }
- namespace {
- void PrintDirectoryInfo(std::stringstream& ss, const base::FilePath& path) {
- base::stat_wrapper_t file_stat;
- base::File::Error error_code = base::File::Error::FILE_OK;
- if (base::File::Stat(path.value().c_str(), &file_stat) == -1) {
- error_code = base::File::OSErrorToFileError(errno);
- }
- ss << path << ", ";
- ss << std::oct << file_stat.st_mode << std::dec << ", ";
- ss << "uid " << file_stat.st_uid << ", ";
- ss << "gid " << file_stat.st_gid << std::endl;
- ss << "Enumerate error code: " << error_code << std::endl;
- }
- } // namespace
- // TODO(crbug.com/1163303): Remove when the bug is fixed.
- void DiagnosePublicSlotAndCrash(const base::FilePath& nss_path) {
- std::stringstream ss;
- ss << "Public slot is invalid." << std::endl;
- // Should be something like /home/chronos/u-<hash>/.pki/nssdb .
- ss << "NSS path: " << nss_path << std::endl;
- {
- // NSS files like pkcs11.txt, cert9.db, key4.db .
- base::FileEnumerator files(
- nss_path, /*recursive=*/false,
- /*file_type=*/base::FileEnumerator::FILES,
- /*pattern=*/base::FilePath::StringType(),
- base::FileEnumerator::FolderSearchPolicy::MATCH_ONLY,
- base::FileEnumerator::ErrorPolicy::STOP_ENUMERATION);
- ss << "Public slot database files:" << std::endl;
- for (base::FilePath path = files.Next(); !path.empty();
- path = files.Next()) {
- base::FileEnumerator::FileInfo file_info = files.GetInfo();
- ss << file_info.GetName() << ", ";
- ss << std::oct << file_info.stat().st_mode << std::dec << ", ";
- ss << "uid " << file_info.stat().st_uid << ", ";
- ss << "gid " << file_info.stat().st_gid << ", ";
- ss << file_info.stat().st_size << " bytes, ";
- char buf[16];
- int read_result = base::ReadFile(path, buf, sizeof(buf) - 1);
- ss << ((read_result > 0) ? "readable" : "not readable");
- ss << std::endl;
- }
- ss << "Enumerate error code: " << files.GetError() << std::endl;
- }
- // NSS directory might not be created yet, also check parent directories.
- // Use u-hash directory as a comparison point for user and group ids and
- // access permissions.
- base::FilePath nssdb_path = nss_path.Append(base::FilePath::kParentDirectory);
- PrintDirectoryInfo(ss, nssdb_path);
- base::FilePath pki_path = nssdb_path.Append(base::FilePath::kParentDirectory);
- PrintDirectoryInfo(ss, pki_path);
- base::FilePath u_hash_path =
- pki_path.Append(base::FilePath::kParentDirectory);
- PrintDirectoryInfo(ss, u_hash_path);
- {
- // Check whether the NSS path exists, and if not, check whether it's
- // possible to create it.
- if (base::DirectoryExists(nss_path)) {
- ss << "NSS path exists." << std::endl;
- } else {
- base::File::Error error = base::File::Error::FILE_OK;
- if (base::CreateDirectoryAndGetError(nss_path, &error)) {
- ss << "NSS path didn't exist. Created successfully." << std::endl;
- } else {
- ss << "NSS path didn't exist. Failed to create, error: " << error
- << std::endl;
- }
- }
- }
- CHECK(false) << ss.str();
- }
- } // namespace crypto
|