Only grant permissions to new extensions from sync if they have the expected version
[chromium-blink-merge.git] / chrome / browser / password_manager / native_backend_libsecret.cc
blobfdd95a3ad4be3a8a5ebdb117df11628e8b83bce8
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"
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;
24 namespace {
25 const char kEmptyString[] = "";
26 const int kMaxPossibleTimeTValue = std::numeric_limits<int>::max();
27 } // namespace
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)},
60 {nullptr, nullptr}};
62 bool LibsecretLoader::LoadLibsecret() {
63 if (libsecret_loaded)
64 return true;
66 void* handle = dlopen("libsecret-1.so.0", RTLD_NOW | RTLD_GLOBAL);
67 if (!handle) {
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();
72 return false;
75 for (size_t i = 0; functions[i].name; ++i) {
76 dlerror();
77 *functions[i].pointer = dlsym(handle, functions[i].name);
78 const char* error = dlerror();
79 if (error) {
80 VLOG(1) << "Unable to load symbol " << functions[i].name << ": " << error;
81 dlclose(handle);
82 return false;
86 libsecret_loaded = true;
87 // We leak the library handle. That's OK: this function is called only once.
88 return true;
91 namespace {
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);
133 if (!value)
134 return uint32_t();
135 uint32_t result;
136 bool value_ok = base::StringToUint(static_cast<char*>(value), &result);
137 DCHECK(value_ok);
138 return 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);
166 DCHECK(date_ok);
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");
178 form->type =
179 static_cast<PasswordForm::Type>(GetUintFromAttributes(attrs, "type"));
180 form->times_used = GetUintFromAttributes(attrs, "times_used");
181 form->scheme =
182 static_cast<PasswordForm::Scheme>(GetUintFromAttributes(attrs, "scheme"));
183 int64 date_synced = 0;
184 base::StringToInt64(GetStringFromAttributes(attrs, "date_synced"),
185 &date_synced);
186 form->date_synced = base::Time::FromInternalValue(date_synced);
187 form->display_name =
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,
199 &form->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);
205 return form.Pass();
208 class LibsecretAttributesBuilder {
209 public:
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_; }
218 private:
219 // |name_values_| is a storage for strings referenced in |attrs_|.
220 std::list<std::string> name_values_;
221 GHashTable* attrs_;
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);
237 gpointer name_str =
238 static_cast<gpointer>(const_cast<char*>(name_values_.back().c_str()));
239 name_values_.push_back(value);
240 gpointer value_str =
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);
257 } // namespace
259 bool LibsecretLoader::LibsecretIsAvailable() {
260 if (!libsecret_loaded)
261 return false;
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(),
271 SECRET_SEARCH_ALL,
272 nullptr, // no cancellable ojbect
273 &error);
274 bool success = (error == nullptr);
275 if (error)
276 g_error_free(error);
277 if (found)
278 g_list_free(found);
280 return success;
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))
302 return changes;
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))
312 return 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));
321 return changes;
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!
333 DCHECK(changes);
334 ScopedVector<autofill::PasswordForm> forms;
335 if (!AddUpdateLoginSearch(form, &forms))
336 return false;
337 if (forms.empty())
338 return true;
339 if (forms.size() == 1 && *forms.front() == form)
340 return true;
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
346 // of times.
347 if (!RemoveLogin(*keychain_form, &temp_changes))
348 return false;
351 if (RawAddLogin(form)) {
352 password_manager::PasswordStoreChange change(
353 password_manager::PasswordStoreChange::UPDATE, form);
354 changes->push_back(change);
355 return true;
357 return false;
360 bool NativeBackendLibsecret::RemoveLogin(
361 const autofill::PasswordForm& form,
362 password_manager::PasswordStoreChangeList* changes) {
363 DCHECK(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));
377 if (error) {
378 LOG(ERROR) << "Libsecret delete failed: " << error->message;
379 g_error_free(error);
380 return false;
382 return true;
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,
390 changes);
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(),
420 SECRET_SEARCH_ALL,
421 nullptr, // no cancellable ojbect
422 &error);
423 if (error) {
424 LOG(ERROR) << "Unable to get logins " << error->message;
425 g_error_free(error);
426 if (found)
427 g_list_free(found);
428 return false;
431 *forms = ConvertFormList(found, &lookup_form);
432 return true;
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.
439 if (!date_created)
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(
446 &kLibsecretSchema,
447 nullptr, // Default collection.
448 form.origin.spec().c_str(), // Display name.
449 UTF16ToUTF8(form.password_value).c_str(),
450 nullptr, // no cancellable ojbect
451 &error,
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,
463 "type", form.type,
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);
475 if (error) {
476 LOG(ERROR) << "Libsecret add raw login failed: " << error->message;
477 g_error_free(error);
478 return false;
480 return true;
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);
501 if (lookup_form &&
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(),
510 SECRET_SEARCH_ALL,
511 nullptr, // no cancellable ojbect
512 &error);
513 if (error) {
514 LOG(ERROR) << "Unable to get logins " << error->message;
515 g_error_free(error);
516 if (found)
517 g_list_free(found);
518 return false;
521 *forms = ConvertFormList(found, lookup_form);
522 if (lookup_form)
523 return true;
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())
530 return true;
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)
538 return false;
541 return true;
544 bool NativeBackendLibsecret::GetLoginsBetween(
545 base::Time get_begin,
546 base::Time get_end,
547 TimestampToCompare date_to_compare,
548 ScopedVector<autofill::PasswordForm>* forms) {
549 forms->clear();
550 ScopedVector<autofill::PasswordForm> all_forms;
551 if (!GetLoginsList(nullptr, ALL_LOGINS, &all_forms))
552 return false;
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;
566 return true;
569 bool NativeBackendLibsecret::RemoveLoginsBetween(
570 base::Time get_begin,
571 base::Time get_end,
572 TimestampToCompare date_to_compare,
573 password_manager::PasswordStoreChangeList* changes) {
574 DCHECK(changes);
575 changes->clear();
576 ScopedVector<autofill::PasswordForm> forms;
577 if (!GetLoginsBetween(get_begin, get_end, date_to_compare, &forms))
578 return false;
580 for (size_t i = 0; i < forms.size(); ++i) {
581 if (!RemoveLogin(*forms[i], changes))
582 return false;
584 return true;
587 ScopedVector<autofill::PasswordForm> NativeBackendLibsecret::ConvertFormList(
588 GList* found,
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);
598 if (error) {
599 LOG(ERROR) << "Unable to load secret item" << error->message;
600 g_error_free(error);
601 error = nullptr;
602 continue;
604 GHashTable* attrs = secret_item_get_attributes(secretItem);
605 scoped_ptr<PasswordForm> form(FormOutOfAttributes(attrs));
606 g_hash_table_unref(attrs);
607 if (form) {
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))) {
614 continue;
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);
622 if (secretValue) {
623 form->password_value = UTF8ToUTF16(secret_value_get_text(secretValue));
624 secret_value_unref(secretValue);
625 } else {
626 LOG(WARNING) << "Unable to access password from list element!";
628 forms.push_back(form.Pass());
629 } else {
630 VLOG(1) << "Could not initialize PasswordForm from attributes!";
634 if (lookup_form) {
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);
645 g_list_free(found);
646 return forms.Pass();