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 24 using 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 51 void 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 65 CertificateManagerModel::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 77 CertificateManagerModel::~CertificateManagerModel() { 78 } 79 80 void 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 94 void 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 102 void 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 123 base::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 158 int 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 169 bool 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 179 bool 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 190 bool 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 197 bool CertificateManagerModel::Delete(net::X509Certificate* cert) { 198 bool result = cert_db_->DeleteCertAndKey(cert); 199 if (result) 200 Refresh(); 201 return result; 202 } 203 204 bool CertificateManagerModel::IsHardwareBacked( 205 const net::X509Certificate* cert) const { 206 return cert_db_->IsHardwareBacked(cert); 207 } 208 209 // static 210 void 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 224 void 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 247 void 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