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.
4
5#include "chrome/browser/password_manager/native_backend_gnome_x.h"
6
7#include <dlfcn.h>
8#include <gnome-keyring.h>
9
10#include <map>
11#include <string>
12#include <vector>
13
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 "components/autofill/core/common/password_form.h"
26#include "components/password_manager/core/browser/psl_matching_helper.h"
27#include "content/public/browser/browser_thread.h"
28
29using autofill::PasswordForm;
30using base::UTF8ToUTF16;
31using base::UTF16ToUTF8;
32using content::BrowserThread;
33
34#define GNOME_KEYRING_DEFINE_POINTER(name) \
35  typeof(&::gnome_keyring_##name) GnomeKeyringLoader::gnome_keyring_##name;
36GNOME_KEYRING_FOR_EACH_FUNC(GNOME_KEYRING_DEFINE_POINTER)
37#undef GNOME_KEYRING_DEFINE_POINTER
38
39bool GnomeKeyringLoader::keyring_loaded = false;
40
41#if defined(DLOPEN_GNOME_KEYRING)
42
43#define GNOME_KEYRING_FUNCTION_INFO(name) \
44  {"gnome_keyring_"#name, reinterpret_cast<void**>(&gnome_keyring_##name)},
45const GnomeKeyringLoader::FunctionInfo GnomeKeyringLoader::functions[] = {
46  GNOME_KEYRING_FOR_EACH_FUNC(GNOME_KEYRING_FUNCTION_INFO)
47  {NULL, NULL}
48};
49#undef GNOME_KEYRING_FUNCTION_INFO
50
51/* Load the library and initialize the function pointers. */
52bool GnomeKeyringLoader::LoadGnomeKeyring() {
53  if (keyring_loaded)
54    return true;
55
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;
63  }
64
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;
74    }
75  }
76
77  keyring_loaded = true;
78  // We leak the library handle. That's OK: this function is called only once.
79  return true;
80}
81
82#else  // defined(DLOPEN_GNOME_KEYRING)
83
84bool 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;
93}
94
95#endif  // defined(DLOPEN_GNOME_KEYRING)
96
97namespace {
98
99const char kGnomeKeyringAppString[] = "chrome";
100
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.
104scoped_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;
114  }
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>();
119
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->type = static_cast<PasswordForm::Type>(uint_attr_map["type"]);
137  form->times_used = uint_attr_map["times_used"];
138  form->scheme = static_cast<PasswordForm::Scheme>(uint_attr_map["scheme"]);
139  int64 date_synced = 0;
140  base::StringToInt64(string_attr_map["date_synced"], &date_synced);
141  form->date_synced = base::Time::FromInternalValue(date_synced);
142  form->display_name = UTF8ToUTF16(string_attr_map["display_name"]);
143  form->avatar_url = GURL(string_attr_map["avatar_url"]);
144  form->federation_url = GURL(string_attr_map["federation_url"]);
145  form->is_zero_click = uint_attr_map["is_zero_click"];
146
147  return form.Pass();
148}
149
150// Parse all the results from the given GList into a PasswordFormList, and free
151// the GList. PasswordForms are allocated on the heap, and should be deleted by
152// the consumer. If not NULL, |lookup_form| is used to filter out results --
153// only credentials with signon realms passing the PSL matching against
154// |lookup_form->signon_realm| will be kept. PSL matched results get their
155// signon_realm, origin, and action rewritten to those of |lookup_form_|, with
156// the original signon_realm saved into the result's original_signon_realm data
157// member.
158void ConvertFormList(GList* found,
159                     const PasswordForm* lookup_form,
160                     NativeBackendGnome::PasswordFormList* forms) {
161  password_manager::PSLDomainMatchMetric psl_domain_match_metric =
162      password_manager::PSL_DOMAIN_MATCH_NONE;
163  for (GList* element = g_list_first(found); element != NULL;
164       element = g_list_next(element)) {
165    GnomeKeyringFound* data = static_cast<GnomeKeyringFound*>(element->data);
166    GnomeKeyringAttributeList* attrs = data->attributes;
167
168    scoped_ptr<PasswordForm> form(FormFromAttributes(attrs));
169    if (form) {
170      if (lookup_form && form->signon_realm != lookup_form->signon_realm) {
171        // This is not an exact match, we try PSL matching.
172        if (lookup_form->scheme != PasswordForm::SCHEME_HTML ||
173            form->scheme != PasswordForm::SCHEME_HTML ||
174            !(password_manager::IsPublicSuffixDomainMatch(
175                lookup_form->signon_realm, form->signon_realm))) {
176          continue;
177        }
178        psl_domain_match_metric = password_manager::PSL_DOMAIN_MATCH_FOUND;
179        form->original_signon_realm = form->signon_realm;
180        form->signon_realm = lookup_form->signon_realm;
181        form->origin = lookup_form->origin;
182        form->action = lookup_form->action;
183      }
184      if (data->secret) {
185        form->password_value = UTF8ToUTF16(data->secret);
186      } else {
187        LOG(WARNING) << "Unable to access password from list element!";
188      }
189      forms->push_back(form.release());
190    } else {
191      LOG(WARNING) << "Could not initialize PasswordForm from attributes!";
192    }
193  }
194  if (lookup_form) {
195    const GURL signon_realm(lookup_form->signon_realm);
196    std::string registered_domain =
197        password_manager::GetRegistryControlledDomain(signon_realm);
198    UMA_HISTOGRAM_ENUMERATION(
199        "PasswordManager.PslDomainMatchTriggering",
200        password_manager::ShouldPSLDomainMatchingApply(registered_domain)
201            ? psl_domain_match_metric
202            : password_manager::PSL_DOMAIN_MATCH_NOT_USED,
203        password_manager::PSL_DOMAIN_MATCH_COUNT);
204  }
205}
206
207// Schema is analagous to the fields in PasswordForm.
208// TODO(gcasto): Adding 'form_data' would be nice, but we would need to
209// serialize in a way that is guaranteed to not have any embedded NULLs. Pickle
210// doesn't make this guarantee, so we just don't serialize this field. Since
211// it's only used to crowd source data collection it doesn't matter that much
212// if it's not available on this platform.
213const GnomeKeyringPasswordSchema kGnomeSchema = {
214  GNOME_KEYRING_ITEM_GENERIC_SECRET, {
215    { "origin_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
216    { "action_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
217    { "username_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
218    { "username_value", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
219    { "password_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
220    { "submit_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
221    { "signon_realm", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
222    { "ssl_valid", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
223    { "preferred", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
224    { "date_created", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
225    { "blacklisted_by_user", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
226    { "scheme", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
227    { "type", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
228    { "times_used", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
229    { "date_synced", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
230    { "display_name", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
231    { "avatar_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
232    { "federation_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
233    { "is_zero_click", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
234    // This field is always "chrome" so that we can search for it.
235    { "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
236    { NULL }
237  }
238};
239
240// Sadly, PasswordStore goes to great lengths to switch from the originally
241// calling thread to the DB thread, and to provide an asynchronous API to
242// callers while using a synchronous (virtual) API provided by subclasses like
243// PasswordStoreX -- but GNOME Keyring really wants to be on the GLib main
244// thread, which is the UI thread to us. So we end up having to switch threads
245// again, possibly back to the very same thread (in case the UI thread is the
246// caller, e.g. in the password management UI), and *block* the DB thread
247// waiting for a response from the UI thread to provide the synchronous API
248// PasswordStore expects of us. (It will then in turn switch back to the
249// original caller to send the asynchronous reply to the original request.)
250
251// This class represents a call to a GNOME Keyring method. A RunnableMethod
252// should be posted to the UI thread to call one of its action methods, and then
253// a WaitResult() method should be called to wait for the result. Each instance
254// supports only one outstanding method at a time, though multiple instances may
255// be used in parallel.
256class GKRMethod : public GnomeKeyringLoader {
257 public:
258  typedef NativeBackendGnome::PasswordFormList PasswordFormList;
259
260  GKRMethod() : event_(false, false), result_(GNOME_KEYRING_RESULT_CANCELLED) {}
261
262  // Action methods. These call gnome_keyring_* functions. Call from UI thread.
263  // See GetProfileSpecificAppString() for more information on the app string.
264  void AddLogin(const PasswordForm& form, const char* app_string);
265  void AddLoginSearch(const PasswordForm& form, const char* app_string);
266  void UpdateLoginSearch(const PasswordForm& form, const char* app_string);
267  void RemoveLogin(const PasswordForm& form, const char* app_string);
268  void GetLogins(const PasswordForm& form, const char* app_string);
269  void GetLoginsList(uint32_t blacklisted_by_user, const char* app_string);
270  void GetAllLogins(const char* app_string);
271
272  // Use after AddLogin, RemoveLogin.
273  GnomeKeyringResult WaitResult();
274
275  // Use after AddLoginSearch, UpdateLoginSearch, GetLogins, GetLoginsList,
276  // GetAllLogins.
277  GnomeKeyringResult WaitResult(PasswordFormList* forms);
278
279 private:
280  struct GnomeKeyringAttributeListFreeDeleter {
281    inline void operator()(void* list) const {
282      gnome_keyring_attribute_list_free(
283          static_cast<GnomeKeyringAttributeList*>(list));
284    }
285  };
286
287  typedef scoped_ptr<GnomeKeyringAttributeList,
288                     GnomeKeyringAttributeListFreeDeleter> ScopedAttributeList;
289
290  // Helper methods to abbreviate Gnome Keyring long API names.
291  static void AppendString(ScopedAttributeList* list,
292                           const char* name,
293                           const char* value);
294  static void AppendString(ScopedAttributeList* list,
295                           const char* name,
296                           const std::string& value);
297  static void AppendUint32(ScopedAttributeList* list,
298                           const char* name,
299                           guint32 value);
300
301  // All these callbacks are called on UI thread.
302  static void OnOperationDone(GnomeKeyringResult result, gpointer data);
303
304  static void OnOperationGetList(GnomeKeyringResult result, GList* list,
305                                 gpointer data);
306
307  base::WaitableEvent event_;
308  GnomeKeyringResult result_;
309  NativeBackendGnome::PasswordFormList forms_;
310  // If the credential search is specified by a single form and needs to use PSL
311  // matching, then the specifying form is stored in |lookup_form_|. If PSL
312  // matching is used to find a result, then the results signon realm, origin
313  // and action are stored are replaced by those of |lookup_form_|.
314  // Additionally, |lookup_form_->signon_realm| is also used to narrow down the
315  // found logins to those which indeed PSL-match the look-up. And finally,
316  // |lookup_form_| set to NULL means that PSL matching is not required.
317  scoped_ptr<PasswordForm> lookup_form_;
318};
319
320void GKRMethod::AddLogin(const PasswordForm& form, const char* app_string) {
321  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
322  time_t date_created = form.date_created.ToTimeT();
323  // If we are asked to save a password with 0 date, use the current time.
324  // We don't want to actually save passwords as though on January 1, 1970.
325  if (!date_created)
326    date_created = time(NULL);
327  int64 date_synced = form.date_synced.ToInternalValue();
328  gnome_keyring_store_password(
329      &kGnomeSchema,
330      NULL,  // Default keyring.
331      form.origin.spec().c_str(),  // Display name.
332      UTF16ToUTF8(form.password_value).c_str(),
333      OnOperationDone,
334      this,  // data
335      NULL,  // destroy_data
336      "origin_url", form.origin.spec().c_str(),
337      "action_url", form.action.spec().c_str(),
338      "username_element", UTF16ToUTF8(form.username_element).c_str(),
339      "username_value", UTF16ToUTF8(form.username_value).c_str(),
340      "password_element", UTF16ToUTF8(form.password_element).c_str(),
341      "submit_element", UTF16ToUTF8(form.submit_element).c_str(),
342      "signon_realm", form.signon_realm.c_str(),
343      "ssl_valid", form.ssl_valid,
344      "preferred", form.preferred,
345      "date_created", base::Int64ToString(date_created).c_str(),
346      "blacklisted_by_user", form.blacklisted_by_user,
347      "type", form.type,
348      "times_used", form.times_used,
349      "scheme", form.scheme,
350      "date_synced", base::Int64ToString(date_synced).c_str(),
351      "display_name", UTF16ToUTF8(form.display_name).c_str(),
352      "avatar_url", form.avatar_url.spec().c_str(),
353      "federation_url", form.federation_url.spec().c_str(),
354      "is_zero_click", form.is_zero_click,
355      "application", app_string,
356      NULL);
357}
358
359void GKRMethod::AddLoginSearch(const PasswordForm& form,
360                               const char* app_string) {
361  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
362  lookup_form_.reset(NULL);
363  // Search GNOME Keyring for matching passwords to update.
364  ScopedAttributeList attrs(gnome_keyring_attribute_list_new());
365  AppendString(&attrs, "origin_url", form.origin.spec());
366  AppendString(&attrs, "username_element", UTF16ToUTF8(form.username_element));
367  AppendString(&attrs, "username_value", UTF16ToUTF8(form.username_value));
368  AppendString(&attrs, "password_element", UTF16ToUTF8(form.password_element));
369  AppendString(&attrs, "submit_element", UTF16ToUTF8(form.submit_element));
370  AppendString(&attrs, "signon_realm", form.signon_realm);
371  AppendString(&attrs, "application", app_string);
372  gnome_keyring_find_items(GNOME_KEYRING_ITEM_GENERIC_SECRET,
373                           attrs.get(),
374                           OnOperationGetList,
375                           /*data=*/this,
376                           /*destroy_data=*/NULL);
377}
378
379void GKRMethod::UpdateLoginSearch(const PasswordForm& form,
380                                  const char* app_string) {
381  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
382  lookup_form_.reset(NULL);
383  // Search GNOME Keyring for matching passwords to update.
384  ScopedAttributeList attrs(gnome_keyring_attribute_list_new());
385  AppendString(&attrs, "origin_url", form.origin.spec());
386  AppendString(&attrs, "username_element", UTF16ToUTF8(form.username_element));
387  AppendString(&attrs, "username_value", UTF16ToUTF8(form.username_value));
388  AppendString(&attrs, "password_element", UTF16ToUTF8(form.password_element));
389  AppendString(&attrs, "signon_realm", form.signon_realm);
390  AppendString(&attrs, "application", app_string);
391  gnome_keyring_find_items(GNOME_KEYRING_ITEM_GENERIC_SECRET,
392                           attrs.get(),
393                           OnOperationGetList,
394                           /*data=*/this,
395                           /*destroy_data=*/NULL);
396}
397
398void GKRMethod::RemoveLogin(const PasswordForm& form, const char* app_string) {
399  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
400  // We find forms using the same fields as LoginDatabase::RemoveLogin().
401  gnome_keyring_delete_password(
402      &kGnomeSchema,
403      OnOperationDone,
404      this,  // data
405      NULL,  // destroy_data
406      "origin_url", form.origin.spec().c_str(),
407      "username_element", UTF16ToUTF8(form.username_element).c_str(),
408      "username_value", UTF16ToUTF8(form.username_value).c_str(),
409      "password_element", UTF16ToUTF8(form.password_element).c_str(),
410      "submit_element", UTF16ToUTF8(form.submit_element).c_str(),
411      "signon_realm", form.signon_realm.c_str(),
412      "application", app_string,
413      NULL);
414}
415
416void GKRMethod::GetLogins(const PasswordForm& form, const char* app_string) {
417  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
418  lookup_form_.reset(new PasswordForm(form));
419  // Search GNOME Keyring for matching passwords.
420  ScopedAttributeList attrs(gnome_keyring_attribute_list_new());
421  if (!password_manager::ShouldPSLDomainMatchingApply(
422          password_manager::GetRegistryControlledDomain(
423              GURL(form.signon_realm)))) {
424    AppendString(&attrs, "signon_realm", form.signon_realm);
425  }
426  AppendString(&attrs, "application", app_string);
427  gnome_keyring_find_items(GNOME_KEYRING_ITEM_GENERIC_SECRET,
428                           attrs.get(),
429                           OnOperationGetList,
430                           /*data=*/this,
431                           /*destroy_data=*/NULL);
432}
433
434void GKRMethod::GetLoginsList(uint32_t blacklisted_by_user,
435                              const char* app_string) {
436  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
437  lookup_form_.reset(NULL);
438  // Search GNOME Keyring for matching passwords.
439  ScopedAttributeList attrs(gnome_keyring_attribute_list_new());
440  AppendUint32(&attrs, "blacklisted_by_user", blacklisted_by_user);
441  AppendString(&attrs, "application", app_string);
442  gnome_keyring_find_items(GNOME_KEYRING_ITEM_GENERIC_SECRET,
443                           attrs.get(),
444                           OnOperationGetList,
445                           /*data=*/this,
446                           /*destroy_data=*/NULL);
447}
448
449void GKRMethod::GetAllLogins(const char* app_string) {
450  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
451  lookup_form_.reset(NULL);
452  // We need to search for something, otherwise we get no results - so
453  // we search for the fixed application string.
454  ScopedAttributeList attrs(gnome_keyring_attribute_list_new());
455  AppendString(&attrs, "application", app_string);
456  gnome_keyring_find_items(GNOME_KEYRING_ITEM_GENERIC_SECRET,
457                           attrs.get(),
458                           OnOperationGetList,
459                           /*data=*/this,
460                           /*destroy_data=*/NULL);
461}
462
463GnomeKeyringResult GKRMethod::WaitResult() {
464  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
465  event_.Wait();
466  return result_;
467}
468
469GnomeKeyringResult GKRMethod::WaitResult(PasswordFormList* forms) {
470  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
471  event_.Wait();
472  if (forms->empty()) {
473    // Normal case. Avoid extra allocation by swapping.
474    forms->swap(forms_);
475  } else {
476    // Rare case. Append forms_ to *forms.
477    forms->insert(forms->end(), forms_.begin(), forms_.end());
478    forms_.clear();
479  }
480  return result_;
481}
482
483// static
484void GKRMethod::AppendString(GKRMethod::ScopedAttributeList* list,
485                             const char* name,
486                             const char* value) {
487  gnome_keyring_attribute_list_append_string(list->get(), name, value);
488}
489
490// static
491void GKRMethod::AppendString(GKRMethod::ScopedAttributeList* list,
492                             const char* name,
493                             const std::string& value) {
494  AppendString(list, name, value.c_str());
495}
496
497// static
498void GKRMethod::AppendUint32(GKRMethod::ScopedAttributeList* list,
499                             const char* name,
500                             guint32 value) {
501  gnome_keyring_attribute_list_append_uint32(list->get(), name, value);
502}
503
504// static
505void GKRMethod::OnOperationDone(GnomeKeyringResult result, gpointer data) {
506  GKRMethod* method = static_cast<GKRMethod*>(data);
507  method->result_ = result;
508  method->event_.Signal();
509}
510
511// static
512void GKRMethod::OnOperationGetList(GnomeKeyringResult result, GList* list,
513                                   gpointer data) {
514  GKRMethod* method = static_cast<GKRMethod*>(data);
515  method->result_ = result;
516  method->forms_.clear();
517  // |list| will be freed after this callback returns, so convert it now.
518  ConvertFormList(list, method->lookup_form_.get(), &method->forms_);
519  method->lookup_form_.reset(NULL);
520  method->event_.Signal();
521}
522
523}  // namespace
524
525NativeBackendGnome::NativeBackendGnome(LocalProfileId id)
526    : profile_id_(id) {
527  app_string_ = GetProfileSpecificAppString();
528}
529
530NativeBackendGnome::~NativeBackendGnome() {
531}
532
533bool NativeBackendGnome::Init() {
534  return LoadGnomeKeyring() && gnome_keyring_is_available();
535}
536
537bool NativeBackendGnome::RawAddLogin(const PasswordForm& form) {
538  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
539  GKRMethod method;
540  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
541                          base::Bind(&GKRMethod::AddLogin,
542                                     base::Unretained(&method),
543                                     form, app_string_.c_str()));
544  GnomeKeyringResult result = method.WaitResult();
545  if (result != GNOME_KEYRING_RESULT_OK) {
546    LOG(ERROR) << "Keyring save failed: "
547               << gnome_keyring_result_to_message(result);
548    return false;
549  }
550  return true;
551}
552
553password_manager::PasswordStoreChangeList NativeBackendGnome::AddLogin(
554    const PasswordForm& form) {
555  // Based on LoginDatabase::AddLogin(), we search for an existing match based
556  // on origin_url, username_element, username_value, password_element, submit
557  // element, and signon_realm first, remove that, and then add the new entry.
558  // We'd add the new one first, and then delete the original, but then the
559  // delete might actually delete the newly-added entry!
560  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
561  GKRMethod method;
562  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
563                          base::Bind(&GKRMethod::AddLoginSearch,
564                                     base::Unretained(&method),
565                                     form, app_string_.c_str()));
566  ScopedVector<autofill::PasswordForm> forms;
567  GnomeKeyringResult result = method.WaitResult(&forms.get());
568  if (result != GNOME_KEYRING_RESULT_OK &&
569      result != GNOME_KEYRING_RESULT_NO_MATCH) {
570    LOG(ERROR) << "Keyring find failed: "
571               << gnome_keyring_result_to_message(result);
572    return password_manager::PasswordStoreChangeList();
573  }
574  password_manager::PasswordStoreChangeList changes;
575  if (forms.size() > 0) {
576    if (forms.size() > 1) {
577      LOG(WARNING) << "Adding login when there are " << forms.size()
578                   << " matching logins already! Will replace only the first.";
579    }
580
581    if (RemoveLogin(*forms[0])) {
582      changes.push_back(password_manager::PasswordStoreChange(
583          password_manager::PasswordStoreChange::REMOVE, *forms[0]));
584    }
585  }
586  if (RawAddLogin(form)) {
587    changes.push_back(password_manager::PasswordStoreChange(
588        password_manager::PasswordStoreChange::ADD, form));
589  }
590  return changes;
591}
592
593bool NativeBackendGnome::UpdateLogin(
594    const PasswordForm& form,
595    password_manager::PasswordStoreChangeList* changes) {
596  // Based on LoginDatabase::UpdateLogin(), we search for forms to update by
597  // origin_url, username_element, username_value, password_element, and
598  // signon_realm. We then compare the result to the updated form. If they
599  // differ in any of the mutable fields, then we remove the original, and
600  // then add the new entry. We'd add the new one first, and then delete the
601  // original, but then the delete might actually delete the newly-added entry!
602  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
603  DCHECK(changes);
604  changes->clear();
605  GKRMethod method;
606  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
607                          base::Bind(&GKRMethod::UpdateLoginSearch,
608                                     base::Unretained(&method),
609                                     form, app_string_.c_str()));
610  ScopedVector<autofill::PasswordForm> forms;
611  GnomeKeyringResult result = method.WaitResult(&forms.get());
612  if (result != GNOME_KEYRING_RESULT_OK) {
613    LOG(ERROR) << "Keyring find failed: "
614               << gnome_keyring_result_to_message(result);
615    return false;
616  }
617
618  bool removed = false;
619  for (size_t i = 0; i < forms.size(); ++i) {
620    if (*forms[i] != form) {
621      RemoveLogin(*forms[i]);
622      removed = true;
623    }
624  }
625  if (!removed)
626    return true;
627
628  if (RawAddLogin(form)) {
629    password_manager::PasswordStoreChange change(
630        password_manager::PasswordStoreChange::UPDATE, form);
631    changes->push_back(change);
632    return true;
633  }
634  return false;
635}
636
637bool NativeBackendGnome::RemoveLogin(const PasswordForm& form) {
638  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
639  GKRMethod method;
640  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
641                          base::Bind(&GKRMethod::RemoveLogin,
642                                     base::Unretained(&method),
643                                     form, app_string_.c_str()));
644  GnomeKeyringResult result = method.WaitResult();
645  if (result != GNOME_KEYRING_RESULT_OK) {
646    // Warning, not error, because this can sometimes happen due to the user
647    // racing with the daemon to delete the password a second time.
648    LOG(WARNING) << "Keyring delete failed: "
649                 << gnome_keyring_result_to_message(result);
650    return false;
651  }
652  return true;
653}
654
655bool NativeBackendGnome::RemoveLoginsCreatedBetween(
656    base::Time delete_begin,
657    base::Time delete_end,
658    password_manager::PasswordStoreChangeList* changes) {
659  return RemoveLoginsBetween(
660      delete_begin, delete_end, CREATION_TIMESTAMP, changes);
661}
662
663bool NativeBackendGnome::RemoveLoginsSyncedBetween(
664    base::Time delete_begin,
665    base::Time delete_end,
666    password_manager::PasswordStoreChangeList* changes) {
667  return RemoveLoginsBetween(delete_begin, delete_end, SYNC_TIMESTAMP, changes);
668}
669
670bool NativeBackendGnome::GetLogins(const PasswordForm& form,
671                                   PasswordFormList* forms) {
672  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
673  GKRMethod method;
674  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
675                          base::Bind(&GKRMethod::GetLogins,
676                                     base::Unretained(&method),
677                                     form, app_string_.c_str()));
678  GnomeKeyringResult result = method.WaitResult(forms);
679  if (result == GNOME_KEYRING_RESULT_NO_MATCH)
680    return true;
681  if (result != GNOME_KEYRING_RESULT_OK) {
682    LOG(ERROR) << "Keyring find failed: "
683               << gnome_keyring_result_to_message(result);
684    return false;
685  }
686  return true;
687}
688
689bool NativeBackendGnome::GetAutofillableLogins(PasswordFormList* forms) {
690  return GetLoginsList(forms, true);
691}
692
693bool NativeBackendGnome::GetBlacklistLogins(PasswordFormList* forms) {
694  return GetLoginsList(forms, false);
695}
696
697bool NativeBackendGnome::GetLoginsList(PasswordFormList* forms,
698                                       bool autofillable) {
699  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
700
701  uint32_t blacklisted_by_user = !autofillable;
702
703  GKRMethod method;
704  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
705                          base::Bind(&GKRMethod::GetLoginsList,
706                                     base::Unretained(&method),
707                                     blacklisted_by_user, app_string_.c_str()));
708  GnomeKeyringResult result = method.WaitResult(forms);
709  if (result == GNOME_KEYRING_RESULT_NO_MATCH)
710    return true;
711  if (result != GNOME_KEYRING_RESULT_OK) {
712    LOG(ERROR) << "Keyring find failed: "
713               << gnome_keyring_result_to_message(result);
714    return false;
715  }
716  return true;
717}
718
719bool NativeBackendGnome::GetAllLogins(PasswordFormList* forms) {
720  GKRMethod method;
721  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
722                          base::Bind(&GKRMethod::GetAllLogins,
723                                     base::Unretained(&method),
724                                     app_string_.c_str()));
725  GnomeKeyringResult result = method.WaitResult(forms);
726  if (result == GNOME_KEYRING_RESULT_NO_MATCH)
727    return true;
728  if (result != GNOME_KEYRING_RESULT_OK) {
729    LOG(ERROR) << "Keyring find failed: "
730               << gnome_keyring_result_to_message(result);
731    return false;
732  }
733  return true;
734}
735
736bool NativeBackendGnome::GetLoginsBetween(base::Time get_begin,
737                                          base::Time get_end,
738                                          TimestampToCompare date_to_compare,
739                                          PasswordFormList* forms) {
740  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
741  // We could walk the list and add items as we find them, but it is much
742  // easier to build the list and then filter the results.
743  PasswordFormList all_forms;
744  if (!GetAllLogins(&all_forms))
745    return false;
746
747  base::Time autofill::PasswordForm::*date_member =
748      date_to_compare == CREATION_TIMESTAMP
749          ? &autofill::PasswordForm::date_created
750          : &autofill::PasswordForm::date_synced;
751  for (size_t i = 0; i < all_forms.size(); ++i) {
752    if (get_begin <= all_forms[i]->*date_member &&
753        (get_end.is_null() || all_forms[i]->*date_member < get_end)) {
754      forms->push_back(all_forms[i]);
755    } else {
756      delete all_forms[i];
757    }
758  }
759
760  return true;
761}
762
763bool NativeBackendGnome::RemoveLoginsBetween(
764    base::Time get_begin,
765    base::Time get_end,
766    TimestampToCompare date_to_compare,
767    password_manager::PasswordStoreChangeList* changes) {
768  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
769  DCHECK(changes);
770  changes->clear();
771  // We could walk the list and delete items as we find them, but it is much
772  // easier to build the list and use RemoveLogin() to delete them.
773  ScopedVector<autofill::PasswordForm> forms;
774  if (!GetLoginsBetween(get_begin, get_end, date_to_compare, &forms.get()))
775    return false;
776
777  bool ok = true;
778  for (size_t i = 0; i < forms.size(); ++i) {
779    if (RemoveLogin(*forms[i])) {
780      changes->push_back(password_manager::PasswordStoreChange(
781          password_manager::PasswordStoreChange::REMOVE, *forms[i]));
782    } else {
783      ok = false;
784    }
785  }
786  return ok;
787}
788
789std::string NativeBackendGnome::GetProfileSpecificAppString() const {
790  // Originally, the application string was always just "chrome" and used only
791  // so that we had *something* to search for since GNOME Keyring won't search
792  // for nothing. Now we use it to distinguish passwords for different profiles.
793  return base::StringPrintf("%s-%d", kGnomeKeyringAppString, profile_id_);
794}
795