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, ¬_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 ¬_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