// 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 "chrome/browser/certificate_manager_model.h" #include #include "base/bind.h" #include "base/logging.h" #include "base/strings/utf_string_conversions.h" #include "base/task/post_task.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/nss_util.h" #include "crypto/nss_util_internal.h" #include "net/base/net_errors.h" #include "net/cert/nss_cert_database.h" #include "net/cert/x509_certificate.h" using content::BrowserThread; namespace { net::NSSCertDatabase* g_nss_cert_database = nullptr; net::NSSCertDatabase* GetNSSCertDatabaseForResourceContext( content::ResourceContext* context, base::OnceCallback callback) { // This initialization is not thread safe. This CHECK ensures that this code // is only run on a single thread. CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); if (!g_nss_cert_database) { // Linux has only a single persistent slot compared to ChromeOS's separate // public and private slot. // Redirect any slot usage to this persistent slot on Linux. g_nss_cert_database = new net::NSSCertDatabase( crypto::ScopedPK11Slot( crypto::GetPersistentNSSKeySlot()) /* public slot */, crypto::ScopedPK11Slot( crypto::GetPersistentNSSKeySlot()) /* private slot */); } return g_nss_cert_database; } } // namespace // 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 // | // GetNSSCertDatabaseForResourceContext // | // CertificateManagerModel::DidGetCertDBOnIOThread // v--------------------------------------/ // CertificateManagerModel::DidGetCertDBOnUIThread // | // new CertificateManagerModel // | // callback // static void CertificateManagerModel::Create(content::BrowserContext* browser_context, const CreationCallback& callback) { DCHECK_CURRENTLY_ON(BrowserThread::UI); base::PostTaskWithTraits( FROM_HERE, {BrowserThread::IO}, base::BindOnce(&CertificateManagerModel::GetCertDBOnIOThread, browser_context->GetResourceContext(), callback)); } CertificateManagerModel::CertificateManagerModel( net::NSSCertDatabase* nss_cert_database, bool is_user_db_available) : cert_db_(nss_cert_database), is_user_db_available_(is_user_db_available) { DCHECK_CURRENTLY_ON(BrowserThread::UI); } CertificateManagerModel::~CertificateManagerModel() = default; int CertificateManagerModel::ImportFromPKCS12( PK11SlotInfo* slot_info, const std::string& data, const base::string16& password, bool is_extractable, net::ScopedCERTCertificateList* imported_certs) { return cert_db_->ImportFromPKCS12(slot_info, data, password, is_extractable, imported_certs); } int CertificateManagerModel::ImportUserCert(const std::string& data) { return cert_db_->ImportUserCert(data); } bool CertificateManagerModel::ImportCACerts( const net::ScopedCERTCertificateList& certificates, net::NSSCertDatabase::TrustBits trust_bits, net::NSSCertDatabase::ImportCertFailureList* not_imported) { return cert_db_->ImportCACerts(certificates, trust_bits, not_imported); } bool CertificateManagerModel::ImportServerCert( const net::ScopedCERTCertificateList& certificates, net::NSSCertDatabase::TrustBits trust_bits, net::NSSCertDatabase::ImportCertFailureList* not_imported) { return cert_db_->ImportServerCert(certificates, trust_bits, not_imported); } bool CertificateManagerModel::SetCertTrust( CERTCertificate* cert, net::CertType type, net::NSSCertDatabase::TrustBits trust_bits) { return cert_db_->SetCertTrust(cert, type, trust_bits); } bool CertificateManagerModel::Delete(CERTCertificate* cert) { return cert_db_->DeleteCertAndKey(cert); } // static void CertificateManagerModel::DidGetCertDBOnUIThread( net::NSSCertDatabase* cert_db, bool is_user_db_available, const CreationCallback& callback) { DCHECK_CURRENTLY_ON(BrowserThread::UI); std::unique_ptr model( new CertificateManagerModel(cert_db, is_user_db_available)); callback.Run(std::move(model)); } // static void CertificateManagerModel::DidGetCertDBOnIOThread( const CreationCallback& callback, net::NSSCertDatabase* cert_db) { DCHECK_CURRENTLY_ON(BrowserThread::IO); bool is_user_db_available = !!cert_db->GetPublicSlot(); base::PostTaskWithTraits( FROM_HERE, {BrowserThread::UI}, base::BindOnce(&CertificateManagerModel::DidGetCertDBOnUIThread, cert_db, is_user_db_available, callback)); } // static void CertificateManagerModel::GetCertDBOnIOThread( content::ResourceContext* context, const CreationCallback& callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); net::NSSCertDatabase* cert_db = GetNSSCertDatabaseForResourceContext( context, base::BindOnce(&CertificateManagerModel::DidGetCertDBOnIOThread, callback)); if (cert_db) DidGetCertDBOnIOThread(callback, cert_db); }