Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / password_manager / native_backend_gnome_x.cc
blobc974b0a569c5aa6e8315ed410bc982a74ca8b579
1 // Copyright (c) 2012 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_gnome_x.h"
7 #include <dlfcn.h>
8 #include <gnome-keyring.h>
10 #include <map>
11 #include <string>
12 #include <vector>
14 #include "base/basictypes.h"
15 #include "base/logging.h"
16 #include "base/memory/scoped_ptr.h"
17 #include "base/metrics/histogram.h"
18 #include "base/strings/string_number_conversions.h"
19 #include "base/strings/string_piece.h"
20 #include "base/strings/string_util.h"
21 #include "base/strings/stringprintf.h"
22 #include "base/strings/utf_string_conversions.h"
23 #include "base/synchronization/waitable_event.h"
24 #include "base/time/time.h"
25 #include "chrome/browser/password_manager/psl_matching_helper.h"
26 #include "components/autofill/core/common/password_form.h"
27 #include "content/public/browser/browser_thread.h"
29 using autofill::PasswordForm;
30 using base::UTF8ToUTF16;
31 using base::UTF16ToUTF8;
32 using content::BrowserThread;
34 #define GNOME_KEYRING_DEFINE_POINTER(name) \
35 typeof(&::gnome_keyring_##name) GnomeKeyringLoader::gnome_keyring_##name;
36 GNOME_KEYRING_FOR_EACH_FUNC(GNOME_KEYRING_DEFINE_POINTER)
37 #undef GNOME_KEYRING_DEFINE_POINTER
39 bool GnomeKeyringLoader::keyring_loaded = false;
41 #if defined(DLOPEN_GNOME_KEYRING)
43 #define GNOME_KEYRING_FUNCTION_INFO(name) \
44 {"gnome_keyring_"#name, reinterpret_cast<void**>(&gnome_keyring_##name)},
45 const GnomeKeyringLoader::FunctionInfo GnomeKeyringLoader::functions[] = {
46 GNOME_KEYRING_FOR_EACH_FUNC(GNOME_KEYRING_FUNCTION_INFO)
47 {NULL, NULL}
49 #undef GNOME_KEYRING_FUNCTION_INFO
51 /* Load the library and initialize the function pointers. */
52 bool GnomeKeyringLoader::LoadGnomeKeyring() {
53 if (keyring_loaded)
54 return true;
56 void* handle = dlopen("libgnome-keyring.so.0", RTLD_NOW | RTLD_GLOBAL);
57 if (!handle) {
58 // We wanted to use GNOME Keyring, but we couldn't load it. Warn, because
59 // either the user asked for this, or we autodetected it incorrectly. (Or
60 // the system has broken libraries, which is also good to warn about.)
61 LOG(WARNING) << "Could not load libgnome-keyring.so.0: " << dlerror();
62 return false;
65 for (size_t i = 0; functions[i].name; ++i) {
66 dlerror();
67 *functions[i].pointer = dlsym(handle, functions[i].name);
68 const char* error = dlerror();
69 if (error) {
70 LOG(ERROR) << "Unable to load symbol "
71 << functions[i].name << ": " << error;
72 dlclose(handle);
73 return false;
77 keyring_loaded = true;
78 // We leak the library handle. That's OK: this function is called only once.
79 return true;
82 #else // defined(DLOPEN_GNOME_KEYRING)
84 bool GnomeKeyringLoader::LoadGnomeKeyring() {
85 if (keyring_loaded)
86 return true;
87 #define GNOME_KEYRING_ASSIGN_POINTER(name) \
88 gnome_keyring_##name = &::gnome_keyring_##name;
89 GNOME_KEYRING_FOR_EACH_FUNC(GNOME_KEYRING_ASSIGN_POINTER)
90 #undef GNOME_KEYRING_ASSIGN_POINTER
91 keyring_loaded = true;
92 return true;
95 #endif // defined(DLOPEN_GNOME_KEYRING)
97 namespace {
99 const char kGnomeKeyringAppString[] = "chrome";
101 // Convert the attributes of a given keyring entry into a new PasswordForm.
102 // Note: does *not* get the actual password, as that is not a key attribute!
103 // Returns NULL if the attributes are for the wrong application.
104 scoped_ptr<PasswordForm> FormFromAttributes(GnomeKeyringAttributeList* attrs) {
105 // Read the string and int attributes into the appropriate map.
106 std::map<std::string, std::string> string_attr_map;
107 std::map<std::string, uint32_t> uint_attr_map;
108 for (guint i = 0; i < attrs->len; ++i) {
109 GnomeKeyringAttribute attr = gnome_keyring_attribute_list_index(attrs, i);
110 if (attr.type == GNOME_KEYRING_ATTRIBUTE_TYPE_STRING)
111 string_attr_map[attr.name] = attr.value.string;
112 else if (attr.type == GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32)
113 uint_attr_map[attr.name] = attr.value.integer;
115 // Check to make sure this is a password we care about.
116 const std::string& app_value = string_attr_map["application"];
117 if (!base::StringPiece(app_value).starts_with(kGnomeKeyringAppString))
118 return scoped_ptr<PasswordForm>();
120 scoped_ptr<PasswordForm> form(new PasswordForm());
121 form->origin = GURL(string_attr_map["origin_url"]);
122 form->action = GURL(string_attr_map["action_url"]);
123 form->username_element = UTF8ToUTF16(string_attr_map["username_element"]);
124 form->username_value = UTF8ToUTF16(string_attr_map["username_value"]);
125 form->password_element = UTF8ToUTF16(string_attr_map["password_element"]);
126 form->submit_element = UTF8ToUTF16(string_attr_map["submit_element"]);
127 form->signon_realm = string_attr_map["signon_realm"];
128 form->ssl_valid = uint_attr_map["ssl_valid"];
129 form->preferred = uint_attr_map["preferred"];
130 int64 date_created = 0;
131 bool date_ok = base::StringToInt64(string_attr_map["date_created"],
132 &date_created);
133 DCHECK(date_ok);
134 form->date_created = base::Time::FromTimeT(date_created);
135 form->blacklisted_by_user = uint_attr_map["blacklisted_by_user"];
136 form->scheme = static_cast<PasswordForm::Scheme>(uint_attr_map["scheme"]);
138 return form.Pass();
141 // Parse all the results from the given GList into a PasswordFormList, and free
142 // the GList. PasswordForms are allocated on the heap, and should be deleted by
143 // the consumer. If not empty, |filter_by_signon_realm| is used to filter out
144 // results -- only credentials with signon realms passing the PSL matching
145 // (done by |helper|) against |filter_by_signon_realm| will be kept.
146 void ConvertFormList(GList* found,
147 const std::string& filter_by_signon_realm,
148 const PSLMatchingHelper& helper,
149 NativeBackendGnome::PasswordFormList* forms) {
150 PSLMatchingHelper::PSLDomainMatchMetric psl_domain_match_metric =
151 PSLMatchingHelper::PSL_DOMAIN_MATCH_NONE;
152 for (GList* element = g_list_first(found); element != NULL;
153 element = g_list_next(element)) {
154 GnomeKeyringFound* data = static_cast<GnomeKeyringFound*>(element->data);
155 GnomeKeyringAttributeList* attrs = data->attributes;
157 scoped_ptr<PasswordForm> form(FormFromAttributes(attrs));
158 if (form) {
159 if (!filter_by_signon_realm.empty() &&
160 form->signon_realm != filter_by_signon_realm) {
161 // This is not an exact match, we try PSL matching.
162 if (!(PSLMatchingHelper::IsPublicSuffixDomainMatch(
163 filter_by_signon_realm, form->signon_realm))) {
164 continue;
166 psl_domain_match_metric = PSLMatchingHelper::PSL_DOMAIN_MATCH_FOUND;
167 form->original_signon_realm = form->signon_realm;
169 if (data->secret) {
170 form->password_value = UTF8ToUTF16(data->secret);
171 } else {
172 LOG(WARNING) << "Unable to access password from list element!";
174 forms->push_back(form.release());
175 } else {
176 LOG(WARNING) << "Could not initialize PasswordForm from attributes!";
179 if (!filter_by_signon_realm.empty()) {
180 UMA_HISTOGRAM_ENUMERATION(
181 "PasswordManager.PslDomainMatchTriggering",
182 helper.IsMatchingEnabled()
183 ? psl_domain_match_metric
184 : PSLMatchingHelper::PSL_DOMAIN_MATCH_DISABLED,
185 PSLMatchingHelper::PSL_DOMAIN_MATCH_COUNT);
189 // Schema is analagous to the fields in PasswordForm.
190 const GnomeKeyringPasswordSchema kGnomeSchema = {
191 GNOME_KEYRING_ITEM_GENERIC_SECRET, {
192 { "origin_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
193 { "action_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
194 { "username_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
195 { "username_value", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
196 { "password_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
197 { "submit_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
198 { "signon_realm", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
199 { "ssl_valid", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
200 { "preferred", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
201 { "date_created", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
202 { "blacklisted_by_user", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
203 { "scheme", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
204 // This field is always "chrome" so that we can search for it.
205 { "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
206 { NULL }
210 // Sadly, PasswordStore goes to great lengths to switch from the originally
211 // calling thread to the DB thread, and to provide an asynchronous API to
212 // callers while using a synchronous (virtual) API provided by subclasses like
213 // PasswordStoreX -- but GNOME Keyring really wants to be on the GLib main
214 // thread, which is the UI thread to us. So we end up having to switch threads
215 // again, possibly back to the very same thread (in case the UI thread is the
216 // caller, e.g. in the password management UI), and *block* the DB thread
217 // waiting for a response from the UI thread to provide the synchronous API
218 // PasswordStore expects of us. (It will then in turn switch back to the
219 // original caller to send the asynchronous reply to the original request.)
221 // This class represents a call to a GNOME Keyring method. A RunnableMethod
222 // should be posted to the UI thread to call one of its action methods, and then
223 // a WaitResult() method should be called to wait for the result. Each instance
224 // supports only one outstanding method at a time, though multiple instances may
225 // be used in parallel.
226 class GKRMethod : public GnomeKeyringLoader {
227 public:
228 typedef NativeBackendGnome::PasswordFormList PasswordFormList;
230 GKRMethod() : event_(false, false), result_(GNOME_KEYRING_RESULT_CANCELLED) {}
232 // Action methods. These call gnome_keyring_* functions. Call from UI thread.
233 // See GetProfileSpecificAppString() for more information on the app string.
234 void AddLogin(const PasswordForm& form, const char* app_string);
235 void AddLoginSearch(const PasswordForm& form, const char* app_string);
236 void UpdateLoginSearch(const PasswordForm& form, const char* app_string);
237 void RemoveLogin(const PasswordForm& form, const char* app_string);
238 void GetLogins(const PasswordForm& form, const char* app_string);
239 void GetLoginsList(uint32_t blacklisted_by_user, const char* app_string);
240 void GetAllLogins(const char* app_string);
242 // Use after AddLogin, RemoveLogin.
243 GnomeKeyringResult WaitResult();
245 // Use after AddLoginSearch, UpdateLoginSearch, GetLogins, GetLoginsList,
246 // GetAllLogins.
247 GnomeKeyringResult WaitResult(PasswordFormList* forms);
249 private:
250 struct GnomeKeyringAttributeListFreeDeleter {
251 inline void operator()(void* list) const {
252 gnome_keyring_attribute_list_free(
253 static_cast<GnomeKeyringAttributeList*>(list));
257 typedef scoped_ptr<GnomeKeyringAttributeList,
258 GnomeKeyringAttributeListFreeDeleter> ScopedAttributeList;
260 // Helper methods to abbreviate Gnome Keyring long API names.
261 static void AppendString(ScopedAttributeList* list,
262 const char* name,
263 const char* value);
264 static void AppendString(ScopedAttributeList* list,
265 const char* name,
266 const std::string& value);
267 static void AppendUint32(ScopedAttributeList* list,
268 const char* name,
269 guint32 value);
271 // All these callbacks are called on UI thread.
272 static void OnOperationDone(GnomeKeyringResult result, gpointer data);
274 static void OnOperationGetList(GnomeKeyringResult result, GList* list,
275 gpointer data);
277 base::WaitableEvent event_;
278 GnomeKeyringResult result_;
279 NativeBackendGnome::PasswordFormList forms_;
280 // Two additional arguments to OnOperationGetList:
281 // If the credential search is related to a particular form,
282 // |original_signon_realm_| contains the signon realm of that form. It is used
283 // to filter the relevant results out of all the found ones.
284 std::string original_signon_realm_;
285 const PSLMatchingHelper helper_;
288 void GKRMethod::AddLogin(const PasswordForm& form, const char* app_string) {
289 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
290 time_t date_created = form.date_created.ToTimeT();
291 // If we are asked to save a password with 0 date, use the current time.
292 // We don't want to actually save passwords as though on January 1, 1970.
293 if (!date_created)
294 date_created = time(NULL);
295 gnome_keyring_store_password(
296 &kGnomeSchema,
297 NULL, // Default keyring.
298 form.origin.spec().c_str(), // Display name.
299 UTF16ToUTF8(form.password_value).c_str(),
300 OnOperationDone,
301 this, // data
302 NULL, // destroy_data
303 "origin_url", form.origin.spec().c_str(),
304 "action_url", form.action.spec().c_str(),
305 "username_element", UTF16ToUTF8(form.username_element).c_str(),
306 "username_value", UTF16ToUTF8(form.username_value).c_str(),
307 "password_element", UTF16ToUTF8(form.password_element).c_str(),
308 "submit_element", UTF16ToUTF8(form.submit_element).c_str(),
309 "signon_realm", form.signon_realm.c_str(),
310 "ssl_valid", form.ssl_valid,
311 "preferred", form.preferred,
312 "date_created", base::Int64ToString(date_created).c_str(),
313 "blacklisted_by_user", form.blacklisted_by_user,
314 "scheme", form.scheme,
315 "application", app_string,
316 NULL);
319 void GKRMethod::AddLoginSearch(const PasswordForm& form,
320 const char* app_string) {
321 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
322 original_signon_realm_ = form.signon_realm;
323 // Search GNOME Keyring for matching passwords to update.
324 ScopedAttributeList attrs(gnome_keyring_attribute_list_new());
325 AppendString(&attrs, "origin_url", form.origin.spec());
326 AppendString(&attrs, "username_element", UTF16ToUTF8(form.username_element));
327 AppendString(&attrs, "username_value", UTF16ToUTF8(form.username_value));
328 AppendString(&attrs, "password_element", UTF16ToUTF8(form.password_element));
329 AppendString(&attrs, "submit_element", UTF16ToUTF8(form.submit_element));
330 AppendString(&attrs, "signon_realm", form.signon_realm);
331 AppendString(&attrs, "application", app_string);
332 gnome_keyring_find_items(GNOME_KEYRING_ITEM_GENERIC_SECRET,
333 attrs.get(),
334 OnOperationGetList,
335 /*data=*/this,
336 /*destroy_data=*/NULL);
339 void GKRMethod::UpdateLoginSearch(const PasswordForm& form,
340 const char* app_string) {
341 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
342 original_signon_realm_ = form.signon_realm;
343 // Search GNOME Keyring for matching passwords to update.
344 ScopedAttributeList attrs(gnome_keyring_attribute_list_new());
345 AppendString(&attrs, "origin_url", form.origin.spec());
346 AppendString(&attrs, "username_element", UTF16ToUTF8(form.username_element));
347 AppendString(&attrs, "username_value", UTF16ToUTF8(form.username_value));
348 AppendString(&attrs, "password_element", UTF16ToUTF8(form.password_element));
349 AppendString(&attrs, "signon_realm", form.signon_realm);
350 AppendString(&attrs, "application", app_string);
351 gnome_keyring_find_items(GNOME_KEYRING_ITEM_GENERIC_SECRET,
352 attrs.get(),
353 OnOperationGetList,
354 /*data=*/this,
355 /*destroy_data=*/NULL);
358 void GKRMethod::RemoveLogin(const PasswordForm& form, const char* app_string) {
359 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
360 // We find forms using the same fields as LoginDatabase::RemoveLogin().
361 gnome_keyring_delete_password(
362 &kGnomeSchema,
363 OnOperationDone,
364 this, // data
365 NULL, // destroy_data
366 "origin_url", form.origin.spec().c_str(),
367 "action_url", form.action.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 "submit_element", UTF16ToUTF8(form.submit_element).c_str(),
372 "signon_realm", form.signon_realm.c_str(),
373 "application", app_string,
374 NULL);
377 void GKRMethod::GetLogins(const PasswordForm& form, const char* app_string) {
378 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
379 original_signon_realm_ = form.signon_realm;
380 // Search GNOME Keyring for matching passwords.
381 ScopedAttributeList attrs(gnome_keyring_attribute_list_new());
382 if (!helper_.ShouldPSLDomainMatchingApply(
383 PSLMatchingHelper::GetRegistryControlledDomain(
384 GURL(form.signon_realm)))) {
385 AppendString(&attrs, "signon_realm", form.signon_realm);
387 AppendString(&attrs, "application", app_string);
388 gnome_keyring_find_items(GNOME_KEYRING_ITEM_GENERIC_SECRET,
389 attrs.get(),
390 OnOperationGetList,
391 /*data=*/this,
392 /*destroy_data=*/NULL);
395 void GKRMethod::GetLoginsList(uint32_t blacklisted_by_user,
396 const char* app_string) {
397 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
398 original_signon_realm_.clear();
399 // Search GNOME Keyring for matching passwords.
400 ScopedAttributeList attrs(gnome_keyring_attribute_list_new());
401 AppendUint32(&attrs, "blacklisted_by_user", blacklisted_by_user);
402 AppendString(&attrs, "application", app_string);
403 gnome_keyring_find_items(GNOME_KEYRING_ITEM_GENERIC_SECRET,
404 attrs.get(),
405 OnOperationGetList,
406 /*data=*/this,
407 /*destroy_data=*/NULL);
410 void GKRMethod::GetAllLogins(const char* app_string) {
411 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
412 original_signon_realm_.clear();
413 // We need to search for something, otherwise we get no results - so
414 // we search for the fixed application string.
415 ScopedAttributeList attrs(gnome_keyring_attribute_list_new());
416 AppendString(&attrs, "application", app_string);
417 gnome_keyring_find_items(GNOME_KEYRING_ITEM_GENERIC_SECRET,
418 attrs.get(),
419 OnOperationGetList,
420 /*data=*/this,
421 /*destroy_data=*/NULL);
424 GnomeKeyringResult GKRMethod::WaitResult() {
425 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
426 event_.Wait();
427 return result_;
430 GnomeKeyringResult GKRMethod::WaitResult(PasswordFormList* forms) {
431 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
432 event_.Wait();
433 if (forms->empty()) {
434 // Normal case. Avoid extra allocation by swapping.
435 forms->swap(forms_);
436 } else {
437 // Rare case. Append forms_ to *forms.
438 forms->insert(forms->end(), forms_.begin(), forms_.end());
439 forms_.clear();
441 return result_;
444 // static
445 void GKRMethod::AppendString(GKRMethod::ScopedAttributeList* list,
446 const char* name,
447 const char* value) {
448 gnome_keyring_attribute_list_append_string(list->get(), name, value);
451 // static
452 void GKRMethod::AppendString(GKRMethod::ScopedAttributeList* list,
453 const char* name,
454 const std::string& value) {
455 AppendString(list, name, value.c_str());
458 // static
459 void GKRMethod::AppendUint32(GKRMethod::ScopedAttributeList* list,
460 const char* name,
461 guint32 value) {
462 gnome_keyring_attribute_list_append_uint32(list->get(), name, value);
465 // static
466 void GKRMethod::OnOperationDone(GnomeKeyringResult result, gpointer data) {
467 GKRMethod* method = static_cast<GKRMethod*>(data);
468 method->result_ = result;
469 method->event_.Signal();
472 // static
473 void GKRMethod::OnOperationGetList(GnomeKeyringResult result, GList* list,
474 gpointer data) {
475 GKRMethod* method = static_cast<GKRMethod*>(data);
476 method->result_ = result;
477 method->forms_.clear();
478 // |list| will be freed after this callback returns, so convert it now.
479 ConvertFormList(
480 list, method->original_signon_realm_, method->helper_, &method->forms_);
481 method->original_signon_realm_.clear();
482 method->event_.Signal();
485 } // namespace
487 NativeBackendGnome::NativeBackendGnome(LocalProfileId id, PrefService* prefs)
488 : profile_id_(id), prefs_(prefs) {
489 // TODO(mdm): after a few more releases, remove the code which is now dead due
490 // to the true || here, and simplify this code. We don't do it yet to make it
491 // easier to revert if necessary.
492 if (true || PasswordStoreX::PasswordsUseLocalProfileId(prefs)) {
493 app_string_ = GetProfileSpecificAppString();
494 // We already did the migration previously. Don't try again.
495 migrate_tried_ = true;
496 } else {
497 app_string_ = kGnomeKeyringAppString;
498 migrate_tried_ = false;
502 NativeBackendGnome::~NativeBackendGnome() {
505 bool NativeBackendGnome::Init() {
506 return LoadGnomeKeyring() && gnome_keyring_is_available();
509 bool NativeBackendGnome::RawAddLogin(const PasswordForm& form) {
510 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
511 GKRMethod method;
512 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
513 base::Bind(&GKRMethod::AddLogin,
514 base::Unretained(&method),
515 form, app_string_.c_str()));
516 GnomeKeyringResult result = method.WaitResult();
517 if (result != GNOME_KEYRING_RESULT_OK) {
518 LOG(ERROR) << "Keyring save failed: "
519 << gnome_keyring_result_to_message(result);
520 return false;
522 // Successful write. Try migration if necessary.
523 if (!migrate_tried_)
524 MigrateToProfileSpecificLogins();
525 return true;
528 bool NativeBackendGnome::AddLogin(const PasswordForm& form) {
529 // Based on LoginDatabase::AddLogin(), we search for an existing match based
530 // on origin_url, username_element, username_value, password_element, submit
531 // element, and signon_realm first, remove that, and then add the new entry.
532 // We'd add the new one first, and then delete the original, but then the
533 // delete might actually delete the newly-added entry!
534 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
535 GKRMethod method;
536 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
537 base::Bind(&GKRMethod::AddLoginSearch,
538 base::Unretained(&method),
539 form, app_string_.c_str()));
540 PasswordFormList forms;
541 GnomeKeyringResult result = method.WaitResult(&forms);
542 if (result != GNOME_KEYRING_RESULT_OK &&
543 result != GNOME_KEYRING_RESULT_NO_MATCH) {
544 LOG(ERROR) << "Keyring find failed: "
545 << gnome_keyring_result_to_message(result);
546 return false;
548 if (forms.size() > 0) {
549 if (forms.size() > 1) {
550 LOG(WARNING) << "Adding login when there are " << forms.size()
551 << " matching logins already! Will replace only the first.";
554 // We try migration before updating the existing logins, since otherwise
555 // we'd do it after making some but not all of the changes below.
556 if (forms.size() > 0 && !migrate_tried_)
557 MigrateToProfileSpecificLogins();
559 RemoveLogin(*forms[0]);
560 for (size_t i = 0; i < forms.size(); ++i)
561 delete forms[i];
563 return RawAddLogin(form);
566 bool NativeBackendGnome::UpdateLogin(const PasswordForm& form) {
567 // Based on LoginDatabase::UpdateLogin(), we search for forms to update by
568 // origin_url, username_element, username_value, password_element, and
569 // signon_realm. We then compare the result to the updated form. If they
570 // differ in any of the action, password_value, ssl_valid, or preferred
571 // fields, then we remove the original, and then add the new entry. We'd add
572 // the new one first, and then delete the original, but then the delete might
573 // actually delete the newly-added entry!
574 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
575 GKRMethod method;
576 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
577 base::Bind(&GKRMethod::UpdateLoginSearch,
578 base::Unretained(&method),
579 form, app_string_.c_str()));
580 PasswordFormList forms;
581 GnomeKeyringResult result = method.WaitResult(&forms);
582 if (result != GNOME_KEYRING_RESULT_OK) {
583 LOG(ERROR) << "Keyring find failed: "
584 << gnome_keyring_result_to_message(result);
585 return false;
588 // We try migration before updating the existing logins, since otherwise
589 // we'd do it after making some but not all of the changes below.
590 if (forms.size() > 0 && !migrate_tried_)
591 MigrateToProfileSpecificLogins();
593 bool ok = true;
594 for (size_t i = 0; i < forms.size(); ++i) {
595 if (forms[i]->action != form.action ||
596 forms[i]->password_value != form.password_value ||
597 forms[i]->ssl_valid != form.ssl_valid ||
598 forms[i]->preferred != form.preferred) {
599 RemoveLogin(*forms[i]);
602 for (size_t i = 0; i < forms.size(); ++i) {
603 if (forms[i]->action != form.action ||
604 forms[i]->password_value != form.password_value ||
605 forms[i]->ssl_valid != form.ssl_valid ||
606 forms[i]->preferred != form.preferred) {
607 forms[i]->action = form.action;
608 forms[i]->password_value = form.password_value;
609 forms[i]->ssl_valid = form.ssl_valid;
610 forms[i]->preferred = form.preferred;
611 if (!RawAddLogin(*forms[i]))
612 ok = false;
614 delete forms[i];
616 return ok;
619 bool NativeBackendGnome::RemoveLogin(const PasswordForm& form) {
620 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
621 GKRMethod method;
622 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
623 base::Bind(&GKRMethod::RemoveLogin,
624 base::Unretained(&method),
625 form, app_string_.c_str()));
626 GnomeKeyringResult result = method.WaitResult();
627 if (result != GNOME_KEYRING_RESULT_OK) {
628 // Warning, not error, because this can sometimes happen due to the user
629 // racing with the daemon to delete the password a second time.
630 LOG(WARNING) << "Keyring delete failed: "
631 << gnome_keyring_result_to_message(result);
632 return false;
634 // Successful write. Try migration if necessary. Note that presumably if we've
635 // been asked to delete a login, it's because we returned it previously; thus,
636 // this will probably never happen since we'd have already tried migration.
637 if (!migrate_tried_)
638 MigrateToProfileSpecificLogins();
639 return true;
642 bool NativeBackendGnome::RemoveLoginsCreatedBetween(
643 const base::Time& delete_begin,
644 const base::Time& delete_end) {
645 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
646 bool ok = true;
647 // We could walk the list and delete items as we find them, but it is much
648 // easier to build the list and use RemoveLogin() to delete them.
649 PasswordFormList forms;
650 if (!GetAllLogins(&forms))
651 return false;
652 // No need to try migration here: GetAllLogins() does it.
654 for (size_t i = 0; i < forms.size(); ++i) {
655 if (delete_begin <= forms[i]->date_created &&
656 (delete_end.is_null() || forms[i]->date_created < delete_end)) {
657 if (!RemoveLogin(*forms[i]))
658 ok = false;
660 delete forms[i];
662 return ok;
665 bool NativeBackendGnome::GetLogins(const PasswordForm& form,
666 PasswordFormList* forms) {
667 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
668 GKRMethod method;
669 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
670 base::Bind(&GKRMethod::GetLogins,
671 base::Unretained(&method),
672 form, app_string_.c_str()));
673 GnomeKeyringResult result = method.WaitResult(forms);
674 if (result == GNOME_KEYRING_RESULT_NO_MATCH)
675 return true;
676 if (result != GNOME_KEYRING_RESULT_OK) {
677 LOG(ERROR) << "Keyring find failed: "
678 << gnome_keyring_result_to_message(result);
679 return false;
681 // Successful read of actual data. Try migration if necessary.
682 if (!migrate_tried_)
683 MigrateToProfileSpecificLogins();
684 return true;
687 bool NativeBackendGnome::GetLoginsCreatedBetween(const base::Time& get_begin,
688 const base::Time& get_end,
689 PasswordFormList* forms) {
690 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
691 // We could walk the list and add items as we find them, but it is much
692 // easier to build the list and then filter the results.
693 PasswordFormList all_forms;
694 if (!GetAllLogins(&all_forms))
695 return false;
696 // No need to try migration here: GetAllLogins() does it.
698 forms->reserve(forms->size() + all_forms.size());
699 for (size_t i = 0; i < all_forms.size(); ++i) {
700 if (get_begin <= all_forms[i]->date_created &&
701 (get_end.is_null() || all_forms[i]->date_created < get_end)) {
702 forms->push_back(all_forms[i]);
703 } else {
704 delete all_forms[i];
708 return true;
711 bool NativeBackendGnome::GetAutofillableLogins(PasswordFormList* forms) {
712 return GetLoginsList(forms, true);
715 bool NativeBackendGnome::GetBlacklistLogins(PasswordFormList* forms) {
716 return GetLoginsList(forms, false);
719 bool NativeBackendGnome::GetLoginsList(PasswordFormList* forms,
720 bool autofillable) {
721 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
723 uint32_t blacklisted_by_user = !autofillable;
725 GKRMethod method;
726 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
727 base::Bind(&GKRMethod::GetLoginsList,
728 base::Unretained(&method),
729 blacklisted_by_user, app_string_.c_str()));
730 GnomeKeyringResult result = method.WaitResult(forms);
731 if (result == GNOME_KEYRING_RESULT_NO_MATCH)
732 return true;
733 if (result != GNOME_KEYRING_RESULT_OK) {
734 LOG(ERROR) << "Keyring find failed: "
735 << gnome_keyring_result_to_message(result);
736 return false;
738 // Successful read of actual data. Try migration if necessary.
739 if (!migrate_tried_)
740 MigrateToProfileSpecificLogins();
741 return true;
744 bool NativeBackendGnome::GetAllLogins(PasswordFormList* forms) {
745 GKRMethod method;
746 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
747 base::Bind(&GKRMethod::GetAllLogins,
748 base::Unretained(&method),
749 app_string_.c_str()));
750 GnomeKeyringResult result = method.WaitResult(forms);
751 if (result == GNOME_KEYRING_RESULT_NO_MATCH)
752 return true;
753 if (result != GNOME_KEYRING_RESULT_OK) {
754 LOG(ERROR) << "Keyring find failed: "
755 << gnome_keyring_result_to_message(result);
756 return false;
758 // Successful read of actual data. Try migration if necessary.
759 if (!migrate_tried_)
760 MigrateToProfileSpecificLogins();
761 return true;
764 std::string NativeBackendGnome::GetProfileSpecificAppString() const {
765 // Originally, the application string was always just "chrome" and used only
766 // so that we had *something* to search for since GNOME Keyring won't search
767 // for nothing. Now we use it to distinguish passwords for different profiles.
768 return base::StringPrintf("%s-%d", kGnomeKeyringAppString, profile_id_);
771 void NativeBackendGnome::MigrateToProfileSpecificLogins() {
772 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
774 DCHECK(!migrate_tried_);
775 DCHECK_EQ(app_string_, kGnomeKeyringAppString);
777 // Record the fact that we've attempted migration already right away, so that
778 // we don't get recursive calls back to MigrateToProfileSpecificLogins().
779 migrate_tried_ = true;
781 // First get all the logins, using the old app string.
782 PasswordFormList forms;
783 if (!GetAllLogins(&forms))
784 return;
786 // Now switch to a profile-specific app string.
787 app_string_ = GetProfileSpecificAppString();
789 // Try to add all the logins with the new app string.
790 bool ok = true;
791 for (size_t i = 0; i < forms.size(); ++i) {
792 if (!RawAddLogin(*forms[i]))
793 ok = false;
794 delete forms[i];
797 if (ok) {
798 // All good! Keep the new app string and set a persistent pref.
799 // NOTE: We explicitly don't delete the old passwords yet. They are
800 // potentially shared with other profiles and other user data dirs!
801 // Each other profile must be able to migrate the shared data as well,
802 // so we must leave it alone. After a few releases, we'll add code to
803 // delete them, and eventually remove this migration code.
804 // TODO(mdm): follow through with the plan above.
805 PasswordStoreX::SetPasswordsUseLocalProfileId(prefs_);
806 } else {
807 // We failed to migrate for some reason. Use the old app string.
808 app_string_ = kGnomeKeyringAppString;