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