1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chrome/browser/certificate_manager_model.h" 6 7#include "base/bind.h" 8#include "base/i18n/time_formatting.h" 9#include "base/logging.h" 10#include "base/strings/utf_string_conversions.h" 11#include "chrome/browser/net/nss_context.h" 12#include "chrome/browser/ui/crypto_module_password_dialog_nss.h" 13#include "chrome/common/net/x509_certificate_model.h" 14#include "chrome/grit/generated_resources.h" 15#include "content/public/browser/browser_context.h" 16#include "content/public/browser/browser_thread.h" 17#include "content/public/browser/resource_context.h" 18#include "crypto/nss_util.h" 19#include "net/base/crypto_module.h" 20#include "net/base/net_errors.h" 21#include "net/cert/x509_certificate.h" 22#include "ui/base/l10n/l10n_util.h" 23 24using content::BrowserThread; 25 26// CertificateManagerModel is created on the UI thread. It needs a 27// NSSCertDatabase handle (and on ChromeOS it needs to get the TPM status) which 28// needs to be done on the IO thread. 29// 30// The initialization flow is roughly: 31// 32// UI thread IO Thread 33// 34// CertificateManagerModel::Create 35// \--------------------------------------v 36// CertificateManagerModel::GetCertDBOnIOThread 37// | 38// GetNSSCertDatabaseForResourceContext 39// | 40// CertificateManagerModel::DidGetCertDBOnIOThread 41// | 42// crypto::IsTPMTokenEnabledForNSS 43// v--------------------------------------/ 44// CertificateManagerModel::DidGetCertDBOnUIThread 45// | 46// new CertificateManagerModel 47// | 48// callback 49 50// static 51void CertificateManagerModel::Create( 52 content::BrowserContext* browser_context, 53 CertificateManagerModel::Observer* observer, 54 const CreationCallback& callback) { 55 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 56 BrowserThread::PostTask( 57 BrowserThread::IO, 58 FROM_HERE, 59 base::Bind(&CertificateManagerModel::GetCertDBOnIOThread, 60 browser_context->GetResourceContext(), 61 observer, 62 callback)); 63} 64 65CertificateManagerModel::CertificateManagerModel( 66 net::NSSCertDatabase* nss_cert_database, 67 bool is_user_db_available, 68 bool is_tpm_available, 69 Observer* observer) 70 : cert_db_(nss_cert_database), 71 is_user_db_available_(is_user_db_available), 72 is_tpm_available_(is_tpm_available), 73 observer_(observer) { 74 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 75} 76 77CertificateManagerModel::~CertificateManagerModel() { 78} 79 80void CertificateManagerModel::Refresh() { 81 DVLOG(1) << "refresh started"; 82 net::CryptoModuleList modules; 83 cert_db_->ListModules(&modules, false); 84 DVLOG(1) << "refresh waiting for unlocking..."; 85 chrome::UnlockSlotsIfNecessary( 86 modules, 87 chrome::kCryptoModulePasswordListCerts, 88 net::HostPortPair(), // unused. 89 NULL, // TODO(mattm): supply parent window. 90 base::Bind(&CertificateManagerModel::RefreshSlotsUnlocked, 91 base::Unretained(this))); 92} 93 94void CertificateManagerModel::RefreshSlotsUnlocked() { 95 DVLOG(1) << "refresh listing certs..."; 96 // TODO(tbarzic): Use async |ListCerts|. 97 cert_db_->ListCertsSync(&cert_list_); 98 observer_->CertificatesRefreshed(); 99 DVLOG(1) << "refresh finished"; 100} 101 102void CertificateManagerModel::FilterAndBuildOrgGroupingMap( 103 net::CertType filter_type, 104 CertificateManagerModel::OrgGroupingMap* map) const { 105 for (net::CertificateList::const_iterator i = cert_list_.begin(); 106 i != cert_list_.end(); ++i) { 107 net::X509Certificate* cert = i->get(); 108 net::CertType type = 109 x509_certificate_model::GetType(cert->os_cert_handle()); 110 if (type != filter_type) 111 continue; 112 113 std::string org; 114 if (!cert->subject().organization_names.empty()) 115 org = cert->subject().organization_names[0]; 116 if (org.empty()) 117 org = cert->subject().GetDisplayName(); 118 119 (*map)[org].push_back(cert); 120 } 121} 122 123base::string16 CertificateManagerModel::GetColumnText( 124 const net::X509Certificate& cert, 125 Column column) const { 126 base::string16 rv; 127 switch (column) { 128 case COL_SUBJECT_NAME: 129 rv = base::UTF8ToUTF16( 130 x509_certificate_model::GetCertNameOrNickname(cert.os_cert_handle())); 131 132 // TODO(xiyuan): Put this into a column when we have js tree-table. 133 if (IsHardwareBacked(&cert)) { 134 rv = l10n_util::GetStringFUTF16( 135 IDS_CERT_MANAGER_HARDWARE_BACKED_KEY_FORMAT, 136 rv, 137 l10n_util::GetStringUTF16(IDS_CERT_MANAGER_HARDWARE_BACKED)); 138 } 139 break; 140 case COL_CERTIFICATE_STORE: 141 rv = base::UTF8ToUTF16( 142 x509_certificate_model::GetTokenName(cert.os_cert_handle())); 143 break; 144 case COL_SERIAL_NUMBER: 145 rv = base::ASCIIToUTF16(x509_certificate_model::GetSerialNumberHexified( 146 cert.os_cert_handle(), std::string())); 147 break; 148 case COL_EXPIRES_ON: 149 if (!cert.valid_expiry().is_null()) 150 rv = base::TimeFormatShortDateNumeric(cert.valid_expiry()); 151 break; 152 default: 153 NOTREACHED(); 154 } 155 return rv; 156} 157 158int CertificateManagerModel::ImportFromPKCS12(net::CryptoModule* module, 159 const std::string& data, 160 const base::string16& password, 161 bool is_extractable) { 162 int result = cert_db_->ImportFromPKCS12(module, data, password, 163 is_extractable, NULL); 164 if (result == net::OK) 165 Refresh(); 166 return result; 167} 168 169bool CertificateManagerModel::ImportCACerts( 170 const net::CertificateList& certificates, 171 net::NSSCertDatabase::TrustBits trust_bits, 172 net::NSSCertDatabase::ImportCertFailureList* not_imported) { 173 bool result = cert_db_->ImportCACerts(certificates, trust_bits, not_imported); 174 if (result && not_imported->size() != certificates.size()) 175 Refresh(); 176 return result; 177} 178 179bool CertificateManagerModel::ImportServerCert( 180 const net::CertificateList& certificates, 181 net::NSSCertDatabase::TrustBits trust_bits, 182 net::NSSCertDatabase::ImportCertFailureList* not_imported) { 183 bool result = cert_db_->ImportServerCert(certificates, trust_bits, 184 not_imported); 185 if (result && not_imported->size() != certificates.size()) 186 Refresh(); 187 return result; 188} 189 190bool CertificateManagerModel::SetCertTrust( 191 const net::X509Certificate* cert, 192 net::CertType type, 193 net::NSSCertDatabase::TrustBits trust_bits) { 194 return cert_db_->SetCertTrust(cert, type, trust_bits); 195} 196 197bool CertificateManagerModel::Delete(net::X509Certificate* cert) { 198 bool result = cert_db_->DeleteCertAndKey(cert); 199 if (result) 200 Refresh(); 201 return result; 202} 203 204bool CertificateManagerModel::IsHardwareBacked( 205 const net::X509Certificate* cert) const { 206 return cert_db_->IsHardwareBacked(cert); 207} 208 209// static 210void CertificateManagerModel::DidGetCertDBOnUIThread( 211 net::NSSCertDatabase* cert_db, 212 bool is_user_db_available, 213 bool is_tpm_available, 214 CertificateManagerModel::Observer* observer, 215 const CreationCallback& callback) { 216 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 217 218 scoped_ptr<CertificateManagerModel> model(new CertificateManagerModel( 219 cert_db, is_user_db_available, is_tpm_available, observer)); 220 callback.Run(model.Pass()); 221} 222 223// static 224void CertificateManagerModel::DidGetCertDBOnIOThread( 225 CertificateManagerModel::Observer* observer, 226 const CreationCallback& callback, 227 net::NSSCertDatabase* cert_db) { 228 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 229 230 bool is_user_db_available = cert_db->GetPublicSlot(); 231 bool is_tpm_available = false; 232#if defined(OS_CHROMEOS) 233 is_tpm_available = crypto::IsTPMTokenEnabledForNSS(); 234#endif 235 BrowserThread::PostTask( 236 BrowserThread::UI, 237 FROM_HERE, 238 base::Bind(&CertificateManagerModel::DidGetCertDBOnUIThread, 239 cert_db, 240 is_user_db_available, 241 is_tpm_available, 242 observer, 243 callback)); 244} 245 246// static 247void CertificateManagerModel::GetCertDBOnIOThread( 248 content::ResourceContext* context, 249 CertificateManagerModel::Observer* observer, 250 const CreationCallback& callback) { 251 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 252 net::NSSCertDatabase* cert_db = GetNSSCertDatabaseForResourceContext( 253 context, 254 base::Bind(&CertificateManagerModel::DidGetCertDBOnIOThread, 255 observer, 256 callback)); 257 if (cert_db) 258 DidGetCertDBOnIOThread(observer, callback, cert_db); 259} 260