// 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 "base/bind.h" #include "base/i18n/time_formatting.h" #include "base/logging.h" #include "base/strings/utf_string_conversions.h" #include "chrome/browser/net/nss_context.h" #include "chrome/browser/ui/crypto_module_password_dialog_nss.h" #include "chrome/common/net/x509_certificate_model.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/resource_context.h" #include "crypto/nss_util.h" #include "grit/generated_resources.h" #include "net/base/crypto_module.h" #include "net/base/net_errors.h" #include "net/cert/x509_certificate.h" #include "ui/base/l10n/l10n_util.h" using content::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 // | // GetNSSCertDatabaseForResourceContext // | // CertificateManagerModel::DidGetCertDBOnIOThread // | // crypto::IsTPMTokenEnabledForNSS // v--------------------------------------/ // CertificateManagerModel::DidGetCertDBOnUIThread // | // new CertificateManagerModel // | // callback // static void CertificateManagerModel::Create( content::BrowserContext* browser_context, CertificateManagerModel::Observer* observer, const CreationCallback& callback) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::Bind(&CertificateManagerModel::GetCertDBOnIOThread, browser_context->GetResourceContext(), observer, callback)); } CertificateManagerModel::CertificateManagerModel( net::NSSCertDatabase* nss_cert_database, bool is_user_db_available, bool is_tpm_available, Observer* observer) : cert_db_(nss_cert_database), is_user_db_available_(is_user_db_available), is_tpm_available_(is_tpm_available), observer_(observer) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); } CertificateManagerModel::~CertificateManagerModel() { } void CertificateManagerModel::Refresh() { DVLOG(1) << "refresh started"; net::CryptoModuleList modules; cert_db_->ListModules(&modules, false); DVLOG(1) << "refresh waiting for unlocking..."; chrome::UnlockSlotsIfNecessary( modules, chrome::kCryptoModulePasswordListCerts, net::HostPortPair(), // unused. NULL, // TODO(mattm): supply parent window. base::Bind(&CertificateManagerModel::RefreshSlotsUnlocked, base::Unretained(this))); } void CertificateManagerModel::RefreshSlotsUnlocked() { DVLOG(1) << "refresh listing certs..."; // TODO(tbarzic): Use async |ListCerts|. cert_db_->ListCertsSync(&cert_list_); observer_->CertificatesRefreshed(); DVLOG(1) << "refresh finished"; } void CertificateManagerModel::FilterAndBuildOrgGroupingMap( net::CertType filter_type, CertificateManagerModel::OrgGroupingMap* map) const { for (net::CertificateList::const_iterator i = cert_list_.begin(); i != cert_list_.end(); ++i) { net::X509Certificate* cert = i->get(); net::CertType type = x509_certificate_model::GetType(cert->os_cert_handle()); if (type != filter_type) continue; std::string org; if (!cert->subject().organization_names.empty()) org = cert->subject().organization_names[0]; if (org.empty()) org = cert->subject().GetDisplayName(); (*map)[org].push_back(cert); } } base::string16 CertificateManagerModel::GetColumnText( const net::X509Certificate& cert, Column column) const { base::string16 rv; switch (column) { case COL_SUBJECT_NAME: rv = base::UTF8ToUTF16( x509_certificate_model::GetCertNameOrNickname(cert.os_cert_handle())); // TODO(xiyuan): Put this into a column when we have js tree-table. if (IsHardwareBacked(&cert)) { rv = l10n_util::GetStringFUTF16( IDS_CERT_MANAGER_HARDWARE_BACKED_KEY_FORMAT, rv, l10n_util::GetStringUTF16(IDS_CERT_MANAGER_HARDWARE_BACKED)); } break; case COL_CERTIFICATE_STORE: rv = base::UTF8ToUTF16( x509_certificate_model::GetTokenName(cert.os_cert_handle())); break; case COL_SERIAL_NUMBER: rv = base::ASCIIToUTF16(x509_certificate_model::GetSerialNumberHexified( cert.os_cert_handle(), std::string())); break; case COL_EXPIRES_ON: if (!cert.valid_expiry().is_null()) rv = base::TimeFormatShortDateNumeric(cert.valid_expiry()); break; default: NOTREACHED(); } return rv; } int CertificateManagerModel::ImportFromPKCS12(net::CryptoModule* module, const std::string& data, const base::string16& password, bool is_extractable) { int result = cert_db_->ImportFromPKCS12(module, data, password, is_extractable, NULL); if (result == net::OK) Refresh(); return result; } bool CertificateManagerModel::ImportCACerts( const net::CertificateList& certificates, net::NSSCertDatabase::TrustBits trust_bits, net::NSSCertDatabase::ImportCertFailureList* not_imported) { bool result = cert_db_->ImportCACerts(certificates, trust_bits, not_imported); if (result && not_imported->size() != certificates.size()) Refresh(); return result; } bool CertificateManagerModel::ImportServerCert( const net::CertificateList& certificates, net::NSSCertDatabase::TrustBits trust_bits, net::NSSCertDatabase::ImportCertFailureList* not_imported) { bool result = cert_db_->ImportServerCert(certificates, trust_bits, not_imported); if (result && not_imported->size() != certificates.size()) Refresh(); return result; } bool CertificateManagerModel::SetCertTrust( const net::X509Certificate* cert, net::CertType type, net::NSSCertDatabase::TrustBits trust_bits) { return cert_db_->SetCertTrust(cert, type, trust_bits); } bool CertificateManagerModel::Delete(net::X509Certificate* cert) { bool result = cert_db_->DeleteCertAndKey(cert); if (result) Refresh(); return result; } bool CertificateManagerModel::IsHardwareBacked( const net::X509Certificate* cert) const { return cert_db_->IsHardwareBacked(cert); } // static void CertificateManagerModel::DidGetCertDBOnUIThread( net::NSSCertDatabase* cert_db, bool is_user_db_available, bool is_tpm_available, CertificateManagerModel::Observer* observer, const CreationCallback& callback) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); scoped_ptr<CertificateManagerModel> model(new CertificateManagerModel( cert_db, is_user_db_available, is_tpm_available, observer)); callback.Run(model.Pass()); } // static void CertificateManagerModel::DidGetCertDBOnIOThread( CertificateManagerModel::Observer* observer, const CreationCallback& callback, net::NSSCertDatabase* cert_db) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); bool is_user_db_available = cert_db->GetPublicSlot(); bool is_tpm_available = false; #if defined(OS_CHROMEOS) is_tpm_available = crypto::IsTPMTokenEnabledForNSS(); #endif BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&CertificateManagerModel::DidGetCertDBOnUIThread, cert_db, is_user_db_available, is_tpm_available, observer, callback)); } // static void CertificateManagerModel::GetCertDBOnIOThread( content::ResourceContext* context, CertificateManagerModel::Observer* observer, const CreationCallback& callback) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); net::NSSCertDatabase* cert_db = GetNSSCertDatabaseForResourceContext( context, base::Bind(&CertificateManagerModel::DidGetCertDBOnIOThread, observer, callback)); if (cert_db) DidGetCertDBOnIOThread(observer, callback, cert_db); }