ozone: evdev: Sync caps lock LED state to evdev
[chromium-blink-merge.git] / chrome / browser / password_manager / native_backend_libsecret.cc
blob43673a3532b6f968a288df3228d4b56ad4fbd435
1 // Copyright (c) 2015 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.
5 #include "chrome/browser/password_manager/native_backend_libsecret.h"
7 #include <dlfcn.h>
8 #include <list>
10 #include "base/basictypes.h"
11 #include "base/logging.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/metrics/histogram.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/strings/utf_string_conversions.h"
18 using autofill::PasswordForm;
19 using base::UTF8ToUTF16;
20 using base::UTF16ToUTF8;
22 namespace {
23 const char kEmptyString[] = "";
24 const int kMaxPossibleTimeTValue = std::numeric_limits<int>::max();
27 typeof(&::secret_password_store_sync)
28 LibsecretLoader::secret_password_store_sync;
29 typeof(&::secret_service_search_sync)
30 LibsecretLoader::secret_service_search_sync;
31 typeof(&::secret_password_clear_sync)
32 LibsecretLoader::secret_password_clear_sync;
33 typeof(&::secret_item_get_secret) LibsecretLoader::secret_item_get_secret;
34 typeof(&::secret_value_get_text) LibsecretLoader::secret_value_get_text;
35 typeof(&::secret_item_get_attributes)
36 LibsecretLoader::secret_item_get_attributes;
37 typeof(&::secret_item_load_secret_sync)
38 LibsecretLoader::secret_item_load_secret_sync;
39 typeof(&::secret_value_unref) LibsecretLoader::secret_value_unref;
41 bool LibsecretLoader::libsecret_loaded = false;
43 const LibsecretLoader::FunctionInfo LibsecretLoader::functions[] = {
44 {"secret_password_store_sync",
45 reinterpret_cast<void**>(&secret_password_store_sync)},
46 {"secret_service_search_sync",
47 reinterpret_cast<void**>(&secret_service_search_sync)},
48 {"secret_password_clear_sync",
49 reinterpret_cast<void**>(&secret_password_clear_sync)},
50 {"secret_item_get_secret",
51 reinterpret_cast<void**>(&secret_item_get_secret)},
52 {"secret_value_get_text", reinterpret_cast<void**>(&secret_value_get_text)},
53 {"secret_item_get_attributes",
54 reinterpret_cast<void**>(&secret_item_get_attributes)},
55 {"secret_item_load_secret_sync",
56 reinterpret_cast<void**>(&secret_item_load_secret_sync)},
57 {"secret_value_unref", reinterpret_cast<void**>(&secret_value_unref)},
58 {nullptr, nullptr}};
60 bool LibsecretLoader::LoadLibsecret() {
61 if (libsecret_loaded)
62 return true;
64 void* handle = dlopen("libsecret-1.so.0", RTLD_NOW | RTLD_GLOBAL);
65 if (!handle) {
66 // We wanted to use libsecret, but we couldn't load it. Warn, because
67 // either the user asked for this, or we autodetected it incorrectly. (Or
68 // the system has broken libraries, which is also good to warn about.)
69 VLOG(1) << "Could not load libsecret-1.so.0: " << dlerror();
70 return false;
73 for (size_t i = 0; functions[i].name; ++i) {
74 dlerror();
75 *functions[i].pointer = dlsym(handle, functions[i].name);
76 const char* error = dlerror();
77 if (error) {
78 VLOG(1) << "Unable to load symbol " << functions[i].name << ": " << error;
79 dlclose(handle);
80 return false;
84 libsecret_loaded = true;
85 // We leak the library handle. That's OK: this function is called only once.
86 return true;
89 namespace {
91 const char kLibsecretAppString[] = "chrome";
93 // Schema is analagous to the fields in PasswordForm.
94 // TODO(gcasto): Adding 'form_data' would be nice, but we would need to
95 // serialize in a way that is guaranteed to not have any embedded NULLs. Pickle
96 // doesn't make this guarantee, so we just don't serialize this field. Since
97 // it's only used to crowd source data collection it doesn't matter that much
98 // if it's not available on this platform.
99 const SecretSchema kLibsecretSchema = {
100 "chrome_libsecret_password_schema",
101 // We have to use SECRET_SCHEMA_DONT_MATCH_NAME in order to get old
102 // passwords stored with gnome_keyring.
103 SECRET_SCHEMA_DONT_MATCH_NAME,
104 {{"origin_url", SECRET_SCHEMA_ATTRIBUTE_STRING},
105 {"action_url", SECRET_SCHEMA_ATTRIBUTE_STRING},
106 {"username_element", SECRET_SCHEMA_ATTRIBUTE_STRING},
107 {"username_value", SECRET_SCHEMA_ATTRIBUTE_STRING},
108 {"password_element", SECRET_SCHEMA_ATTRIBUTE_STRING},
109 {"submit_element", SECRET_SCHEMA_ATTRIBUTE_STRING},
110 {"signon_realm", SECRET_SCHEMA_ATTRIBUTE_STRING},
111 {"ssl_valid", SECRET_SCHEMA_ATTRIBUTE_INTEGER},
112 {"preferred", SECRET_SCHEMA_ATTRIBUTE_INTEGER},
113 {"date_created", SECRET_SCHEMA_ATTRIBUTE_STRING},
114 {"blacklisted_by_user", SECRET_SCHEMA_ATTRIBUTE_INTEGER},
115 {"scheme", SECRET_SCHEMA_ATTRIBUTE_INTEGER},
116 {"type", SECRET_SCHEMA_ATTRIBUTE_INTEGER},
117 {"times_used", SECRET_SCHEMA_ATTRIBUTE_INTEGER},
118 {"date_synced", SECRET_SCHEMA_ATTRIBUTE_STRING},
119 {"display_name", SECRET_SCHEMA_ATTRIBUTE_STRING},
120 {"avatar_url", SECRET_SCHEMA_ATTRIBUTE_STRING},
121 {"federation_url", SECRET_SCHEMA_ATTRIBUTE_STRING},
122 {"skip_zero_click", SECRET_SCHEMA_ATTRIBUTE_INTEGER},
123 {"generation_upload_status", SECRET_SCHEMA_ATTRIBUTE_INTEGER},
124 // This field is always "chrome-profile_id" so that we can search for it.
125 {"application", SECRET_SCHEMA_ATTRIBUTE_STRING},
126 {nullptr, SECRET_SCHEMA_ATTRIBUTE_STRING}}};
128 const char* GetStringFromAttributes(GHashTable* attrs, const char* keyname) {
129 gpointer value = g_hash_table_lookup(attrs, keyname);
130 return value ? static_cast<char*>(value) : kEmptyString;
133 uint32_t GetUintFromAttributes(GHashTable* attrs, const char* keyname) {
134 gpointer value = g_hash_table_lookup(attrs, keyname);
135 if (!value)
136 return uint32_t();
137 uint32_t result;
138 bool value_ok = base::StringToUint(static_cast<char*>(value), &result);
139 DCHECK(value_ok);
140 return result;
143 // Convert the attributes into a new PasswordForm.
144 // Note: does *not* get the actual password, as that is not a key attribute!
145 // Returns nullptr if the attributes are for the wrong application.
146 scoped_ptr<PasswordForm> FormOutOfAttributes(GHashTable* attrs) {
147 base::StringPiece app_value = GetStringFromAttributes(attrs, "application");
148 if (!app_value.starts_with(kLibsecretAppString))
149 return scoped_ptr<PasswordForm>();
151 scoped_ptr<PasswordForm> form(new PasswordForm());
152 form->origin = GURL(GetStringFromAttributes(attrs, "origin_url"));
153 form->action = GURL(GetStringFromAttributes(attrs, "action_url"));
154 form->username_element =
155 UTF8ToUTF16(GetStringFromAttributes(attrs, "username_element"));
156 form->username_value =
157 UTF8ToUTF16(GetStringFromAttributes(attrs, "username_value"));
158 form->password_element =
159 UTF8ToUTF16(GetStringFromAttributes(attrs, "password_element"));
160 form->submit_element =
161 UTF8ToUTF16(GetStringFromAttributes(attrs, "submit_element"));
162 form->signon_realm = GetStringFromAttributes(attrs, "signon_realm");
163 form->ssl_valid = GetUintFromAttributes(attrs, "ssl_valid");
164 form->preferred = GetUintFromAttributes(attrs, "preferred");
165 int64 date_created = 0;
166 bool date_ok = base::StringToInt64(
167 GetStringFromAttributes(attrs, "date_created"), &date_created);
168 DCHECK(date_ok);
169 // In the past |date_created| was stored as time_t. Currently is stored as
170 // base::Time's internal value. We need to distinguish, which format the
171 // number in |date_created| was stored in. We use the fact that
172 // kMaxPossibleTimeTValue interpreted as the internal value corresponds to an
173 // unlikely date back in 17th century, and anything above
174 // kMaxPossibleTimeTValue clearly must be in the internal value format.
175 form->date_created = date_created < kMaxPossibleTimeTValue
176 ? base::Time::FromTimeT(date_created)
177 : base::Time::FromInternalValue(date_created);
178 form->blacklisted_by_user =
179 GetUintFromAttributes(attrs, "blacklisted_by_user");
180 form->type =
181 static_cast<PasswordForm::Type>(GetUintFromAttributes(attrs, "type"));
182 form->times_used = GetUintFromAttributes(attrs, "times_used");
183 form->scheme =
184 static_cast<PasswordForm::Scheme>(GetUintFromAttributes(attrs, "scheme"));
185 int64 date_synced = 0;
186 base::StringToInt64(GetStringFromAttributes(attrs, "date_synced"),
187 &date_synced);
188 form->date_synced = base::Time::FromInternalValue(date_synced);
189 form->display_name =
190 UTF8ToUTF16(GetStringFromAttributes(attrs, "display_name"));
191 form->avatar_url = GURL(GetStringFromAttributes(attrs, "avatar_url"));
192 form->federation_url = GURL(GetStringFromAttributes(attrs, "federation_url"));
193 form->skip_zero_click = GetUintFromAttributes(attrs, "skip_zero_click");
194 form->generation_upload_status =
195 static_cast<PasswordForm::GenerationUploadStatus>(
196 GetUintFromAttributes(attrs, "generation_upload_status"));
198 return form.Pass();
201 class LibsecretAttributesBuilder {
202 public:
203 LibsecretAttributesBuilder();
204 ~LibsecretAttributesBuilder();
205 void Append(const std::string& name, const std::string& value);
206 void Append(const std::string& name, int64 value);
207 // GHashTable, its keys and values returned from Get() are destroyed in
208 // |LibsecretAttributesBuilder| desctructor.
209 GHashTable* Get() { return attrs_; }
211 private:
212 // |name_values_| is a storage for strings referenced in |attrs_|.
213 std::list<std::string> name_values_;
214 GHashTable* attrs_;
217 LibsecretAttributesBuilder::LibsecretAttributesBuilder() {
218 attrs_ = g_hash_table_new_full(g_str_hash, g_str_equal,
219 nullptr, // no deleter for keys
220 nullptr); // no deleter for values
223 LibsecretAttributesBuilder::~LibsecretAttributesBuilder() {
224 g_hash_table_destroy(attrs_);
227 void LibsecretAttributesBuilder::Append(const std::string& name,
228 const std::string& value) {
229 name_values_.push_back(name);
230 gpointer name_str =
231 static_cast<gpointer>(const_cast<char*>(name_values_.back().c_str()));
232 name_values_.push_back(value);
233 gpointer value_str =
234 static_cast<gpointer>(const_cast<char*>(name_values_.back().c_str()));
235 g_hash_table_insert(attrs_, name_str, value_str);
238 void LibsecretAttributesBuilder::Append(const std::string& name, int64 value) {
239 Append(name, base::Int64ToString(value));
242 } // namespace
244 bool LibsecretLoader::LibsecretIsAvailable() {
245 if (!libsecret_loaded)
246 return false;
247 // A dummy query is made to check for availability, because libsecret doesn't
248 // have a dedicated availability function. For performance reasons, the query
249 // is meant to return an empty result.
250 LibsecretAttributesBuilder attrs;
251 attrs.Append("application", "chrome-string_to_get_empty_result");
253 GError* error = nullptr;
254 GList* found = secret_service_search_sync(nullptr, // default secret service
255 &kLibsecretSchema, attrs.Get(),
256 SECRET_SEARCH_ALL,
257 nullptr, // no cancellable ojbect
258 &error);
259 bool success = (error == nullptr);
260 if (error)
261 g_error_free(error);
262 if (found)
263 g_list_free(found);
265 return success;
268 NativeBackendLibsecret::NativeBackendLibsecret(LocalProfileId id)
269 : app_string_(GetProfileSpecificAppString(id)) {
272 NativeBackendLibsecret::~NativeBackendLibsecret() {
275 bool NativeBackendLibsecret::Init() {
276 return LoadLibsecret() && LibsecretIsAvailable();
279 password_manager::PasswordStoreChangeList NativeBackendLibsecret::AddLogin(
280 const PasswordForm& form) {
281 // Based on LoginDatabase::AddLogin(), we search for an existing match based
282 // on origin_url, username_element, username_value, password_element, submit
283 // element, and signon_realm first, remove that, and then add the new entry.
284 // We'd add the new one first, and then delete the original, but then the
285 // delete might actually delete the newly-added entry!
286 ScopedVector<autofill::PasswordForm> forms;
287 AddUpdateLoginSearch(form, SEARCH_USE_SUBMIT, &forms);
288 password_manager::PasswordStoreChangeList changes;
289 if (forms.size() > 0) {
290 if (forms.size() > 1) {
291 VLOG(1) << "Adding login when there are " << forms.size()
292 << " matching logins already! Will replace only the first.";
295 if (RemoveLogin(*forms[0])) {
296 changes.push_back(password_manager::PasswordStoreChange(
297 password_manager::PasswordStoreChange::REMOVE, *forms[0]));
300 if (RawAddLogin(form)) {
301 changes.push_back(password_manager::PasswordStoreChange(
302 password_manager::PasswordStoreChange::ADD, form));
304 return changes;
307 bool NativeBackendLibsecret::UpdateLogin(
308 const PasswordForm& form,
309 password_manager::PasswordStoreChangeList* changes) {
310 // Based on LoginDatabase::UpdateLogin(), we search for forms to update by
311 // origin_url, username_element, username_value, password_element, and
312 // signon_realm. We then compare the result to the updated form. If they
313 // differ in any of the mutable fields, then we remove the original, and
314 // then add the new entry. We'd add the new one first, and then delete the
315 // original, but then the delete might actually delete the newly-added entry!
316 DCHECK(changes);
317 changes->clear();
319 ScopedVector<autofill::PasswordForm> forms;
320 AddUpdateLoginSearch(form, SEARCH_IGNORE_SUBMIT, &forms);
322 bool removed = false;
323 for (size_t i = 0; i < forms.size(); ++i) {
324 if (*forms[i] != form) {
325 RemoveLogin(*forms[i]);
326 removed = true;
329 if (!removed)
330 return true;
332 if (RawAddLogin(form)) {
333 password_manager::PasswordStoreChange change(
334 password_manager::PasswordStoreChange::UPDATE, form);
335 changes->push_back(change);
336 return true;
338 return false;
341 bool NativeBackendLibsecret::RemoveLogin(const autofill::PasswordForm& form) {
342 GError* error = nullptr;
343 secret_password_clear_sync(
344 &kLibsecretSchema, nullptr, &error, "origin_url",
345 form.origin.spec().c_str(), "username_element",
346 UTF16ToUTF8(form.username_element).c_str(), "username_value",
347 UTF16ToUTF8(form.username_value).c_str(), "password_element",
348 UTF16ToUTF8(form.password_element).c_str(), "submit_element",
349 UTF16ToUTF8(form.submit_element).c_str(), "signon_realm",
350 form.signon_realm.c_str(), "application", app_string_.c_str(), nullptr);
352 if (error) {
353 VLOG(1) << "Libsecret delete failed: " << error->message;
354 g_error_free(error);
355 return false;
357 return true;
360 bool NativeBackendLibsecret::RemoveLoginsCreatedBetween(
361 base::Time delete_begin,
362 base::Time delete_end,
363 password_manager::PasswordStoreChangeList* changes) {
364 return RemoveLoginsBetween(delete_begin, delete_end, CREATION_TIMESTAMP,
365 changes);
368 bool NativeBackendLibsecret::RemoveLoginsSyncedBetween(
369 base::Time delete_begin,
370 base::Time delete_end,
371 password_manager::PasswordStoreChangeList* changes) {
372 return RemoveLoginsBetween(delete_begin, delete_end, SYNC_TIMESTAMP, changes);
375 bool NativeBackendLibsecret::GetLogins(
376 const PasswordForm& form,
377 ScopedVector<autofill::PasswordForm>* forms) {
378 return GetLoginsList(&form, ALL_LOGINS, forms);
381 void NativeBackendLibsecret::AddUpdateLoginSearch(
382 const autofill::PasswordForm& lookup_form,
383 AddUpdateLoginSearchOptions options,
384 ScopedVector<autofill::PasswordForm>* forms) {
385 LibsecretAttributesBuilder attrs;
386 attrs.Append("origin_url", lookup_form.origin.spec());
387 attrs.Append("username_element", UTF16ToUTF8(lookup_form.username_element));
388 attrs.Append("username_value", UTF16ToUTF8(lookup_form.username_value));
389 attrs.Append("password_element", UTF16ToUTF8(lookup_form.password_element));
390 if (options == SEARCH_USE_SUBMIT)
391 attrs.Append("submit_element", UTF16ToUTF8(lookup_form.submit_element));
392 attrs.Append("signon_realm", lookup_form.signon_realm);
393 attrs.Append("application", app_string_);
395 GError* error = nullptr;
396 GList* found = secret_service_search_sync(nullptr, // default secret service
397 &kLibsecretSchema, attrs.Get(),
398 SECRET_SEARCH_ALL,
399 nullptr, // no cancellable ojbect
400 &error);
401 if (error) {
402 VLOG(1) << "Unable to get logins " << error->message;
403 g_error_free(error);
404 if (found)
405 g_list_free(found);
406 return;
409 ConvertFormList(found, &lookup_form, forms);
412 bool NativeBackendLibsecret::RawAddLogin(const PasswordForm& form) {
413 int64 date_created = form.date_created.ToInternalValue();
414 // If we are asked to save a password with 0 date, use the current time.
415 // We don't want to actually save passwords as though on January 1, 1601.
416 if (!date_created)
417 date_created = base::Time::Now().ToInternalValue();
418 int64 date_synced = form.date_synced.ToInternalValue();
419 GError* error = nullptr;
420 secret_password_store_sync(
421 &kLibsecretSchema,
422 nullptr, // Default collection.
423 form.origin.spec().c_str(), // Display name.
424 UTF16ToUTF8(form.password_value).c_str(),
425 nullptr, // no cancellable ojbect
426 &error,
427 "origin_url", form.origin.spec().c_str(),
428 "action_url", form.action.spec().c_str(),
429 "username_element", UTF16ToUTF8(form.username_element).c_str(),
430 "username_value", UTF16ToUTF8(form.username_value).c_str(),
431 "password_element", UTF16ToUTF8(form.password_element).c_str(),
432 "submit_element", UTF16ToUTF8(form.submit_element).c_str(),
433 "signon_realm", form.signon_realm.c_str(),
434 "ssl_valid", form.ssl_valid,
435 "preferred", form.preferred,
436 "date_created", base::Int64ToString(date_created).c_str(),
437 "blacklisted_by_user", form.blacklisted_by_user,
438 "type", form.type,
439 "times_used", form.times_used,
440 "scheme", form.scheme,
441 "date_synced", base::Int64ToString(date_synced).c_str(),
442 "display_name", UTF16ToUTF8(form.display_name).c_str(),
443 "avatar_url", form.avatar_url.spec().c_str(),
444 "federation_url", form.federation_url.spec().c_str(),
445 "skip_zero_click", form.skip_zero_click,
446 "generation_upload_status", form.generation_upload_status,
447 "application", app_string_.c_str(), nullptr);
449 if (error) {
450 VLOG(1) << "Libsecret add raw login failed: " << error->message;
451 g_error_free(error);
452 return false;
454 return true;
457 bool NativeBackendLibsecret::GetAutofillableLogins(
458 ScopedVector<autofill::PasswordForm>* forms) {
459 return GetLoginsList(nullptr, AUTOFILLABLE_LOGINS, forms);
462 bool NativeBackendLibsecret::GetBlacklistLogins(
463 ScopedVector<autofill::PasswordForm>* forms) {
464 return GetLoginsList(nullptr, BLACKLISTED_LOGINS, forms);
467 bool NativeBackendLibsecret::GetLoginsList(
468 const PasswordForm* lookup_form,
469 GetLoginsListOptions options,
470 ScopedVector<autofill::PasswordForm>* forms) {
471 LibsecretAttributesBuilder attrs;
472 attrs.Append("application", app_string_);
473 if (options != ALL_LOGINS)
474 attrs.Append("blacklisted_by_user", options == BLACKLISTED_LOGINS);
475 if (lookup_form &&
476 !password_manager::ShouldPSLDomainMatchingApply(
477 password_manager::GetRegistryControlledDomain(
478 GURL(lookup_form->signon_realm))))
479 attrs.Append("signon_realm", lookup_form->signon_realm);
481 GError* error = nullptr;
482 GList* found = secret_service_search_sync(nullptr, // default secret service
483 &kLibsecretSchema, attrs.Get(),
484 SECRET_SEARCH_ALL,
485 nullptr, // no cancellable ojbect
486 &error);
487 if (error) {
488 VLOG(1) << "Unable to get logins " << error->message;
489 g_error_free(error);
490 if (found)
491 g_list_free(found);
492 return false;
495 return ConvertFormList(found, lookup_form, forms);
498 bool NativeBackendLibsecret::GetAllLogins(
499 ScopedVector<autofill::PasswordForm>* forms) {
500 return GetLoginsList(nullptr, ALL_LOGINS, forms);
503 bool NativeBackendLibsecret::GetLoginsBetween(
504 base::Time get_begin,
505 base::Time get_end,
506 TimestampToCompare date_to_compare,
507 ScopedVector<autofill::PasswordForm>* forms) {
508 ScopedVector<autofill::PasswordForm> all_forms;
509 if (!GetAllLogins(&all_forms))
510 return false;
512 base::Time autofill::PasswordForm::*date_member =
513 date_to_compare == CREATION_TIMESTAMP
514 ? &autofill::PasswordForm::date_created
515 : &autofill::PasswordForm::date_synced;
516 for (auto& saved_form : all_forms) {
517 if (get_begin <= saved_form->*date_member &&
518 (get_end.is_null() || saved_form->*date_member < get_end)) {
519 forms->push_back(saved_form);
520 saved_form = nullptr;
524 return true;
527 bool NativeBackendLibsecret::RemoveLoginsBetween(
528 base::Time get_begin,
529 base::Time get_end,
530 TimestampToCompare date_to_compare,
531 password_manager::PasswordStoreChangeList* changes) {
532 DCHECK(changes);
533 changes->clear();
534 ScopedVector<autofill::PasswordForm> forms;
535 if (!GetLoginsBetween(get_begin, get_end, date_to_compare, &forms))
536 return false;
538 bool ok = true;
539 for (size_t i = 0; i < forms.size(); ++i) {
540 if (RemoveLogin(*forms[i])) {
541 changes->push_back(password_manager::PasswordStoreChange(
542 password_manager::PasswordStoreChange::REMOVE, *forms[i]));
543 } else {
544 ok = false;
547 return ok;
550 bool NativeBackendLibsecret::ConvertFormList(
551 GList* found,
552 const PasswordForm* lookup_form,
553 ScopedVector<autofill::PasswordForm>* forms) {
554 password_manager::PSLDomainMatchMetric psl_domain_match_metric =
555 password_manager::PSL_DOMAIN_MATCH_NONE;
556 GError* error = nullptr;
557 for (GList* element = g_list_first(found); element != nullptr;
558 element = g_list_next(element)) {
559 SecretItem* secretItem = static_cast<SecretItem*>(element->data);
560 LibsecretLoader::secret_item_load_secret_sync(secretItem, nullptr, &error);
561 if (error) {
562 VLOG(1) << "Unable to load secret item" << error->message;
563 g_error_free(error);
564 error = nullptr;
565 continue;
567 GHashTable* attrs = secret_item_get_attributes(secretItem);
568 scoped_ptr<PasswordForm> form(FormOutOfAttributes(attrs));
569 g_hash_table_unref(attrs);
570 if (form) {
571 if (lookup_form && form->signon_realm != lookup_form->signon_realm) {
572 // This is not an exact match, we try PSL matching.
573 if (lookup_form->scheme != PasswordForm::SCHEME_HTML ||
574 form->scheme != PasswordForm::SCHEME_HTML ||
575 !(password_manager::IsPublicSuffixDomainMatch(
576 lookup_form->signon_realm, form->signon_realm))) {
577 continue;
579 psl_domain_match_metric = password_manager::PSL_DOMAIN_MATCH_FOUND;
580 form->original_signon_realm = form->signon_realm;
581 form->signon_realm = lookup_form->signon_realm;
582 form->origin = lookup_form->origin;
584 SecretValue* secretValue = secret_item_get_secret(secretItem);
585 if (secretValue) {
586 form->password_value = UTF8ToUTF16(secret_value_get_text(secretValue));
587 secret_value_unref(secretValue);
588 } else {
589 VLOG(1) << "Unable to access password from list element!";
591 forms->push_back(form.release());
592 } else {
593 VLOG(1) << "Could not initialize PasswordForm from attributes!";
597 if (lookup_form) {
598 const GURL signon_realm(lookup_form->signon_realm);
599 std::string registered_domain =
600 password_manager::GetRegistryControlledDomain(signon_realm);
601 UMA_HISTOGRAM_ENUMERATION(
602 "PasswordManager.PslDomainMatchTriggering",
603 password_manager::ShouldPSLDomainMatchingApply(registered_domain)
604 ? psl_domain_match_metric
605 : password_manager::PSL_DOMAIN_MATCH_NOT_USED,
606 password_manager::PSL_DOMAIN_MATCH_COUNT);
608 g_list_free(found);
609 return true;
612 std::string NativeBackendLibsecret::GetProfileSpecificAppString(
613 LocalProfileId id) {
614 // Originally, the application string was always just "chrome" and used only
615 // so that we had *something* to search for since GNOME Keyring won't search
616 // for nothing. Now we use it to distinguish passwords for different profiles.
617 return base::StringPrintf("%s-%d", kLibsecretAppString, id);