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"
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"
17 #include "components/password_manager/core/browser/password_manager_metrics_util.h"
18 #include "components/password_manager/core/browser/password_manager_util.h"
20 using autofill::PasswordForm
;
21 using base::UTF8ToUTF16
;
22 using base::UTF16ToUTF8
;
25 const char kEmptyString
[] = "";
26 const int kMaxPossibleTimeTValue
= std::numeric_limits
<int>::max();
29 typeof(&::secret_password_store_sync
)
30 LibsecretLoader::secret_password_store_sync
;
31 typeof(&::secret_service_search_sync
)
32 LibsecretLoader::secret_service_search_sync
;
33 typeof(&::secret_password_clear_sync
)
34 LibsecretLoader::secret_password_clear_sync
;
35 typeof(&::secret_item_get_secret
) LibsecretLoader::secret_item_get_secret
;
36 typeof(&::secret_value_get_text
) LibsecretLoader::secret_value_get_text
;
37 typeof(&::secret_item_get_attributes
)
38 LibsecretLoader::secret_item_get_attributes
;
39 typeof(&::secret_item_load_secret_sync
)
40 LibsecretLoader::secret_item_load_secret_sync
;
41 typeof(&::secret_value_unref
) LibsecretLoader::secret_value_unref
;
43 bool LibsecretLoader::libsecret_loaded
= false;
45 const LibsecretLoader::FunctionInfo
LibsecretLoader::functions
[] = {
46 {"secret_password_store_sync",
47 reinterpret_cast<void**>(&secret_password_store_sync
)},
48 {"secret_service_search_sync",
49 reinterpret_cast<void**>(&secret_service_search_sync
)},
50 {"secret_password_clear_sync",
51 reinterpret_cast<void**>(&secret_password_clear_sync
)},
52 {"secret_item_get_secret",
53 reinterpret_cast<void**>(&secret_item_get_secret
)},
54 {"secret_value_get_text", reinterpret_cast<void**>(&secret_value_get_text
)},
55 {"secret_item_get_attributes",
56 reinterpret_cast<void**>(&secret_item_get_attributes
)},
57 {"secret_item_load_secret_sync",
58 reinterpret_cast<void**>(&secret_item_load_secret_sync
)},
59 {"secret_value_unref", reinterpret_cast<void**>(&secret_value_unref
)},
62 bool LibsecretLoader::LoadLibsecret() {
66 void* handle
= dlopen("libsecret-1.so.0", RTLD_NOW
| RTLD_GLOBAL
);
68 // We wanted to use libsecret, but we couldn't load it. Warn, because
69 // either the user asked for this, or we autodetected it incorrectly. (Or
70 // the system has broken libraries, which is also good to warn about.)
71 LOG(WARNING
) << "Could not load libsecret-1.so.0: " << dlerror();
75 for (size_t i
= 0; functions
[i
].name
; ++i
) {
77 *functions
[i
].pointer
= dlsym(handle
, functions
[i
].name
);
78 const char* error
= dlerror();
80 VLOG(1) << "Unable to load symbol " << functions
[i
].name
<< ": " << error
;
86 libsecret_loaded
= true;
87 // We leak the library handle. That's OK: this function is called only once.
93 const char kLibsecretAppString
[] = "chrome";
95 // Schema is analagous to the fields in PasswordForm.
96 const SecretSchema kLibsecretSchema
= {
97 "chrome_libsecret_password_schema",
98 // We have to use SECRET_SCHEMA_DONT_MATCH_NAME in order to get old
99 // passwords stored with gnome_keyring.
100 SECRET_SCHEMA_DONT_MATCH_NAME
,
101 {{"origin_url", SECRET_SCHEMA_ATTRIBUTE_STRING
},
102 {"action_url", SECRET_SCHEMA_ATTRIBUTE_STRING
},
103 {"username_element", SECRET_SCHEMA_ATTRIBUTE_STRING
},
104 {"username_value", SECRET_SCHEMA_ATTRIBUTE_STRING
},
105 {"password_element", SECRET_SCHEMA_ATTRIBUTE_STRING
},
106 {"submit_element", SECRET_SCHEMA_ATTRIBUTE_STRING
},
107 {"signon_realm", SECRET_SCHEMA_ATTRIBUTE_STRING
},
108 {"ssl_valid", SECRET_SCHEMA_ATTRIBUTE_INTEGER
},
109 {"preferred", SECRET_SCHEMA_ATTRIBUTE_INTEGER
},
110 {"date_created", SECRET_SCHEMA_ATTRIBUTE_STRING
},
111 {"blacklisted_by_user", SECRET_SCHEMA_ATTRIBUTE_INTEGER
},
112 {"scheme", SECRET_SCHEMA_ATTRIBUTE_INTEGER
},
113 {"type", SECRET_SCHEMA_ATTRIBUTE_INTEGER
},
114 {"times_used", SECRET_SCHEMA_ATTRIBUTE_INTEGER
},
115 {"date_synced", SECRET_SCHEMA_ATTRIBUTE_STRING
},
116 {"display_name", SECRET_SCHEMA_ATTRIBUTE_STRING
},
117 {"avatar_url", SECRET_SCHEMA_ATTRIBUTE_STRING
},
118 {"federation_url", SECRET_SCHEMA_ATTRIBUTE_STRING
},
119 {"skip_zero_click", SECRET_SCHEMA_ATTRIBUTE_INTEGER
},
120 {"generation_upload_status", SECRET_SCHEMA_ATTRIBUTE_INTEGER
},
121 {"form_data", SECRET_SCHEMA_ATTRIBUTE_STRING
},
122 // This field is always "chrome-profile_id" so that we can search for it.
123 {"application", SECRET_SCHEMA_ATTRIBUTE_STRING
},
124 {nullptr, SECRET_SCHEMA_ATTRIBUTE_STRING
}}};
126 const char* GetStringFromAttributes(GHashTable
* attrs
, const char* keyname
) {
127 gpointer value
= g_hash_table_lookup(attrs
, keyname
);
128 return value
? static_cast<char*>(value
) : kEmptyString
;
131 uint32_t GetUintFromAttributes(GHashTable
* attrs
, const char* keyname
) {
132 gpointer value
= g_hash_table_lookup(attrs
, keyname
);
136 bool value_ok
= base::StringToUint(static_cast<char*>(value
), &result
);
141 // Convert the attributes into a new PasswordForm.
142 // Note: does *not* get the actual password, as that is not a key attribute!
143 // Returns nullptr if the attributes are for the wrong application.
144 scoped_ptr
<PasswordForm
> FormOutOfAttributes(GHashTable
* attrs
) {
145 base::StringPiece app_value
= GetStringFromAttributes(attrs
, "application");
146 if (!app_value
.starts_with(kLibsecretAppString
))
147 return scoped_ptr
<PasswordForm
>();
149 scoped_ptr
<PasswordForm
> form(new PasswordForm());
150 form
->origin
= GURL(GetStringFromAttributes(attrs
, "origin_url"));
151 form
->action
= GURL(GetStringFromAttributes(attrs
, "action_url"));
152 form
->username_element
=
153 UTF8ToUTF16(GetStringFromAttributes(attrs
, "username_element"));
154 form
->username_value
=
155 UTF8ToUTF16(GetStringFromAttributes(attrs
, "username_value"));
156 form
->password_element
=
157 UTF8ToUTF16(GetStringFromAttributes(attrs
, "password_element"));
158 form
->submit_element
=
159 UTF8ToUTF16(GetStringFromAttributes(attrs
, "submit_element"));
160 form
->signon_realm
= GetStringFromAttributes(attrs
, "signon_realm");
161 form
->ssl_valid
= GetUintFromAttributes(attrs
, "ssl_valid");
162 form
->preferred
= GetUintFromAttributes(attrs
, "preferred");
163 int64 date_created
= 0;
164 bool date_ok
= base::StringToInt64(
165 GetStringFromAttributes(attrs
, "date_created"), &date_created
);
167 // In the past |date_created| was stored as time_t. Currently is stored as
168 // base::Time's internal value. We need to distinguish, which format the
169 // number in |date_created| was stored in. We use the fact that
170 // kMaxPossibleTimeTValue interpreted as the internal value corresponds to an
171 // unlikely date back in 17th century, and anything above
172 // kMaxPossibleTimeTValue clearly must be in the internal value format.
173 form
->date_created
= date_created
< kMaxPossibleTimeTValue
174 ? base::Time::FromTimeT(date_created
)
175 : base::Time::FromInternalValue(date_created
);
176 form
->blacklisted_by_user
=
177 GetUintFromAttributes(attrs
, "blacklisted_by_user");
179 static_cast<PasswordForm::Type
>(GetUintFromAttributes(attrs
, "type"));
180 form
->times_used
= GetUintFromAttributes(attrs
, "times_used");
182 static_cast<PasswordForm::Scheme
>(GetUintFromAttributes(attrs
, "scheme"));
183 int64 date_synced
= 0;
184 base::StringToInt64(GetStringFromAttributes(attrs
, "date_synced"),
186 form
->date_synced
= base::Time::FromInternalValue(date_synced
);
188 UTF8ToUTF16(GetStringFromAttributes(attrs
, "display_name"));
189 form
->icon_url
= GURL(GetStringFromAttributes(attrs
, "avatar_url"));
190 form
->federation_url
= GURL(GetStringFromAttributes(attrs
, "federation_url"));
191 form
->skip_zero_click
= GetUintFromAttributes(attrs
, "skip_zero_click");
192 form
->generation_upload_status
=
193 static_cast<PasswordForm::GenerationUploadStatus
>(
194 GetUintFromAttributes(attrs
, "generation_upload_status"));
195 base::StringPiece encoded_form_data
=
196 GetStringFromAttributes(attrs
, "form_data");
197 if (!encoded_form_data
.empty()) {
198 bool success
= DeserializeFormDataFromBase64String(encoded_form_data
,
200 password_manager::metrics_util::FormDeserializationStatus status
=
201 success
? password_manager::metrics_util::GNOME_SUCCESS
202 : password_manager::metrics_util::GNOME_FAILURE
;
203 LogFormDataDeserializationStatus(status
);
208 class LibsecretAttributesBuilder
{
210 LibsecretAttributesBuilder();
211 ~LibsecretAttributesBuilder();
212 void Append(const std::string
& name
, const std::string
& value
);
213 void Append(const std::string
& name
, int64 value
);
214 // GHashTable, its keys and values returned from Get() are destroyed in
215 // |LibsecretAttributesBuilder| desctructor.
216 GHashTable
* Get() { return attrs_
; }
219 // |name_values_| is a storage for strings referenced in |attrs_|.
220 std::list
<std::string
> name_values_
;
224 LibsecretAttributesBuilder::LibsecretAttributesBuilder() {
225 attrs_
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
226 nullptr, // no deleter for keys
227 nullptr); // no deleter for values
230 LibsecretAttributesBuilder::~LibsecretAttributesBuilder() {
231 g_hash_table_destroy(attrs_
);
234 void LibsecretAttributesBuilder::Append(const std::string
& name
,
235 const std::string
& value
) {
236 name_values_
.push_back(name
);
238 static_cast<gpointer
>(const_cast<char*>(name_values_
.back().c_str()));
239 name_values_
.push_back(value
);
241 static_cast<gpointer
>(const_cast<char*>(name_values_
.back().c_str()));
242 g_hash_table_insert(attrs_
, name_str
, value_str
);
245 void LibsecretAttributesBuilder::Append(const std::string
& name
, int64 value
) {
246 Append(name
, base::Int64ToString(value
));
249 // Generates a profile-specific app string based on profile_id_.
250 std::string
GetProfileSpecificAppString(LocalProfileId id
) {
251 // Originally, the application string was always just "chrome" and used only
252 // so that we had *something* to search for since GNOME Keyring won't search
253 // for nothing. Now we use it to distinguish passwords for different profiles.
254 return base::StringPrintf("%s-%d", kLibsecretAppString
, id
);
259 bool LibsecretLoader::LibsecretIsAvailable() {
260 if (!libsecret_loaded
)
262 // A dummy query is made to check for availability, because libsecret doesn't
263 // have a dedicated availability function. For performance reasons, the query
264 // is meant to return an empty result.
265 LibsecretAttributesBuilder attrs
;
266 attrs
.Append("application", "chrome-string_to_get_empty_result");
268 GError
* error
= nullptr;
269 GList
* found
= secret_service_search_sync(nullptr, // default secret service
270 &kLibsecretSchema
, attrs
.Get(),
272 nullptr, // no cancellable ojbect
274 bool success
= (error
== nullptr);
283 NativeBackendLibsecret::NativeBackendLibsecret(LocalProfileId id
)
284 : app_string_(GetProfileSpecificAppString(id
)) {
287 NativeBackendLibsecret::~NativeBackendLibsecret() {
290 bool NativeBackendLibsecret::Init() {
291 return LoadLibsecret() && LibsecretIsAvailable();
294 password_manager::PasswordStoreChangeList
NativeBackendLibsecret::AddLogin(
295 const PasswordForm
& form
) {
296 // Based on LoginDatabase::AddLogin(), we search for an existing match based
297 // on origin_url, username_element, username_value, password_element and
298 // signon_realm first, remove that, and then add the new entry.
299 password_manager::PasswordStoreChangeList changes
;
300 ScopedVector
<autofill::PasswordForm
> forms
;
301 if (!AddUpdateLoginSearch(form
, &forms
))
304 if (forms
.size() > 0) {
305 password_manager::PasswordStoreChangeList temp_changes
;
306 if (forms
.size() > 1) {
307 LOG(WARNING
) << "Adding login when there are " << forms
.size()
308 << " matching logins already!";
310 for (const PasswordForm
* old_form
: forms
) {
311 if (!RemoveLogin(*old_form
, &temp_changes
))
314 changes
.push_back(password_manager::PasswordStoreChange(
315 password_manager::PasswordStoreChange::REMOVE
, *forms
[0]));
317 if (RawAddLogin(form
)) {
318 changes
.push_back(password_manager::PasswordStoreChange(
319 password_manager::PasswordStoreChange::ADD
, form
));
324 bool NativeBackendLibsecret::UpdateLogin(
325 const PasswordForm
& form
,
326 password_manager::PasswordStoreChangeList
* changes
) {
327 // Based on LoginDatabase::UpdateLogin(), we search for forms to update by
328 // origin_url, username_element, username_value, password_element, and
329 // signon_realm. We then compare the result to the updated form. If they
330 // differ in any of the mutable fields, then we remove the original, and
331 // then add the new entry. We'd add the new one first, and then delete the
332 // original, but then the delete might actually delete the newly-added entry!
334 ScopedVector
<autofill::PasswordForm
> forms
;
335 if (!AddUpdateLoginSearch(form
, &forms
))
339 if (forms
.size() == 1 && *forms
.front() == form
)
342 password_manager::PasswordStoreChangeList temp_changes
;
343 for (const PasswordForm
* keychain_form
: forms
) {
344 // Remove all the obsolete forms. Note that RemoveLogin can remove any form
345 // matching the unique key. Thus, it's important to call it the right number
347 if (!RemoveLogin(*keychain_form
, &temp_changes
))
351 if (RawAddLogin(form
)) {
352 password_manager::PasswordStoreChange
change(
353 password_manager::PasswordStoreChange::UPDATE
, form
);
354 changes
->push_back(change
);
360 bool NativeBackendLibsecret::RemoveLogin(
361 const autofill::PasswordForm
& form
,
362 password_manager::PasswordStoreChangeList
* changes
) {
364 GError
* error
= nullptr;
365 if (secret_password_clear_sync(
366 &kLibsecretSchema
, nullptr, &error
,
367 "origin_url", form
.origin
.spec().c_str(),
368 "username_element", UTF16ToUTF8(form
.username_element
).c_str(),
369 "username_value", UTF16ToUTF8(form
.username_value
).c_str(),
370 "password_element", UTF16ToUTF8(form
.password_element
).c_str(),
371 "signon_realm", form
.signon_realm
.c_str(),
372 "application", app_string_
.c_str(), nullptr)) {
373 changes
->push_back(password_manager::PasswordStoreChange(
374 password_manager::PasswordStoreChange::REMOVE
, form
));
378 LOG(ERROR
) << "Libsecret delete failed: " << error
->message
;
385 bool NativeBackendLibsecret::RemoveLoginsCreatedBetween(
386 base::Time delete_begin
,
387 base::Time delete_end
,
388 password_manager::PasswordStoreChangeList
* changes
) {
389 return RemoveLoginsBetween(delete_begin
, delete_end
, CREATION_TIMESTAMP
,
393 bool NativeBackendLibsecret::RemoveLoginsSyncedBetween(
394 base::Time delete_begin
,
395 base::Time delete_end
,
396 password_manager::PasswordStoreChangeList
* changes
) {
397 return RemoveLoginsBetween(delete_begin
, delete_end
, SYNC_TIMESTAMP
, changes
);
400 bool NativeBackendLibsecret::GetLogins(
401 const PasswordForm
& form
,
402 ScopedVector
<autofill::PasswordForm
>* forms
) {
403 return GetLoginsList(&form
, ALL_LOGINS
, forms
);
406 bool NativeBackendLibsecret::AddUpdateLoginSearch(
407 const autofill::PasswordForm
& lookup_form
,
408 ScopedVector
<autofill::PasswordForm
>* forms
) {
409 LibsecretAttributesBuilder attrs
;
410 attrs
.Append("origin_url", lookup_form
.origin
.spec());
411 attrs
.Append("username_element", UTF16ToUTF8(lookup_form
.username_element
));
412 attrs
.Append("username_value", UTF16ToUTF8(lookup_form
.username_value
));
413 attrs
.Append("password_element", UTF16ToUTF8(lookup_form
.password_element
));
414 attrs
.Append("signon_realm", lookup_form
.signon_realm
);
415 attrs
.Append("application", app_string_
);
417 GError
* error
= nullptr;
418 GList
* found
= secret_service_search_sync(nullptr, // default secret service
419 &kLibsecretSchema
, attrs
.Get(),
421 nullptr, // no cancellable ojbect
424 LOG(ERROR
) << "Unable to get logins " << error
->message
;
431 *forms
= ConvertFormList(found
, &lookup_form
);
435 bool NativeBackendLibsecret::RawAddLogin(const PasswordForm
& form
) {
436 int64 date_created
= form
.date_created
.ToInternalValue();
437 // If we are asked to save a password with 0 date, use the current time.
438 // We don't want to actually save passwords as though on January 1, 1601.
440 date_created
= base::Time::Now().ToInternalValue();
441 int64 date_synced
= form
.date_synced
.ToInternalValue();
442 std::string form_data
;
443 SerializeFormDataToBase64String(form
.form_data
, &form_data
);
444 GError
* error
= nullptr;
445 secret_password_store_sync(
447 nullptr, // Default collection.
448 form
.origin
.spec().c_str(), // Display name.
449 UTF16ToUTF8(form
.password_value
).c_str(),
450 nullptr, // no cancellable ojbect
452 "origin_url", form
.origin
.spec().c_str(),
453 "action_url", form
.action
.spec().c_str(),
454 "username_element", UTF16ToUTF8(form
.username_element
).c_str(),
455 "username_value", UTF16ToUTF8(form
.username_value
).c_str(),
456 "password_element", UTF16ToUTF8(form
.password_element
).c_str(),
457 "submit_element", UTF16ToUTF8(form
.submit_element
).c_str(),
458 "signon_realm", form
.signon_realm
.c_str(),
459 "ssl_valid", form
.ssl_valid
,
460 "preferred", form
.preferred
,
461 "date_created", base::Int64ToString(date_created
).c_str(),
462 "blacklisted_by_user", form
.blacklisted_by_user
,
464 "times_used", form
.times_used
,
465 "scheme", form
.scheme
,
466 "date_synced", base::Int64ToString(date_synced
).c_str(),
467 "display_name", UTF16ToUTF8(form
.display_name
).c_str(),
468 "avatar_url", form
.icon_url
.spec().c_str(),
469 "federation_url", form
.federation_url
.spec().c_str(),
470 "skip_zero_click", form
.skip_zero_click
,
471 "generation_upload_status", form
.generation_upload_status
,
472 "form_data", form_data
.c_str(),
473 "application", app_string_
.c_str(), nullptr);
476 LOG(ERROR
) << "Libsecret add raw login failed: " << error
->message
;
483 bool NativeBackendLibsecret::GetAutofillableLogins(
484 ScopedVector
<autofill::PasswordForm
>* forms
) {
485 return GetLoginsList(nullptr, AUTOFILLABLE_LOGINS
, forms
);
488 bool NativeBackendLibsecret::GetBlacklistLogins(
489 ScopedVector
<autofill::PasswordForm
>* forms
) {
490 return GetLoginsList(nullptr, BLACKLISTED_LOGINS
, forms
);
493 bool NativeBackendLibsecret::GetLoginsList(
494 const PasswordForm
* lookup_form
,
495 GetLoginsListOptions options
,
496 ScopedVector
<autofill::PasswordForm
>* forms
) {
497 LibsecretAttributesBuilder attrs
;
498 attrs
.Append("application", app_string_
);
499 if (options
!= ALL_LOGINS
)
500 attrs
.Append("blacklisted_by_user", options
== BLACKLISTED_LOGINS
);
502 !password_manager::ShouldPSLDomainMatchingApply(
503 password_manager::GetRegistryControlledDomain(
504 GURL(lookup_form
->signon_realm
))))
505 attrs
.Append("signon_realm", lookup_form
->signon_realm
);
507 GError
* error
= nullptr;
508 GList
* found
= secret_service_search_sync(nullptr, // default secret service
509 &kLibsecretSchema
, attrs
.Get(),
511 nullptr, // no cancellable ojbect
514 LOG(ERROR
) << "Unable to get logins " << error
->message
;
521 *forms
= ConvertFormList(found
, lookup_form
);
525 // Get rid of the forms with the same sync tags.
526 ScopedVector
<autofill::PasswordForm
> duplicates
;
527 std::vector
<std::vector
<autofill::PasswordForm
*>> tag_groups
;
528 password_manager_util::FindDuplicates(forms
, &duplicates
, &tag_groups
);
529 if (duplicates
.empty())
531 for (const auto& group
: tag_groups
) {
532 if (group
.size() > 1) {
533 // There are duplicates. Readd the first form. AddLogin() is smart enough
534 // to clean the previous ones.
535 password_manager::PasswordStoreChangeList changes
= AddLogin(*group
[0]);
536 if (changes
.empty() ||
537 changes
.back().type() != password_manager::PasswordStoreChange::ADD
)
544 bool NativeBackendLibsecret::GetLoginsBetween(
545 base::Time get_begin
,
547 TimestampToCompare date_to_compare
,
548 ScopedVector
<autofill::PasswordForm
>* forms
) {
550 ScopedVector
<autofill::PasswordForm
> all_forms
;
551 if (!GetLoginsList(nullptr, ALL_LOGINS
, &all_forms
))
554 base::Time
autofill::PasswordForm::*date_member
=
555 date_to_compare
== CREATION_TIMESTAMP
556 ? &autofill::PasswordForm::date_created
557 : &autofill::PasswordForm::date_synced
;
558 for (auto& saved_form
: all_forms
) {
559 if (get_begin
<= saved_form
->*date_member
&&
560 (get_end
.is_null() || saved_form
->*date_member
< get_end
)) {
561 forms
->push_back(saved_form
);
562 saved_form
= nullptr;
569 bool NativeBackendLibsecret::RemoveLoginsBetween(
570 base::Time get_begin
,
572 TimestampToCompare date_to_compare
,
573 password_manager::PasswordStoreChangeList
* changes
) {
576 ScopedVector
<autofill::PasswordForm
> forms
;
577 if (!GetLoginsBetween(get_begin
, get_end
, date_to_compare
, &forms
))
580 for (size_t i
= 0; i
< forms
.size(); ++i
) {
581 if (!RemoveLogin(*forms
[i
], changes
))
587 ScopedVector
<autofill::PasswordForm
> NativeBackendLibsecret::ConvertFormList(
589 const PasswordForm
* lookup_form
) {
590 ScopedVector
<autofill::PasswordForm
> forms
;
591 password_manager::PSLDomainMatchMetric psl_domain_match_metric
=
592 password_manager::PSL_DOMAIN_MATCH_NONE
;
593 GError
* error
= nullptr;
594 for (GList
* element
= g_list_first(found
); element
!= nullptr;
595 element
= g_list_next(element
)) {
596 SecretItem
* secretItem
= static_cast<SecretItem
*>(element
->data
);
597 LibsecretLoader::secret_item_load_secret_sync(secretItem
, nullptr, &error
);
599 LOG(ERROR
) << "Unable to load secret item" << error
->message
;
604 GHashTable
* attrs
= secret_item_get_attributes(secretItem
);
605 scoped_ptr
<PasswordForm
> form(FormOutOfAttributes(attrs
));
606 g_hash_table_unref(attrs
);
608 if (lookup_form
&& form
->signon_realm
!= lookup_form
->signon_realm
) {
609 // This is not an exact match, we try PSL matching.
610 if (lookup_form
->scheme
!= PasswordForm::SCHEME_HTML
||
611 form
->scheme
!= PasswordForm::SCHEME_HTML
||
612 !(password_manager::IsPublicSuffixDomainMatch(
613 lookup_form
->signon_realm
, form
->signon_realm
))) {
616 psl_domain_match_metric
= password_manager::PSL_DOMAIN_MATCH_FOUND
;
617 form
->original_signon_realm
= form
->signon_realm
;
618 form
->signon_realm
= lookup_form
->signon_realm
;
619 form
->origin
= lookup_form
->origin
;
621 SecretValue
* secretValue
= secret_item_get_secret(secretItem
);
623 form
->password_value
= UTF8ToUTF16(secret_value_get_text(secretValue
));
624 secret_value_unref(secretValue
);
626 LOG(WARNING
) << "Unable to access password from list element!";
628 forms
.push_back(form
.Pass());
630 VLOG(1) << "Could not initialize PasswordForm from attributes!";
635 const GURL
signon_realm(lookup_form
->signon_realm
);
636 std::string registered_domain
=
637 password_manager::GetRegistryControlledDomain(signon_realm
);
638 UMA_HISTOGRAM_ENUMERATION(
639 "PasswordManager.PslDomainMatchTriggering",
640 password_manager::ShouldPSLDomainMatchingApply(registered_domain
)
641 ? psl_domain_match_metric
642 : password_manager::PSL_DOMAIN_MATCH_NOT_USED
,
643 password_manager::PSL_DOMAIN_MATCH_COUNT
);