1 // Copyright 2016 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/ui/webui/certificates_handler.h"
6 
7 #include <errno.h>
8 #include <stddef.h>
9 #include <stdint.h>
10 
11 #include <algorithm>
12 #include <map>
13 #include <utility>
14 
15 #include "base/bind.h"
16 #include "base/callback_helpers.h"
17 #include "base/files/file_util.h"  // for FileAccessProvider
18 #include "base/i18n/string_compare.h"
19 #include "base/macros.h"
20 #include "base/posix/safe_strerror.h"
21 #include "base/strings/string_number_conversions.h"
22 #include "base/strings/utf_string_conversions.h"
23 #include "base/task/post_task.h"
24 #include "base/task/task_traits.h"
25 #include "base/task/thread_pool.h"
26 #include "base/values.h"
27 #include "build/build_config.h"
28 #include "chrome/browser/browser_process.h"
29 #include "chrome/browser/certificate_viewer.h"
30 #include "chrome/browser/profiles/profile.h"
31 #include "chrome/browser/ui/certificate_dialogs.h"
32 #include "chrome/browser/ui/chrome_select_file_policy.h"
33 #include "chrome/browser/ui/crypto_module_password_dialog_nss.h"
34 #include "chrome/browser/ui/webui/certificate_viewer_webui.h"
35 #include "chrome/common/net/x509_certificate_model_nss.h"
36 #include "chrome/common/pref_names.h"
37 #include "chrome/grit/generated_resources.h"
38 #include "components/pref_registry/pref_registry_syncable.h"
39 #include "components/prefs/pref_service.h"
40 #include "content/public/browser/web_contents.h"
41 #include "net/base/net_errors.h"
42 #include "net/cert/x509_certificate.h"
43 #include "net/cert/x509_util_nss.h"
44 #include "net/der/input.h"
45 #include "net/der/parser.h"
46 #include "ui/base/l10n/l10n_util.h"
47 
48 using base::UTF8ToUTF16;
49 
50 namespace {
51 
52 // Field names for communicating certificate info to JS.
53 static const char kCertificatesHandlerEmailField[] = "email";
54 static const char kCertificatesHandlerExtractableField[] = "extractable";
55 static const char kCertificatesHandlerKeyField[] = "id";
56 static const char kCertificatesHandlerNameField[] = "name";
57 static const char kCertificatesHandlerObjSignField[] = "objSign";
58 static const char kCertificatesHandlerPolicyInstalledField[] = "policy";
59 static const char kCertificatesHandlerWebTrustAnchorField[] = "webTrustAnchor";
60 static const char kCertificatesHandlerCanBeDeletedField[] = "canBeDeleted";
61 static const char kCertificatesHandlerCanBeEditedField[] = "canBeEdited";
62 static const char kCertificatesHandlerSslField[] = "ssl";
63 static const char kCertificatesHandlerSubnodesField[] = "subnodes";
64 static const char kCertificatesHandlerContainsPolicyCertsField[] =
65     "containsPolicyCerts";
66 static const char kCertificatesHandlerUntrustedField[] = "untrusted";
67 
68 // Field names for communicating erros to JS.
69 static const char kCertificatesHandlerCertificateErrors[] = "certificateErrors";
70 static const char kCertificatesHandlerErrorDescription[] = "description";
71 static const char kCertificatesHandlerErrorField[] = "error";
72 static const char kCertificatesHandlerErrorTitle[] = "title";
73 
74 // Enumeration of different callers of SelectFile.  (Start counting at 1 so
75 // if SelectFile is accidentally called with params=nullptr it won't match any.)
76 enum {
77   EXPORT_PERSONAL_FILE_SELECTED = 1,
78   IMPORT_PERSONAL_FILE_SELECTED,
79   IMPORT_SERVER_FILE_SELECTED,
80   IMPORT_CA_FILE_SELECTED,
81 };
82 
OrgNameToId(const std::string & org)83 std::string OrgNameToId(const std::string& org) {
84   return "org-" + org;
85 }
86 
87 struct DictionaryIdComparator {
DictionaryIdComparator__anone10eb5240111::DictionaryIdComparator88   explicit DictionaryIdComparator(icu::Collator* collator)
89       : collator_(collator) {}
90 
operator ()__anone10eb5240111::DictionaryIdComparator91   bool operator()(const base::Value& a, const base::Value& b) const {
92     DCHECK(a.type() == base::Value::Type::DICTIONARY);
93     DCHECK(b.type() == base::Value::Type::DICTIONARY);
94     const base::DictionaryValue* a_dict;
95     bool a_is_dictionary = a.GetAsDictionary(&a_dict);
96     DCHECK(a_is_dictionary);
97     const base::DictionaryValue* b_dict;
98     bool b_is_dictionary = b.GetAsDictionary(&b_dict);
99     DCHECK(b_is_dictionary);
100     base::string16 a_str;
101     base::string16 b_str;
102     a_dict->GetString(kCertificatesHandlerNameField, &a_str);
103     b_dict->GetString(kCertificatesHandlerNameField, &b_str);
104     if (collator_ == nullptr)
105       return a_str < b_str;
106     return base::i18n::CompareString16WithCollator(*collator_, a_str, b_str) ==
107            UCOL_LESS;
108   }
109 
110   icu::Collator* collator_;
111 };
112 
NetErrorToString(int net_error)113 std::string NetErrorToString(int net_error) {
114   switch (net_error) {
115     // TODO(mattm): handle more cases.
116     case net::ERR_IMPORT_CA_CERT_NOT_CA:
117       return l10n_util::GetStringUTF8(
118           IDS_SETTINGS_CERTIFICATE_MANAGER_ERROR_NOT_CA);
119     case net::ERR_IMPORT_CERT_ALREADY_EXISTS:
120       return l10n_util::GetStringUTF8(
121           IDS_SETTINGS_CERTIFICATE_MANAGER_ERROR_CERT_ALREADY_EXISTS);
122     default:
123       return l10n_util::GetStringUTF8(
124           IDS_SETTINGS_CERTIFICATE_MANAGER_UNKNOWN_ERROR);
125   }
126 }
127 
128 // Struct to bind the Equals member function to an object for use in find_if.
129 struct CertEquals {
CertEquals__anone10eb5240111::CertEquals130   explicit CertEquals(CERTCertificate* cert) : cert_(cert) {}
operator ()__anone10eb5240111::CertEquals131   bool operator()(const scoped_refptr<net::X509Certificate> cert) const {
132     return net::x509_util::IsSameCertificate(cert_, cert.get());
133   }
134   CERTCertificate* cert_;
135 };
136 
137 // Determine if |data| could be a PFX Protocol Data Unit.
138 // This only does the minimum parsing necessary to distinguish a PFX file from a
139 // DER encoded Certificate.
140 //
141 // From RFC 7292 section 4:
142 //   PFX ::= SEQUENCE {
143 //       version     INTEGER {v3(3)}(v3,...),
144 //       authSafe    ContentInfo,
145 //       macData     MacData OPTIONAL
146 //   }
147 // From RFC 5280 section 4.1:
148 //   Certificate  ::=  SEQUENCE  {
149 //       tbsCertificate       TBSCertificate,
150 //       signatureAlgorithm   AlgorithmIdentifier,
151 //       signatureValue       BIT STRING  }
152 //
153 //  Certificate must be DER encoded, while PFX may be BER encoded.
154 //  Therefore PFX can be distingushed by checking if the file starts with an
155 //  indefinite SEQUENCE, or a definite SEQUENCE { INTEGER,  ... }.
CouldBePFX(const std::string & data)156 bool CouldBePFX(const std::string& data) {
157   if (data.size() < 4)
158     return false;
159 
160   // Indefinite length SEQUENCE.
161   if (data[0] == 0x30 && static_cast<uint8_t>(data[1]) == 0x80)
162     return true;
163 
164   // If the SEQUENCE is definite length, it can be parsed through the version
165   // tag using DER parser, since INTEGER must be definite length, even in BER.
166   net::der::Parser parser((net::der::Input(&data)));
167   net::der::Parser sequence_parser;
168   if (!parser.ReadSequence(&sequence_parser))
169     return false;
170   if (!sequence_parser.SkipTag(net::der::kInteger))
171     return false;
172   return true;
173 }
174 
175 }  // namespace
176 
177 namespace certificate_manager {
178 
179 ///////////////////////////////////////////////////////////////////////////////
180 //  FileAccessProvider
181 
182 // TODO(mattm): Move to some shared location?
183 class FileAccessProvider
184     : public base::RefCountedThreadSafe<FileAccessProvider> {
185  public:
186   // The first parameter is 0 on success or errno on failure. The second
187   // parameter is read result.
188   typedef base::Callback<void(const int*, const std::string*)> ReadCallback;
189 
190   // The first parameter is 0 on success or errno on failure. The second
191   // parameter is the number of bytes written on success.
192   typedef base::Callback<void(const int*, const int*)> WriteCallback;
193 
194   base::CancelableTaskTracker::TaskId StartRead(
195       const base::FilePath& path,
196       const ReadCallback& callback,
197       base::CancelableTaskTracker* tracker);
198   base::CancelableTaskTracker::TaskId StartWrite(
199       const base::FilePath& path,
200       const std::string& data,
201       const WriteCallback& callback,
202       base::CancelableTaskTracker* tracker);
203 
204  private:
205   friend class base::RefCountedThreadSafe<FileAccessProvider>;
~FileAccessProvider()206   virtual ~FileAccessProvider() {}
207 
208   // Reads file at |path|. |saved_errno| is 0 on success or errno on failure.
209   // When success, |data| has file content.
210   void DoRead(const base::FilePath& path, int* saved_errno, std::string* data);
211   // Writes data to file at |path|. |saved_errno| is 0 on success or errno on
212   // failure. When success, |bytes_written| has number of bytes written.
213   void DoWrite(const base::FilePath& path,
214                const std::string& data,
215                int* saved_errno,
216                int* bytes_written);
217 };
218 
StartRead(const base::FilePath & path,const ReadCallback & callback,base::CancelableTaskTracker * tracker)219 base::CancelableTaskTracker::TaskId FileAccessProvider::StartRead(
220     const base::FilePath& path,
221     const ReadCallback& callback,
222     base::CancelableTaskTracker* tracker) {
223   // Owned by reply callback posted below.
224   int* saved_errno = new int(0);
225   std::string* data = new std::string();
226 
227   // Post task to a background sequence to read file.
228   auto task_runner = base::ThreadPool::CreateTaskRunner(
229       {base::MayBlock(), base::TaskPriority::BEST_EFFORT});
230   return tracker->PostTaskAndReply(
231       task_runner.get(), FROM_HERE,
232       base::BindOnce(&FileAccessProvider::DoRead, this, path, saved_errno,
233                      data),
234       base::BindOnce(callback, base::Owned(saved_errno), base::Owned(data)));
235 }
236 
StartWrite(const base::FilePath & path,const std::string & data,const WriteCallback & callback,base::CancelableTaskTracker * tracker)237 base::CancelableTaskTracker::TaskId FileAccessProvider::StartWrite(
238     const base::FilePath& path,
239     const std::string& data,
240     const WriteCallback& callback,
241     base::CancelableTaskTracker* tracker) {
242   // Owned by reply callback posted below.
243   int* saved_errno = new int(0);
244   int* bytes_written = new int(0);
245 
246   // This task blocks shutdown because it saves critical user data.
247   auto task_runner = base::ThreadPool::CreateTaskRunner(
248       {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
249        base::TaskShutdownBehavior::BLOCK_SHUTDOWN});
250   return tracker->PostTaskAndReply(
251       task_runner.get(), FROM_HERE,
252       base::BindOnce(&FileAccessProvider::DoWrite, this, path, data,
253                      saved_errno, bytes_written),
254       base::BindOnce(callback, base::Owned(saved_errno),
255                      base::Owned(bytes_written)));
256 }
257 
DoRead(const base::FilePath & path,int * saved_errno,std::string * data)258 void FileAccessProvider::DoRead(const base::FilePath& path,
259                                 int* saved_errno,
260                                 std::string* data) {
261   bool success = base::ReadFileToString(path, data);
262   *saved_errno = success ? 0 : errno;
263 }
264 
DoWrite(const base::FilePath & path,const std::string & data,int * saved_errno,int * bytes_written)265 void FileAccessProvider::DoWrite(const base::FilePath& path,
266                                  const std::string& data,
267                                  int* saved_errno,
268                                  int* bytes_written) {
269   *bytes_written = base::WriteFile(path, data.data(), data.size());
270   *saved_errno = *bytes_written >= 0 ? 0 : errno;
271 }
272 
273 ///////////////////////////////////////////////////////////////////////////////
274 //  CertificatesHandler
275 
CertificatesHandler()276 CertificatesHandler::CertificatesHandler()
277     : requested_certificate_manager_model_(false),
278       use_hardware_backed_(false),
279       file_access_provider_(base::MakeRefCounted<FileAccessProvider>()) {}
280 
~CertificatesHandler()281 CertificatesHandler::~CertificatesHandler() {
282   if (select_file_dialog_.get())
283     select_file_dialog_->ListenerDestroyed();
284   select_file_dialog_ = nullptr;
285 }
286 
RegisterMessages()287 void CertificatesHandler::RegisterMessages() {
288   web_ui()->RegisterMessageCallback(
289       "viewCertificate",
290       base::BindRepeating(&CertificatesHandler::HandleViewCertificate,
291                           base::Unretained(this)));
292 
293   web_ui()->RegisterMessageCallback(
294       "getCaCertificateTrust",
295       base::BindRepeating(&CertificatesHandler::HandleGetCATrust,
296                           base::Unretained(this)));
297   web_ui()->RegisterMessageCallback(
298       "editCaCertificateTrust",
299       base::BindRepeating(&CertificatesHandler::HandleEditCATrust,
300                           base::Unretained(this)));
301 
302   web_ui()->RegisterMessageCallback(
303       "cancelImportExportCertificate",
304       base::BindRepeating(&CertificatesHandler::HandleCancelImportExportProcess,
305                           base::Unretained(this)));
306 
307   web_ui()->RegisterMessageCallback(
308       "exportPersonalCertificate",
309       base::BindRepeating(&CertificatesHandler::HandleExportPersonal,
310                           base::Unretained(this)));
311   web_ui()->RegisterMessageCallback(
312       "exportPersonalCertificatePasswordSelected",
313       base::BindRepeating(
314           &CertificatesHandler::HandleExportPersonalPasswordSelected,
315           base::Unretained(this)));
316 
317   web_ui()->RegisterMessageCallback(
318       "importPersonalCertificate",
319       base::BindRepeating(&CertificatesHandler::HandleImportPersonal,
320                           base::Unretained(this)));
321   web_ui()->RegisterMessageCallback(
322       "importPersonalCertificatePasswordSelected",
323       base::BindRepeating(
324           &CertificatesHandler::HandleImportPersonalPasswordSelected,
325           base::Unretained(this)));
326 
327   web_ui()->RegisterMessageCallback(
328       "importCaCertificate",
329       base::BindRepeating(&CertificatesHandler::HandleImportCA,
330                           base::Unretained(this)));
331   web_ui()->RegisterMessageCallback(
332       "importCaCertificateTrustSelected",
333       base::BindRepeating(&CertificatesHandler::HandleImportCATrustSelected,
334                           base::Unretained(this)));
335 
336   web_ui()->RegisterMessageCallback(
337       "importServerCertificate",
338       base::BindRepeating(&CertificatesHandler::HandleImportServer,
339                           base::Unretained(this)));
340 
341   web_ui()->RegisterMessageCallback(
342       "exportCertificate",
343       base::BindRepeating(&CertificatesHandler::HandleExportCertificate,
344                           base::Unretained(this)));
345 
346   web_ui()->RegisterMessageCallback(
347       "deleteCertificate",
348       base::BindRepeating(&CertificatesHandler::HandleDeleteCertificate,
349                           base::Unretained(this)));
350 
351   web_ui()->RegisterMessageCallback(
352       "refreshCertificates",
353       base::BindRepeating(&CertificatesHandler::HandleRefreshCertificates,
354                           base::Unretained(this)));
355 }
356 
CertificatesRefreshed()357 void CertificatesHandler::CertificatesRefreshed() {
358   PopulateTree("personalCerts", net::USER_CERT);
359   PopulateTree("serverCerts", net::SERVER_CERT);
360   PopulateTree("caCerts", net::CA_CERT);
361   PopulateTree("otherCerts", net::OTHER_CERT);
362 }
363 
FileSelected(const base::FilePath & path,int index,void * params)364 void CertificatesHandler::FileSelected(const base::FilePath& path,
365                                        int index,
366                                        void* params) {
367   switch (reinterpret_cast<intptr_t>(params)) {
368     case EXPORT_PERSONAL_FILE_SELECTED:
369       ExportPersonalFileSelected(path);
370       break;
371     case IMPORT_PERSONAL_FILE_SELECTED:
372       ImportPersonalFileSelected(path);
373       break;
374     case IMPORT_SERVER_FILE_SELECTED:
375       ImportServerFileSelected(path);
376       break;
377     case IMPORT_CA_FILE_SELECTED:
378       ImportCAFileSelected(path);
379       break;
380     default:
381       NOTREACHED();
382   }
383 }
384 
FileSelectionCanceled(void * params)385 void CertificatesHandler::FileSelectionCanceled(void* params) {
386   switch (reinterpret_cast<intptr_t>(params)) {
387     case EXPORT_PERSONAL_FILE_SELECTED:
388     case IMPORT_PERSONAL_FILE_SELECTED:
389     case IMPORT_SERVER_FILE_SELECTED:
390     case IMPORT_CA_FILE_SELECTED:
391       ImportExportCleanup();
392       RejectCallback(base::Value());
393       break;
394     default:
395       NOTREACHED();
396   }
397 }
398 
HandleViewCertificate(const base::ListValue * args)399 void CertificatesHandler::HandleViewCertificate(const base::ListValue* args) {
400   CertificateManagerModel::CertInfo* cert_info =
401       GetCertInfoFromCallbackArgs(*args, 0 /* arg_index */);
402   if (!cert_info)
403     return;
404   net::ScopedCERTCertificateList certs;
405   certs.push_back(net::x509_util::DupCERTCertificate(cert_info->cert()));
406   CertificateViewerDialog::ShowConstrained(
407       std::move(certs), web_ui()->GetWebContents(), GetParentWindow());
408 }
409 
AssignWebUICallbackId(const base::ListValue * args)410 void CertificatesHandler::AssignWebUICallbackId(const base::ListValue* args) {
411   CHECK_LE(1U, args->GetSize());
412   CHECK(webui_callback_id_.empty());
413   CHECK(args->GetString(0, &webui_callback_id_));
414 }
415 
HandleGetCATrust(const base::ListValue * args)416 void CertificatesHandler::HandleGetCATrust(const base::ListValue* args) {
417   AllowJavascript();
418 
419   CHECK_EQ(2U, args->GetSize());
420   AssignWebUICallbackId(args);
421 
422   CertificateManagerModel::CertInfo* cert_info =
423       GetCertInfoFromCallbackArgs(*args, 1 /* arg_index */);
424   if (!cert_info)
425     return;
426 
427   net::NSSCertDatabase::TrustBits trust_bits =
428       certificate_manager_model_->cert_db()->GetCertTrust(cert_info->cert(),
429                                                           net::CA_CERT);
430   std::unique_ptr<base::DictionaryValue> ca_trust_info(
431       new base::DictionaryValue);
432   ca_trust_info->SetBoolean(
433       kCertificatesHandlerSslField,
434       static_cast<bool>(trust_bits & net::NSSCertDatabase::TRUSTED_SSL));
435   ca_trust_info->SetBoolean(
436       kCertificatesHandlerEmailField,
437       static_cast<bool>(trust_bits & net::NSSCertDatabase::TRUSTED_EMAIL));
438   ca_trust_info->SetBoolean(
439       kCertificatesHandlerObjSignField,
440       static_cast<bool>(trust_bits & net::NSSCertDatabase::TRUSTED_OBJ_SIGN));
441   ResolveCallback(*ca_trust_info);
442 }
443 
HandleEditCATrust(const base::ListValue * args)444 void CertificatesHandler::HandleEditCATrust(const base::ListValue* args) {
445   CHECK_EQ(5U, args->GetSize());
446   AssignWebUICallbackId(args);
447 
448   CertificateManagerModel::CertInfo* cert_info =
449       GetCertInfoFromCallbackArgs(*args, 1 /* arg_index */);
450   if (!cert_info)
451     return;
452 
453   if (!CanEditCertificate(cert_info)) {
454     RejectCallbackWithError(
455         l10n_util::GetStringUTF8(
456             IDS_SETTINGS_CERTIFICATE_MANAGER_SET_TRUST_ERROR_TITLE),
457         l10n_util::GetStringUTF8(
458             IDS_SETTINGS_CERTIFICATE_MANAGER_ERROR_NOT_ALLOWED));
459     return;
460   }
461 
462   bool trust_ssl = false;
463   bool trust_email = false;
464   bool trust_obj_sign = false;
465   CHECK(args->GetBoolean(2, &trust_ssl));
466   CHECK(args->GetBoolean(3, &trust_email));
467   CHECK(args->GetBoolean(4, &trust_obj_sign));
468 
469   bool result = certificate_manager_model_->SetCertTrust(
470       cert_info->cert(), net::CA_CERT,
471       trust_ssl * net::NSSCertDatabase::TRUSTED_SSL +
472           trust_email * net::NSSCertDatabase::TRUSTED_EMAIL +
473           trust_obj_sign * net::NSSCertDatabase::TRUSTED_OBJ_SIGN);
474   if (!result) {
475     // TODO(mattm): better error messages?
476     RejectCallbackWithError(
477         l10n_util::GetStringUTF8(
478             IDS_SETTINGS_CERTIFICATE_MANAGER_SET_TRUST_ERROR_TITLE),
479         l10n_util::GetStringUTF8(
480             IDS_SETTINGS_CERTIFICATE_MANAGER_UNKNOWN_ERROR));
481   } else {
482     ResolveCallback(base::Value());
483   }
484 }
485 
HandleExportPersonal(const base::ListValue * args)486 void CertificatesHandler::HandleExportPersonal(const base::ListValue* args) {
487   CHECK_EQ(2U, args->GetSize());
488   AssignWebUICallbackId(args);
489 
490   CertificateManagerModel::CertInfo* cert_info =
491       GetCertInfoFromCallbackArgs(*args, 1 /* arg_index */);
492   if (!cert_info)
493     return;
494 
495   selected_cert_list_.push_back(
496       net::x509_util::DupCERTCertificate(cert_info->cert()));
497 
498   ui::SelectFileDialog::FileTypeInfo file_type_info;
499   file_type_info.extensions.resize(1);
500   file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("p12"));
501   file_type_info.extension_description_overrides.push_back(
502       l10n_util::GetStringUTF16(IDS_SETTINGS_CERTIFICATE_MANAGER_PKCS12_FILES));
503   file_type_info.include_all_files = true;
504   select_file_dialog_ = ui::SelectFileDialog::Create(
505       this,
506       std::make_unique<ChromeSelectFilePolicy>(web_ui()->GetWebContents()));
507   select_file_dialog_->SelectFile(
508       ui::SelectFileDialog::SELECT_SAVEAS_FILE, base::string16(),
509       base::FilePath(), &file_type_info, 1, FILE_PATH_LITERAL("p12"),
510       GetParentWindow(),
511       reinterpret_cast<void*>(EXPORT_PERSONAL_FILE_SELECTED));
512 }
513 
ExportPersonalFileSelected(const base::FilePath & path)514 void CertificatesHandler::ExportPersonalFileSelected(
515     const base::FilePath& path) {
516   file_path_ = path;
517   ResolveCallback(base::Value());
518 }
519 
HandleExportPersonalPasswordSelected(const base::ListValue * args)520 void CertificatesHandler::HandleExportPersonalPasswordSelected(
521     const base::ListValue* args) {
522   CHECK_EQ(2U, args->GetSize());
523   AssignWebUICallbackId(args);
524   CHECK(args->GetString(1, &password_));
525 
526   // Currently, we don't support exporting more than one at a time.  If we do,
527   // this would need to either change this to use UnlockSlotsIfNecessary or
528   // change UnlockCertSlotIfNecessary to take a CertificateList.
529   DCHECK_EQ(selected_cert_list_.size(), 1U);
530 
531   // TODO(mattm): do something smarter about non-extractable keys
532   chrome::UnlockCertSlotIfNecessary(
533       selected_cert_list_[0].get(), kCryptoModulePasswordCertExport,
534       net::HostPortPair(),  // unused.
535       GetParentWindow(),
536       base::BindOnce(&CertificatesHandler::ExportPersonalSlotsUnlocked,
537                      base::Unretained(this)));
538 }
539 
ExportPersonalSlotsUnlocked()540 void CertificatesHandler::ExportPersonalSlotsUnlocked() {
541   std::string output;
542   int num_exported = certificate_manager_model_->cert_db()->ExportToPKCS12(
543       selected_cert_list_, password_, &output);
544   if (!num_exported) {
545     RejectCallbackWithError(
546         l10n_util::GetStringUTF8(
547             IDS_SETTINGS_CERTIFICATE_MANAGER_PKCS12_EXPORT_ERROR_TITLE),
548         l10n_util::GetStringUTF8(
549             IDS_SETTINGS_CERTIFICATE_MANAGER_UNKNOWN_ERROR));
550     ImportExportCleanup();
551     return;
552   }
553   file_access_provider_->StartWrite(
554       file_path_, output,
555       base::Bind(&CertificatesHandler::ExportPersonalFileWritten,
556                  base::Unretained(this)),
557       &tracker_);
558 }
559 
ExportPersonalFileWritten(const int * write_errno,const int * bytes_written)560 void CertificatesHandler::ExportPersonalFileWritten(const int* write_errno,
561                                                     const int* bytes_written) {
562   ImportExportCleanup();
563   if (*write_errno) {
564     RejectCallbackWithError(
565         l10n_util::GetStringUTF8(
566             IDS_SETTINGS_CERTIFICATE_MANAGER_PKCS12_EXPORT_ERROR_TITLE),
567         l10n_util::GetStringFUTF8(
568             IDS_SETTINGS_CERTIFICATE_MANAGER_WRITE_ERROR_FORMAT,
569             UTF8ToUTF16(base::safe_strerror(*write_errno))));
570   } else {
571     ResolveCallback(base::Value());
572   }
573 }
574 
HandleImportPersonal(const base::ListValue * args)575 void CertificatesHandler::HandleImportPersonal(const base::ListValue* args) {
576 #if defined(OS_CHROMEOS)
577   // When policy changes while user on the certificate manager page, the UI
578   // doesn't update without page refresh and user can still see and use import
579   // button. Because of this 'return' the button will do nothing.
580   if (!IsClientCertificateManagementAllowedPolicy(Slot::kUser)) {
581     return;
582   }
583 #endif
584 
585   CHECK_EQ(2U, args->GetSize());
586   AssignWebUICallbackId(args);
587   CHECK(args->GetBoolean(1, &use_hardware_backed_));
588 
589   ui::SelectFileDialog::FileTypeInfo file_type_info;
590   file_type_info.extensions.resize(1);
591   file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("p12"));
592   file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("pfx"));
593   file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("crt"));
594   file_type_info.extension_description_overrides.push_back(
595       l10n_util::GetStringUTF16(
596           IDS_SETTINGS_CERTIFICATE_MANAGER_USAGE_SSL_CLIENT));
597   file_type_info.include_all_files = true;
598   select_file_dialog_ = ui::SelectFileDialog::Create(
599       this,
600       std::make_unique<ChromeSelectFilePolicy>(web_ui()->GetWebContents()));
601   select_file_dialog_->SelectFile(
602       ui::SelectFileDialog::SELECT_OPEN_FILE, base::string16(),
603       base::FilePath(), &file_type_info, 1, FILE_PATH_LITERAL("p12"),
604       GetParentWindow(),
605       reinterpret_cast<void*>(IMPORT_PERSONAL_FILE_SELECTED));
606 }
607 
ImportPersonalFileSelected(const base::FilePath & path)608 void CertificatesHandler::ImportPersonalFileSelected(
609     const base::FilePath& path) {
610   file_access_provider_->StartRead(
611       path,
612       base::Bind(&CertificatesHandler::ImportPersonalFileRead,
613                  base::Unretained(this)),
614       &tracker_);
615 }
616 
ImportPersonalFileRead(const int * read_errno,const std::string * data)617 void CertificatesHandler::ImportPersonalFileRead(const int* read_errno,
618                                                  const std::string* data) {
619   if (*read_errno) {
620     ImportExportCleanup();
621     RejectCallbackWithError(
622         l10n_util::GetStringUTF8(
623             IDS_SETTINGS_CERTIFICATE_MANAGER_IMPORT_ERROR_TITLE),
624         l10n_util::GetStringFUTF8(
625             IDS_SETTINGS_CERTIFICATE_MANAGER_READ_ERROR_FORMAT,
626             UTF8ToUTF16(base::safe_strerror(*read_errno))));
627     return;
628   }
629 
630   file_data_ = *data;
631 
632   if (CouldBePFX(file_data_)) {
633     ResolveCallback(base::Value(true));
634     return;
635   }
636 
637   // Non .p12/.pfx files are assumed to be single/chain certificates without
638   // private key data. The default extension according to spec is '.crt',
639   // however other extensions are also used in some places to represent these
640   // certificates.
641   int result = certificate_manager_model_->ImportUserCert(file_data_);
642   ImportExportCleanup();
643   int string_id;
644   switch (result) {
645     case net::OK:
646       ResolveCallback(base::Value(false));
647       return;
648     case net::ERR_NO_PRIVATE_KEY_FOR_CERT:
649       string_id = IDS_SETTINGS_CERTIFICATE_MANAGER_IMPORT_MISSING_KEY;
650       break;
651     case net::ERR_CERT_INVALID:
652       string_id = IDS_SETTINGS_CERTIFICATE_MANAGER_IMPORT_INVALID_FILE;
653       break;
654     default:
655       string_id = IDS_SETTINGS_CERTIFICATE_MANAGER_UNKNOWN_ERROR;
656       break;
657   }
658   RejectCallbackWithError(
659       l10n_util::GetStringUTF8(
660           IDS_SETTINGS_CERTIFICATE_MANAGER_IMPORT_ERROR_TITLE),
661       l10n_util::GetStringUTF8(string_id));
662 }
663 
HandleImportPersonalPasswordSelected(const base::ListValue * args)664 void CertificatesHandler::HandleImportPersonalPasswordSelected(
665     const base::ListValue* args) {
666   CHECK_EQ(2U, args->GetSize());
667   AssignWebUICallbackId(args);
668   CHECK(args->GetString(1, &password_));
669 
670   if (use_hardware_backed_) {
671     slot_ = certificate_manager_model_->cert_db()->GetPrivateSlot();
672   } else {
673     slot_ = certificate_manager_model_->cert_db()->GetPublicSlot();
674   }
675 
676   std::vector<crypto::ScopedPK11Slot> modules;
677   modules.push_back(crypto::ScopedPK11Slot(PK11_ReferenceSlot(slot_.get())));
678   chrome::UnlockSlotsIfNecessary(
679       std::move(modules), kCryptoModulePasswordCertImport,
680       net::HostPortPair(),  // unused.
681       GetParentWindow(),
682       base::BindOnce(&CertificatesHandler::ImportPersonalSlotUnlocked,
683                      base::Unretained(this)));
684 }
685 
ImportPersonalSlotUnlocked()686 void CertificatesHandler::ImportPersonalSlotUnlocked() {
687   // Determine if the private key should be unextractable after the import.
688   // We do this by checking the value of |use_hardware_backed_| which is set
689   // to true if importing into a hardware module. Currently, this only happens
690   // for Chrome OS when the "Import and Bind" option is chosen.
691   bool is_extractable = !use_hardware_backed_;
692   int result = certificate_manager_model_->ImportFromPKCS12(
693       slot_.get(), file_data_, password_, is_extractable);
694   ImportExportCleanup();
695   int string_id;
696   switch (result) {
697     case net::OK:
698       ResolveCallback(base::Value());
699       return;
700     case net::ERR_PKCS12_IMPORT_BAD_PASSWORD:
701       // TODO(mattm): if the error was a bad password, we should reshow the
702       // password dialog after the user dismisses the error dialog.
703       string_id = IDS_SETTINGS_CERTIFICATE_MANAGER_BAD_PASSWORD;
704       break;
705     case net::ERR_PKCS12_IMPORT_INVALID_MAC:
706       string_id = IDS_SETTINGS_CERTIFICATE_MANAGER_IMPORT_INVALID_MAC;
707       break;
708     case net::ERR_PKCS12_IMPORT_INVALID_FILE:
709       string_id = IDS_SETTINGS_CERTIFICATE_MANAGER_IMPORT_INVALID_FILE;
710       break;
711     case net::ERR_PKCS12_IMPORT_UNSUPPORTED:
712       string_id = IDS_SETTINGS_CERTIFICATE_MANAGER_IMPORT_UNSUPPORTED;
713       break;
714     default:
715       string_id = IDS_SETTINGS_CERTIFICATE_MANAGER_UNKNOWN_ERROR;
716       break;
717   }
718   RejectCallbackWithError(
719       l10n_util::GetStringUTF8(
720           IDS_SETTINGS_CERTIFICATE_MANAGER_IMPORT_ERROR_TITLE),
721       l10n_util::GetStringUTF8(string_id));
722 }
723 
HandleCancelImportExportProcess(const base::ListValue * args)724 void CertificatesHandler::HandleCancelImportExportProcess(
725     const base::ListValue* args) {
726   ImportExportCleanup();
727 }
728 
ImportExportCleanup()729 void CertificatesHandler::ImportExportCleanup() {
730   file_path_.clear();
731   password_.clear();
732   file_data_.clear();
733   use_hardware_backed_ = false;
734   selected_cert_list_.clear();
735   slot_.reset();
736   tracker_.TryCancelAll();
737 
738   // There may be pending file dialogs, we need to tell them that we've gone
739   // away so they don't try and call back to us.
740   if (select_file_dialog_.get())
741     select_file_dialog_->ListenerDestroyed();
742   select_file_dialog_ = nullptr;
743 }
744 
HandleImportServer(const base::ListValue * args)745 void CertificatesHandler::HandleImportServer(const base::ListValue* args) {
746   CHECK_EQ(1U, args->GetSize());
747   AssignWebUICallbackId(args);
748 
749   select_file_dialog_ = ui::SelectFileDialog::Create(
750       this,
751       std::make_unique<ChromeSelectFilePolicy>(web_ui()->GetWebContents()));
752   ShowCertSelectFileDialog(
753       select_file_dialog_.get(), ui::SelectFileDialog::SELECT_OPEN_FILE,
754       base::FilePath(), GetParentWindow(),
755       reinterpret_cast<void*>(IMPORT_SERVER_FILE_SELECTED));
756 }
757 
ImportServerFileSelected(const base::FilePath & path)758 void CertificatesHandler::ImportServerFileSelected(const base::FilePath& path) {
759   file_access_provider_->StartRead(
760       path,
761       base::Bind(&CertificatesHandler::ImportServerFileRead,
762                  base::Unretained(this)),
763       &tracker_);
764 }
765 
ImportServerFileRead(const int * read_errno,const std::string * data)766 void CertificatesHandler::ImportServerFileRead(const int* read_errno,
767                                                const std::string* data) {
768   if (*read_errno) {
769     ImportExportCleanup();
770     RejectCallbackWithError(
771         l10n_util::GetStringUTF8(
772             IDS_SETTINGS_CERTIFICATE_MANAGER_SERVER_IMPORT_ERROR_TITLE),
773         l10n_util::GetStringFUTF8(
774             IDS_SETTINGS_CERTIFICATE_MANAGER_READ_ERROR_FORMAT,
775             UTF8ToUTF16(base::safe_strerror(*read_errno))));
776     return;
777   }
778 
779   selected_cert_list_ = net::x509_util::CreateCERTCertificateListFromBytes(
780       data->data(), data->size(), net::X509Certificate::FORMAT_AUTO);
781   if (selected_cert_list_.empty()) {
782     ImportExportCleanup();
783     RejectCallbackWithError(
784         l10n_util::GetStringUTF8(
785             IDS_SETTINGS_CERTIFICATE_MANAGER_SERVER_IMPORT_ERROR_TITLE),
786         l10n_util::GetStringUTF8(
787             IDS_SETTINGS_CERTIFICATE_MANAGER_CERT_PARSE_ERROR));
788     return;
789   }
790 
791   net::NSSCertDatabase::ImportCertFailureList not_imported;
792   // TODO(mattm): Add UI for trust. http://crbug.com/76274
793   bool result = certificate_manager_model_->ImportServerCert(
794       selected_cert_list_, net::NSSCertDatabase::TRUST_DEFAULT, &not_imported);
795   if (!result) {
796     RejectCallbackWithError(
797         l10n_util::GetStringUTF8(
798             IDS_SETTINGS_CERTIFICATE_MANAGER_SERVER_IMPORT_ERROR_TITLE),
799         l10n_util::GetStringUTF8(
800             IDS_SETTINGS_CERTIFICATE_MANAGER_UNKNOWN_ERROR));
801   } else if (!not_imported.empty()) {
802     RejectCallbackWithImportError(
803         l10n_util::GetStringUTF8(
804             IDS_SETTINGS_CERTIFICATE_MANAGER_SERVER_IMPORT_ERROR_TITLE),
805         not_imported);
806   } else {
807     ResolveCallback(base::Value());
808   }
809   ImportExportCleanup();
810 }
811 
HandleImportCA(const base::ListValue * args)812 void CertificatesHandler::HandleImportCA(const base::ListValue* args) {
813 #if defined(OS_CHROMEOS)
814   // When policy changes while user on the certificate manager page, the UI
815   // doesn't update without page refresh and user can still see and use import
816   // button. Because of this 'return' the button will do nothing.
817   if (!IsCACertificateManagementAllowedPolicy(CertificateSource::kImported)) {
818     return;
819   }
820 #endif  // defined(OS_CHROMEOS)
821 
822   CHECK_EQ(1U, args->GetSize());
823   AssignWebUICallbackId(args);
824 
825   select_file_dialog_ = ui::SelectFileDialog::Create(
826       this,
827       std::make_unique<ChromeSelectFilePolicy>(web_ui()->GetWebContents()));
828   ShowCertSelectFileDialog(select_file_dialog_.get(),
829                            ui::SelectFileDialog::SELECT_OPEN_FILE,
830                            base::FilePath(), GetParentWindow(),
831                            reinterpret_cast<void*>(IMPORT_CA_FILE_SELECTED));
832 }
833 
ImportCAFileSelected(const base::FilePath & path)834 void CertificatesHandler::ImportCAFileSelected(const base::FilePath& path) {
835   file_access_provider_->StartRead(
836       path,
837       base::Bind(&CertificatesHandler::ImportCAFileRead,
838                  base::Unretained(this)),
839       &tracker_);
840 }
841 
ImportCAFileRead(const int * read_errno,const std::string * data)842 void CertificatesHandler::ImportCAFileRead(const int* read_errno,
843                                            const std::string* data) {
844   if (*read_errno) {
845     ImportExportCleanup();
846     RejectCallbackWithError(
847         l10n_util::GetStringUTF8(
848             IDS_SETTINGS_CERTIFICATE_MANAGER_CA_IMPORT_ERROR_TITLE),
849         l10n_util::GetStringFUTF8(
850             IDS_SETTINGS_CERTIFICATE_MANAGER_READ_ERROR_FORMAT,
851             UTF8ToUTF16(base::safe_strerror(*read_errno))));
852     return;
853   }
854 
855   selected_cert_list_ = net::x509_util::CreateCERTCertificateListFromBytes(
856       data->data(), data->size(), net::X509Certificate::FORMAT_AUTO);
857   if (selected_cert_list_.empty()) {
858     ImportExportCleanup();
859     RejectCallbackWithError(
860         l10n_util::GetStringUTF8(
861             IDS_SETTINGS_CERTIFICATE_MANAGER_CA_IMPORT_ERROR_TITLE),
862         l10n_util::GetStringUTF8(
863             IDS_SETTINGS_CERTIFICATE_MANAGER_CERT_PARSE_ERROR));
864     return;
865   }
866 
867   CERTCertificate* root_cert =
868       certificate_manager_model_->cert_db()->FindRootInList(
869           selected_cert_list_);
870 
871   // TODO(mattm): check here if root_cert is not a CA cert and show error.
872 
873   base::Value cert_name(
874       x509_certificate_model::GetSubjectDisplayName(root_cert));
875   ResolveCallback(cert_name);
876 }
877 
HandleImportCATrustSelected(const base::ListValue * args)878 void CertificatesHandler::HandleImportCATrustSelected(
879     const base::ListValue* args) {
880   CHECK_EQ(4U, args->GetSize());
881   AssignWebUICallbackId(args);
882 
883   bool trust_ssl = false;
884   bool trust_email = false;
885   bool trust_obj_sign = false;
886   CHECK(args->GetBoolean(1, &trust_ssl));
887   CHECK(args->GetBoolean(2, &trust_email));
888   CHECK(args->GetBoolean(3, &trust_obj_sign));
889 
890   // TODO(mattm): add UI for setting explicit distrust, too.
891   // http://crbug.com/128411
892   net::NSSCertDatabase::ImportCertFailureList not_imported;
893   bool result = certificate_manager_model_->ImportCACerts(
894       selected_cert_list_,
895       trust_ssl * net::NSSCertDatabase::TRUSTED_SSL +
896           trust_email * net::NSSCertDatabase::TRUSTED_EMAIL +
897           trust_obj_sign * net::NSSCertDatabase::TRUSTED_OBJ_SIGN,
898       &not_imported);
899   if (!result) {
900     RejectCallbackWithError(
901         l10n_util::GetStringUTF8(
902             IDS_SETTINGS_CERTIFICATE_MANAGER_CA_IMPORT_ERROR_TITLE),
903         l10n_util::GetStringUTF8(
904             IDS_SETTINGS_CERTIFICATE_MANAGER_UNKNOWN_ERROR));
905   } else if (!not_imported.empty()) {
906     RejectCallbackWithImportError(
907         l10n_util::GetStringUTF8(
908             IDS_SETTINGS_CERTIFICATE_MANAGER_CA_IMPORT_ERROR_TITLE),
909         not_imported);
910   } else {
911     ResolveCallback(base::Value());
912   }
913   ImportExportCleanup();
914 }
915 
HandleExportCertificate(const base::ListValue * args)916 void CertificatesHandler::HandleExportCertificate(const base::ListValue* args) {
917   CertificateManagerModel::CertInfo* cert_info =
918       GetCertInfoFromCallbackArgs(*args, 0 /* arg_index */);
919   if (!cert_info)
920     return;
921 
922   net::ScopedCERTCertificateList export_certs;
923   export_certs.push_back(net::x509_util::DupCERTCertificate(cert_info->cert()));
924   ShowCertExportDialog(web_ui()->GetWebContents(), GetParentWindow(),
925                        export_certs.begin(), export_certs.end());
926 }
927 
HandleDeleteCertificate(const base::ListValue * args)928 void CertificatesHandler::HandleDeleteCertificate(const base::ListValue* args) {
929   CHECK_EQ(2U, args->GetSize());
930   AssignWebUICallbackId(args);
931 
932   CertificateManagerModel::CertInfo* cert_info =
933       GetCertInfoFromCallbackArgs(*args, 1 /* arg_index */);
934   if (!cert_info)
935     return;
936 
937   if (!CanDeleteCertificate(cert_info)) {
938     RejectCallbackWithError(
939         l10n_util::GetStringUTF8(
940             IDS_SETTINGS_CERTIFICATE_MANAGER_DELETE_CERT_ERROR_TITLE),
941         l10n_util::GetStringUTF8(
942             IDS_SETTINGS_CERTIFICATE_MANAGER_ERROR_NOT_ALLOWED));
943     return;
944   }
945 
946   bool result = certificate_manager_model_->Delete(cert_info->cert());
947   if (!result) {
948     // TODO(mattm): better error messages?
949     RejectCallbackWithError(
950         l10n_util::GetStringUTF8(
951             IDS_SETTINGS_CERTIFICATE_MANAGER_DELETE_CERT_ERROR_TITLE),
952         l10n_util::GetStringUTF8(
953             IDS_SETTINGS_CERTIFICATE_MANAGER_UNKNOWN_ERROR));
954   } else {
955     ResolveCallback(base::Value());
956   }
957 }
958 
OnCertificateManagerModelCreated(std::unique_ptr<CertificateManagerModel> model)959 void CertificatesHandler::OnCertificateManagerModelCreated(
960     std::unique_ptr<CertificateManagerModel> model) {
961   certificate_manager_model_ = std::move(model);
962   CertificateManagerModelReady();
963 }
964 
CertificateManagerModelReady()965 void CertificatesHandler::CertificateManagerModelReady() {
966   bool client_import_allowed = true;
967   bool ca_import_allowed = true;
968 #if defined(OS_CHROMEOS)
969   client_import_allowed =
970       IsClientCertificateManagementAllowedPolicy(Slot::kUser);
971   ca_import_allowed =
972       IsCACertificateManagementAllowedPolicy(CertificateSource::kImported);
973 #endif  // defined(OS_CHROMEOS)
974   if (IsJavascriptAllowed()) {
975     FireWebUIListener("client-import-allowed-changed",
976                       base::Value(client_import_allowed));
977     FireWebUIListener("ca-import-allowed-changed",
978                       base::Value(ca_import_allowed));
979   }
980   certificate_manager_model_->Refresh();
981 }
982 
HandleRefreshCertificates(const base::ListValue * args)983 void CertificatesHandler::HandleRefreshCertificates(
984     const base::ListValue* args) {
985   AllowJavascript();
986 
987   if (certificate_manager_model_) {
988     // Already have a model, the webui must be re-loading.  Just re-run the
989     // webui initialization.
990     CertificateManagerModelReady();
991     return;
992   }
993 
994   if (!requested_certificate_manager_model_) {
995     // Request that a model be created.
996     CertificateManagerModel::Create(
997         Profile::FromWebUI(web_ui()), this,
998         base::BindOnce(&CertificatesHandler::OnCertificateManagerModelCreated,
999                        weak_ptr_factory_.GetWeakPtr()));
1000     requested_certificate_manager_model_ = true;
1001     return;
1002   }
1003 
1004   // We are already waiting for a CertificateManagerModel to be created, no need
1005   // to do anything.
1006 }
1007 
PopulateTree(const std::string & tab_name,net::CertType type)1008 void CertificatesHandler::PopulateTree(const std::string& tab_name,
1009                                        net::CertType type) {
1010   std::unique_ptr<icu::Collator> collator;
1011   UErrorCode error = U_ZERO_ERROR;
1012   collator.reset(icu::Collator::createInstance(
1013       icu::Locale(g_browser_process->GetApplicationLocale().c_str()), error));
1014   if (U_FAILURE(error))
1015     collator.reset();
1016   DictionaryIdComparator comparator(collator.get());
1017   CertificateManagerModel::OrgGroupingMap org_grouping_map;
1018 
1019   certificate_manager_model_->FilterAndBuildOrgGroupingMap(type,
1020                                                            &org_grouping_map);
1021 
1022   base::ListValue nodes;
1023   for (auto& org_grouping_map_entry : org_grouping_map) {
1024     // Populate first level (org name).
1025     base::DictionaryValue org_dict;
1026     org_dict.SetKey(kCertificatesHandlerKeyField,
1027                     base::Value(OrgNameToId(org_grouping_map_entry.first)));
1028     org_dict.SetKey(kCertificatesHandlerNameField,
1029                     base::Value(org_grouping_map_entry.first));
1030 
1031     // Populate second level (certs).
1032     base::ListValue subnodes;
1033     bool contains_policy_certs = false;
1034     for (auto& org_cert : org_grouping_map_entry.second) {
1035       // Move the CertInfo into |cert_info_id_map_|.
1036       CertificateManagerModel::CertInfo* cert_info = org_cert.get();
1037       std::string id =
1038           base::NumberToString(cert_info_id_map_.Add(std::move(org_cert)));
1039 
1040       base::DictionaryValue cert_dict;
1041       cert_dict.SetKey(kCertificatesHandlerKeyField, base::Value(id));
1042       cert_dict.SetKey(kCertificatesHandlerNameField,
1043                        base::Value(cert_info->name()));
1044       cert_dict.SetKey(kCertificatesHandlerCanBeDeletedField,
1045                        base::Value(CanDeleteCertificate(cert_info)));
1046       cert_dict.SetKey(kCertificatesHandlerCanBeEditedField,
1047                        base::Value(CanEditCertificate(cert_info)));
1048       cert_dict.SetKey(kCertificatesHandlerUntrustedField,
1049                        base::Value(cert_info->untrusted()));
1050       cert_dict.SetKey(
1051           kCertificatesHandlerPolicyInstalledField,
1052           base::Value(cert_info->source() ==
1053                       CertificateManagerModel::CertInfo::Source::kPolicy));
1054       cert_dict.SetKey(kCertificatesHandlerWebTrustAnchorField,
1055                        base::Value(cert_info->web_trust_anchor()));
1056       // TODO(hshi): This should be determined by testing for PKCS #11
1057       // CKA_EXTRACTABLE attribute. We may need to use the NSS function
1058       // PK11_ReadRawAttribute to do that.
1059       cert_dict.SetKey(kCertificatesHandlerExtractableField,
1060                        base::Value(!cert_info->hardware_backed()));
1061       // TODO(mattm): Other columns.
1062       subnodes.Append(std::move(cert_dict));
1063 
1064       contains_policy_certs |=
1065           cert_info->source() ==
1066           CertificateManagerModel::CertInfo::Source::kPolicy;
1067     }
1068     std::sort(subnodes.GetList().begin(), subnodes.GetList().end(), comparator);
1069 
1070     org_dict.SetKey(kCertificatesHandlerContainsPolicyCertsField,
1071                     base::Value(contains_policy_certs));
1072     org_dict.SetKey(kCertificatesHandlerSubnodesField, std::move(subnodes));
1073     nodes.Append(std::move(org_dict));
1074   }
1075   std::sort(nodes.GetList().begin(), nodes.GetList().end(), comparator);
1076 
1077   if (IsJavascriptAllowed()) {
1078     FireWebUIListener("certificates-changed", base::Value(tab_name),
1079                       std::move(nodes));
1080   }
1081 }
1082 
ResolveCallback(const base::Value & response)1083 void CertificatesHandler::ResolveCallback(const base::Value& response) {
1084   DCHECK(!webui_callback_id_.empty());
1085   ResolveJavascriptCallback(base::Value(webui_callback_id_), response);
1086   webui_callback_id_.clear();
1087 }
1088 
RejectCallback(const base::Value & response)1089 void CertificatesHandler::RejectCallback(const base::Value& response) {
1090   DCHECK(!webui_callback_id_.empty());
1091   RejectJavascriptCallback(base::Value(webui_callback_id_), response);
1092   webui_callback_id_.clear();
1093 }
1094 
RejectCallbackWithError(const std::string & title,const std::string & error)1095 void CertificatesHandler::RejectCallbackWithError(const std::string& title,
1096                                                   const std::string& error) {
1097   std::unique_ptr<base::DictionaryValue> error_info(new base::DictionaryValue);
1098   error_info->SetString(kCertificatesHandlerErrorTitle, title);
1099   error_info->SetString(kCertificatesHandlerErrorDescription, error);
1100   RejectCallback(*error_info);
1101 }
1102 
RejectCallbackWithImportError(const std::string & title,const net::NSSCertDatabase::ImportCertFailureList & not_imported)1103 void CertificatesHandler::RejectCallbackWithImportError(
1104     const std::string& title,
1105     const net::NSSCertDatabase::ImportCertFailureList& not_imported) {
1106   std::string error;
1107   if (selected_cert_list_.size() == 1)
1108     error = l10n_util::GetStringUTF8(
1109         IDS_SETTINGS_CERTIFICATE_MANAGER_IMPORT_SINGLE_NOT_IMPORTED);
1110   else if (not_imported.size() == selected_cert_list_.size())
1111     error = l10n_util::GetStringUTF8(
1112         IDS_SETTINGS_CERTIFICATE_MANAGER_IMPORT_ALL_NOT_IMPORTED);
1113   else
1114     error = l10n_util::GetStringUTF8(
1115         IDS_SETTINGS_CERTIFICATE_MANAGER_IMPORT_SOME_NOT_IMPORTED);
1116 
1117   std::unique_ptr<base::ListValue> cert_error_list =
1118       std::make_unique<base::ListValue>();
1119   for (size_t i = 0; i < not_imported.size(); ++i) {
1120     const net::NSSCertDatabase::ImportCertFailure& failure = not_imported[i];
1121     std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
1122     dict->SetString(kCertificatesHandlerNameField,
1123                     x509_certificate_model::GetSubjectDisplayName(
1124                         failure.certificate.get()));
1125     dict->SetString(kCertificatesHandlerErrorField,
1126                     NetErrorToString(failure.net_error));
1127     cert_error_list->Append(std::move(dict));
1128   }
1129 
1130   std::unique_ptr<base::DictionaryValue> error_info(new base::DictionaryValue);
1131   error_info->SetString(kCertificatesHandlerErrorTitle, title);
1132   error_info->SetString(kCertificatesHandlerErrorDescription, error);
1133   error_info->Set(kCertificatesHandlerCertificateErrors,
1134                   std::move(cert_error_list));
1135   RejectCallback(*error_info);
1136 }
1137 
GetParentWindow() const1138 gfx::NativeWindow CertificatesHandler::GetParentWindow() const {
1139   return web_ui()->GetWebContents()->GetTopLevelNativeWindow();
1140 }
1141 
1142 CertificateManagerModel::CertInfo*
GetCertInfoFromCallbackArgs(const base::Value & args,size_t arg_index)1143 CertificatesHandler::GetCertInfoFromCallbackArgs(const base::Value& args,
1144                                                  size_t arg_index) {
1145   if (!args.is_list())
1146     return nullptr;
1147   if (arg_index >= args.GetList().size())
1148     return nullptr;
1149   const auto& arg = args.GetList()[arg_index];
1150   if (!arg.is_string())
1151     return nullptr;
1152 
1153   int32_t cert_info_id = 0;
1154   if (!base::StringToInt(arg.GetString(), &cert_info_id))
1155     return nullptr;
1156 
1157   return cert_info_id_map_.Lookup(cert_info_id);
1158 }
1159 
1160 #if defined(OS_CHROMEOS)
IsClientCertificateManagementAllowedPolicy(Slot slot) const1161 bool CertificatesHandler::IsClientCertificateManagementAllowedPolicy(
1162     Slot slot) const {
1163   Profile* profile = Profile::FromWebUI(web_ui());
1164   PrefService* prefs = profile->GetPrefs();
1165   auto policy_value = static_cast<ClientCertificateManagementPermission>(
1166       prefs->GetInteger(prefs::kClientCertificateManagementAllowed));
1167 
1168   if (slot == Slot::kUser) {
1169     return policy_value != ClientCertificateManagementPermission::kNone;
1170   }
1171   return policy_value == ClientCertificateManagementPermission::kAll;
1172 }
1173 
IsCACertificateManagementAllowedPolicy(CertificateSource source) const1174 bool CertificatesHandler::IsCACertificateManagementAllowedPolicy(
1175     CertificateSource source) const {
1176   Profile* profile = Profile::FromWebUI(web_ui());
1177   PrefService* prefs = profile->GetPrefs();
1178   auto policy_value = static_cast<CACertificateManagementPermission>(
1179       prefs->GetInteger(prefs::kCACertificateManagementAllowed));
1180 
1181   switch (source) {
1182     case CertificateSource::kBuiltIn:
1183       return policy_value == CACertificateManagementPermission::kAll;
1184     case CertificateSource::kImported:
1185       return policy_value != CACertificateManagementPermission::kNone;
1186   }
1187 }
1188 #endif  // defined(OS_CHROMEOS)
1189 
CanDeleteCertificate(const CertificateManagerModel::CertInfo * cert_info) const1190 bool CertificatesHandler::CanDeleteCertificate(
1191     const CertificateManagerModel::CertInfo* cert_info) const {
1192   if (!cert_info->can_be_deleted() ||
1193       cert_info->source() ==
1194           CertificateManagerModel::CertInfo::Source::kPolicy) {
1195     return false;
1196   }
1197 
1198 #if defined(OS_CHROMEOS)
1199   if (cert_info->type() == net::CertType::USER_CERT) {
1200     return IsClientCertificateManagementAllowedPolicy(
1201         cert_info->device_wide() ? Slot::kSystem : Slot::kUser);
1202   }
1203   if (cert_info->type() == net::CertType::CA_CERT) {
1204     CertificateSource source = cert_info->can_be_deleted()
1205                                    ? CertificateSource::kImported
1206                                    : CertificateSource::kBuiltIn;
1207     return IsCACertificateManagementAllowedPolicy(source);
1208   }
1209 #endif  // defined(OS_CHROMEOS)
1210   return true;
1211 }
1212 
CanEditCertificate(const CertificateManagerModel::CertInfo * cert_info) const1213 bool CertificatesHandler::CanEditCertificate(
1214     const CertificateManagerModel::CertInfo* cert_info) const {
1215   if ((cert_info->type() != net::CertType::CA_CERT) ||
1216       (cert_info->source() ==
1217        CertificateManagerModel::CertInfo::Source::kPolicy)) {
1218     return false;
1219   }
1220 #if defined(OS_CHROMEOS)
1221   CertificateSource source = cert_info->can_be_deleted()
1222                                  ? CertificateSource::kImported
1223                                  : CertificateSource::kBuiltIn;
1224   return IsCACertificateManagementAllowedPolicy(source);
1225 #endif  // defined(OS_CHROMEOS)
1226   return true;
1227 }
1228 
1229 #if defined(OS_CHROMEOS)
RegisterProfilePrefs(user_prefs::PrefRegistrySyncable * registry)1230 void CertificatesHandler::RegisterProfilePrefs(
1231     user_prefs::PrefRegistrySyncable* registry) {
1232   // Allow users to manage all client certificates by default. This can be
1233   // overridden by enterprise policy.
1234   registry->RegisterIntegerPref(
1235       prefs::kClientCertificateManagementAllowed,
1236       static_cast<int>(ClientCertificateManagementPermission::kAll));
1237 
1238   // Allow users to manage all CA certificates by default. This can be
1239   // overridden by enterprise policy.
1240   registry->RegisterIntegerPref(
1241       prefs::kCACertificateManagementAllowed,
1242       static_cast<int>(CACertificateManagementPermission::kAll));
1243 }
1244 #endif  // defined(OS_CHROMEOS)
1245 
1246 }  // namespace certificate_manager
1247