| // 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); |
| } |