1// Copyright (c) 2011 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 <dbus/dbus-glib.h>
8#include <dlfcn.h>
9#include <gnome-keyring.h>
10
11#include <map>
12#include <string>
13#include <vector>
14
15#include "base/logging.h"
16#include "base/string_number_conversions.h"
17#include "base/string_util.h"
18#include "base/time.h"
19#include "base/utf_string_conversions.h"
20#include "base/synchronization/waitable_event.h"
21#include "content/browser/browser_thread.h"
22
23using webkit_glue::PasswordForm;
24
25namespace {
26
27// Many of the gnome_keyring_* functions use variable arguments, which makes
28// them difficult if not impossible to wrap in C. Therefore, we want the
29// actual uses below to either call the functions directly (if we are linking
30// against libgnome-keyring), or call them via appropriately-typed function
31// pointers (if we are dynamically loading libgnome-keyring).
32
33// Thus, instead of making a wrapper class with two implementations, we use
34// the preprocessor to rename the calls below in the dynamic load case, and
35// provide a function to initialize a set of function pointers that have the
36// alternate names. We also make sure the types are correct, since otherwise
37// dynamic loading like this would leave us vulnerable to signature changes.
38
39#if defined(DLOPEN_GNOME_KEYRING)
40
41// Call a given parameter with the name of each function we use from GNOME
42// Keyring.
43#define GNOME_KEYRING_FOR_EACH_FUNC(F)          \
44  F(is_available)                               \
45  F(store_password)                             \
46  F(delete_password)                            \
47  F(find_itemsv)                                \
48  F(result_to_message)                          \
49  F(list_keyring_names)                         \
50  F(list_item_ids)                              \
51  F(item_get_attributes)                        \
52  F(item_get_info)                              \
53  F(item_info_get_secret)
54
55// Define the actual function pointers that we'll use in application code.
56#define GNOME_KEYRING_DEFINE_WRAPPER(name) \
57  typeof(&gnome_keyring_##name) wrap_gnome_keyring_##name;
58GNOME_KEYRING_FOR_EACH_FUNC(GNOME_KEYRING_DEFINE_WRAPPER)
59#undef GNOME_KEYRING_DEFINE_WRAPPER
60
61// Make it easy to initialize the function pointers above with a loop below.
62#define GNOME_KEYRING_FUNCTION(name) \
63  {"gnome_keyring_"#name, reinterpret_cast<void**>(&wrap_gnome_keyring_##name)},
64const struct {
65  const char* name;
66  void** pointer;
67} gnome_keyring_functions[] = {
68  GNOME_KEYRING_FOR_EACH_FUNC(GNOME_KEYRING_FUNCTION)
69  {NULL, NULL}
70};
71#undef GNOME_KEYRING_FUNCTION
72
73#undef GNOME_KEYRING_FOR_EACH_FUNC
74
75// Allow application code below to use the normal function names, but actually
76// end up using the function pointers above instead.
77#define gnome_keyring_is_available \
78    wrap_gnome_keyring_is_available
79#define gnome_keyring_store_password \
80    wrap_gnome_keyring_store_password
81#define gnome_keyring_delete_password \
82    wrap_gnome_keyring_delete_password
83#define gnome_keyring_find_itemsv \
84    wrap_gnome_keyring_find_itemsv
85#define gnome_keyring_result_to_message \
86    wrap_gnome_keyring_result_to_message
87#define gnome_keyring_list_keyring_names \
88    wrap_gnome_keyring_list_keyring_names
89#define gnome_keyring_list_item_ids \
90    wrap_gnome_keyring_list_item_ids
91#define gnome_keyring_item_get_attributes \
92  wrap_gnome_keyring_item_get_attributes
93#define gnome_keyring_item_get_info \
94  wrap_gnome_keyring_item_get_info
95#define gnome_keyring_item_info_get_secret \
96  wrap_gnome_keyring_item_info_get_secret
97
98/* Load the library and initialize the function pointers. */
99bool LoadGnomeKeyring() {
100  void* handle = dlopen("libgnome-keyring.so.0", RTLD_NOW | RTLD_GLOBAL);
101  if (!handle) {
102    // We wanted to use GNOME Keyring, but we couldn't load it. Warn, because
103    // either the user asked for this, or we autodetected it incorrectly. (Or
104    // the system has broken libraries, which is also good to warn about.)
105    LOG(WARNING) << "Could not load libgnome-keyring.so.0: " << dlerror();
106    return false;
107  }
108  for (size_t i = 0; gnome_keyring_functions[i].name; ++i) {
109    dlerror();
110    *gnome_keyring_functions[i].pointer =
111        dlsym(handle, gnome_keyring_functions[i].name);
112    const char* error = dlerror();
113    if (error) {
114      LOG(ERROR) << "Unable to load symbol "
115                 << gnome_keyring_functions[i].name << ": " << error;
116      dlclose(handle);
117      return false;
118    }
119  }
120  // We leak the library handle. That's OK: this function is called only once.
121  return true;
122}
123
124// Older versions of GNOME Keyring have bugs that prevent them from working
125// correctly with the find_itemsv API. (In particular, the non-pageable memory
126// allocator is rather busted.) There is no official way to check the version,
127// nor could we figure out any reasonable unofficial way to do it. So we work
128// around it by using a much slower API.
129#define GNOME_KEYRING_WORK_AROUND_MEMORY_CORRUPTION
130
131#else  // !defined(DLOPEN_GNOME_KEYRING)
132
133bool LoadGnomeKeyring() {
134  // We don't need to do anything here. When linking directly, we also assume
135  // that whoever is compiling this code has checked that the version is OK.
136  return true;
137}
138
139#endif  // !defined(DLOPEN_GNOME_KEYRING)
140
141#define GNOME_KEYRING_APPLICATION_CHROME "chrome"
142
143// Convert the attributes of a given keyring entry into a new PasswordForm.
144// Note: does *not* get the actual password, as that is not a key attribute!
145// Returns NULL if the attributes are for the wrong application.
146PasswordForm* FormFromAttributes(GnomeKeyringAttributeList* attrs) {
147  // Read the string and int attributes into the appropriate map.
148  std::map<std::string, std::string> string_attr_map;
149  std::map<std::string, uint32_t> uint_attr_map;
150  for (guint i = 0; i < attrs->len; ++i) {
151    GnomeKeyringAttribute attr = gnome_keyring_attribute_list_index(attrs, i);
152    if (attr.type == GNOME_KEYRING_ATTRIBUTE_TYPE_STRING)
153      string_attr_map[attr.name] = attr.value.string;
154    else if (attr.type == GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32)
155      uint_attr_map[attr.name] = attr.value.integer;
156  }
157  // Check to make sure this is a password we care about.
158  if (string_attr_map["application"] != GNOME_KEYRING_APPLICATION_CHROME)
159    return NULL;
160
161  PasswordForm* form = new PasswordForm();
162  form->origin = GURL(string_attr_map["origin_url"]);
163  form->action = GURL(string_attr_map["action_url"]);
164  form->username_element = UTF8ToUTF16(string_attr_map["username_element"]);
165  form->username_value = UTF8ToUTF16(string_attr_map["username_value"]);
166  form->password_element = UTF8ToUTF16(string_attr_map["password_element"]);
167  form->submit_element = UTF8ToUTF16(string_attr_map["submit_element"]);
168  form->signon_realm = string_attr_map["signon_realm"];
169  form->ssl_valid = uint_attr_map["ssl_valid"];
170  form->preferred = uint_attr_map["preferred"];
171  int64 date_created = 0;
172  bool date_ok = base::StringToInt64(string_attr_map["date_created"],
173                                     &date_created);
174  DCHECK(date_ok);
175  form->date_created = base::Time::FromTimeT(date_created);
176  form->blacklisted_by_user = uint_attr_map["blacklisted_by_user"];
177  form->scheme = static_cast<PasswordForm::Scheme>(uint_attr_map["scheme"]);
178
179  return form;
180}
181
182// Parse all the results from the given GList into a PasswordFormList, and free
183// the GList. PasswordForms are allocated on the heap, and should be deleted by
184// the consumer.
185void ConvertFormList(GList* found,
186                     NativeBackendGnome::PasswordFormList* forms) {
187  GList* element = g_list_first(found);
188  while (element != NULL) {
189    GnomeKeyringFound* data = static_cast<GnomeKeyringFound*>(element->data);
190    GnomeKeyringAttributeList* attrs = data->attributes;
191
192    PasswordForm* form = FormFromAttributes(attrs);
193    if (form) {
194      if (data->secret) {
195        form->password_value = UTF8ToUTF16(data->secret);
196      } else {
197        LOG(WARNING) << "Unable to access password from list element!";
198      }
199      forms->push_back(form);
200    } else {
201      LOG(WARNING) << "Could not initialize PasswordForm from attributes!";
202    }
203
204    element = g_list_next(element);
205  }
206}
207
208// Schema is analagous to the fields in PasswordForm.
209const GnomeKeyringPasswordSchema kGnomeSchema = {
210  GNOME_KEYRING_ITEM_GENERIC_SECRET, {
211    { "origin_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
212    { "action_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
213    { "username_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
214    { "username_value", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
215    { "password_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
216    { "submit_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
217    { "signon_realm", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
218    { "ssl_valid", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
219    { "preferred", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
220    { "date_created", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
221    { "blacklisted_by_user", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
222    { "scheme", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 },
223    // This field is always "chrome" so that we can search for it.
224    { "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
225    { NULL }
226  }
227};
228
229// Sadly, PasswordStore goes to great lengths to switch from the originally
230// calling thread to the DB thread, and to provide an asynchronous API to
231// callers while using a synchronous (virtual) API provided by subclasses like
232// PasswordStoreX -- but GNOME Keyring really wants to be on the GLib main
233// thread, which is the UI thread to us. So we end up having to switch threads
234// again, possibly back to the very same thread (in case the UI thread is the
235// caller, e.g. in the password management UI), and *block* the DB thread
236// waiting for a response from the UI thread to provide the synchronous API
237// PasswordStore expects of us. (It will then in turn switch back to the
238// original caller to send the asynchronous reply to the original request.)
239
240// This class represents a call to a GNOME Keyring method. A RunnableMethod
241// should be posted to the UI thread to call one of its action methods, and then
242// a WaitResult() method should be called to wait for the result. Each instance
243// supports only one outstanding method at a time, though multiple instances may
244// be used in parallel.
245class GKRMethod {
246 public:
247  typedef NativeBackendGnome::PasswordFormList PasswordFormList;
248
249  GKRMethod() : event_(false, false), result_(GNOME_KEYRING_RESULT_CANCELLED) {}
250
251  // Action methods. These call gnome_keyring_* functions. Call from UI thread.
252  void AddLogin(const PasswordForm& form);
253  void AddLoginSearch(const PasswordForm& form);
254  void UpdateLoginSearch(const PasswordForm& form);
255  void RemoveLogin(const PasswordForm& form);
256  void GetLogins(const PasswordForm& form);
257#if !defined(GNOME_KEYRING_WORK_AROUND_MEMORY_CORRUPTION)
258  void GetLoginsList(uint32_t blacklisted_by_user);
259  void GetAllLogins();
260#else
261  void GetKeyrings();
262  void GetItemIds(const char* keyring);
263  void GetItemAttrs(const char* keyring, guint id);
264  void GetItemInfo(const char* keyring, guint id);
265#endif
266
267  // Use after AddLogin, RemoveLogin.
268  GnomeKeyringResult WaitResult();
269
270  // Use after AddLoginSearch, UpdateLoginSearch, GetLogins, GetLoginsList,
271  // GetAllLogins.
272  GnomeKeyringResult WaitResult(PasswordFormList* forms);
273
274#if defined(GNOME_KEYRING_WORK_AROUND_MEMORY_CORRUPTION)
275  // Use after GetKeyrings().
276  GnomeKeyringResult WaitResult(std::vector<std::string>* keyrings);
277
278  // Use after GetItemIds().
279  GnomeKeyringResult WaitResult(std::vector<guint>* item_ids);
280
281  // Use after GetItemAttrs().
282  GnomeKeyringResult WaitResult(PasswordForm** form);
283
284  // Use after GetItemInfo().
285  GnomeKeyringResult WaitResult(string16* password);
286#endif
287
288 private:
289  // All these callbacks are called on UI thread.
290  static void OnOperationDone(GnomeKeyringResult result, gpointer data);
291
292  static void OnOperationGetList(GnomeKeyringResult result, GList* list,
293                                 gpointer data);
294
295#if defined(GNOME_KEYRING_WORK_AROUND_MEMORY_CORRUPTION)
296  static void OnOperationGetKeyrings(GnomeKeyringResult result, GList* list,
297                                     gpointer data);
298
299  static void OnOperationGetIds(GnomeKeyringResult result, GList* list,
300                                gpointer data);
301
302  static void OnOperationGetAttrs(GnomeKeyringResult result,
303                                  GnomeKeyringAttributeList* attrs,
304                                  gpointer data);
305
306  static void OnOperationGetInfo(GnomeKeyringResult result,
307                                 GnomeKeyringItemInfo* info,
308                                 gpointer data);
309#endif
310
311  base::WaitableEvent event_;
312  GnomeKeyringResult result_;
313  NativeBackendGnome::PasswordFormList forms_;
314#if defined(GNOME_KEYRING_WORK_AROUND_MEMORY_CORRUPTION)
315  std::vector<std::string> keyrings_;
316  std::vector<guint> item_ids_;
317  scoped_ptr<PasswordForm> form_;
318  string16 password_;
319#endif
320};
321
322void GKRMethod::AddLogin(const PasswordForm& form) {
323  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
324  time_t date_created = form.date_created.ToTimeT();
325  // If we are asked to save a password with 0 date, use the current time.
326  // We don't want to actually save passwords as though on January 1, 1970.
327  if (!date_created)
328    date_created = time(NULL);
329  gnome_keyring_store_password(
330      &kGnomeSchema,
331      NULL,  // Default keyring.
332      form.origin.spec().c_str(),  // Display name.
333      UTF16ToUTF8(form.password_value).c_str(),
334      OnOperationDone,
335      this,  // data
336      NULL,  // destroy_data
337      "origin_url", form.origin.spec().c_str(),
338      "action_url", form.action.spec().c_str(),
339      "username_element", UTF16ToUTF8(form.username_element).c_str(),
340      "username_value", UTF16ToUTF8(form.username_value).c_str(),
341      "password_element", UTF16ToUTF8(form.password_element).c_str(),
342      "submit_element", UTF16ToUTF8(form.submit_element).c_str(),
343      "signon_realm", form.signon_realm.c_str(),
344      "ssl_valid", form.ssl_valid,
345      "preferred", form.preferred,
346      "date_created", base::Int64ToString(date_created).c_str(),
347      "blacklisted_by_user", form.blacklisted_by_user,
348      "scheme", form.scheme,
349      "application", GNOME_KEYRING_APPLICATION_CHROME,
350      NULL);
351}
352
353void GKRMethod::AddLoginSearch(const PasswordForm& form) {
354  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
355  // Search GNOME Keyring for matching passwords to update.
356  gnome_keyring_find_itemsv(
357      GNOME_KEYRING_ITEM_GENERIC_SECRET,
358      OnOperationGetList,
359      this,  // data
360      NULL,  // destroy_data
361      "origin_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
362      form.origin.spec().c_str(),
363      "username_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
364      UTF16ToUTF8(form.username_element).c_str(),
365      "username_value", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
366      UTF16ToUTF8(form.username_value).c_str(),
367      "password_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
368      UTF16ToUTF8(form.password_element).c_str(),
369      "submit_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
370      UTF16ToUTF8(form.submit_element).c_str(),
371      "signon_realm", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
372      form.signon_realm.c_str(),
373      "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
374      GNOME_KEYRING_APPLICATION_CHROME,
375      NULL);
376}
377
378void GKRMethod::UpdateLoginSearch(const PasswordForm& form) {
379  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
380  // Search GNOME Keyring for matching passwords to update.
381  gnome_keyring_find_itemsv(
382      GNOME_KEYRING_ITEM_GENERIC_SECRET,
383      OnOperationGetList,
384      this,  // data
385      NULL,  // destroy_data
386      "origin_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
387      form.origin.spec().c_str(),
388      "username_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
389      UTF16ToUTF8(form.username_element).c_str(),
390      "username_value", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
391      UTF16ToUTF8(form.username_value).c_str(),
392      "password_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
393      UTF16ToUTF8(form.password_element).c_str(),
394      "signon_realm", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
395      form.signon_realm.c_str(),
396      "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
397      GNOME_KEYRING_APPLICATION_CHROME,
398      NULL);
399}
400
401void GKRMethod::RemoveLogin(const PasswordForm& form) {
402  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
403  // We find forms using the same fields as LoginDatabase::RemoveLogin().
404  gnome_keyring_delete_password(
405      &kGnomeSchema,
406      OnOperationDone,
407      this,  // data
408      NULL,  // destroy_data
409      "origin_url", form.origin.spec().c_str(),
410      "action_url", form.action.spec().c_str(),
411      "username_element", UTF16ToUTF8(form.username_element).c_str(),
412      "username_value", UTF16ToUTF8(form.username_value).c_str(),
413      "password_element", UTF16ToUTF8(form.password_element).c_str(),
414      "submit_element", UTF16ToUTF8(form.submit_element).c_str(),
415      "signon_realm", form.signon_realm.c_str(),
416      NULL);
417}
418
419void GKRMethod::GetLogins(const PasswordForm& form) {
420  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
421  // Search GNOME Keyring for matching passwords.
422  gnome_keyring_find_itemsv(
423      GNOME_KEYRING_ITEM_GENERIC_SECRET,
424      OnOperationGetList,
425      this,  // data
426      NULL,  // destroy_data
427      "signon_realm", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
428      form.signon_realm.c_str(),
429      "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
430      GNOME_KEYRING_APPLICATION_CHROME,
431      NULL);
432}
433
434#if !defined(GNOME_KEYRING_WORK_AROUND_MEMORY_CORRUPTION)
435void GKRMethod::GetLoginsList(uint32_t blacklisted_by_user) {
436  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
437  // Search GNOME Keyring for matching passwords.
438  gnome_keyring_find_itemsv(
439      GNOME_KEYRING_ITEM_GENERIC_SECRET,
440      OnOperationGetList,
441      this,  // data
442      NULL,  // destroy_data
443      "blacklisted_by_user", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32,
444      blacklisted_by_user,
445      "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
446      GNOME_KEYRING_APPLICATION_CHROME,
447      NULL);
448}
449
450void GKRMethod::GetAllLogins() {
451  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
452  // We need to search for something, otherwise we get no results - so
453  // we search for the fixed application string.
454  gnome_keyring_find_itemsv(
455      GNOME_KEYRING_ITEM_GENERIC_SECRET,
456      OnOperationGetList,
457      this,  // data
458      NULL,  // destroy_data
459      "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
460      GNOME_KEYRING_APPLICATION_CHROME,
461      NULL);
462}
463#else
464void GKRMethod::GetKeyrings() {
465  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
466  gnome_keyring_list_keyring_names(OnOperationGetKeyrings, this, NULL);
467}
468
469void GKRMethod::GetItemIds(const char* keyring) {
470  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
471  gnome_keyring_list_item_ids(keyring, OnOperationGetIds, this, NULL);
472}
473
474void GKRMethod::GetItemAttrs(const char* keyring, guint id) {
475  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
476  gnome_keyring_item_get_attributes(keyring, id, OnOperationGetAttrs, this,
477                                    NULL);
478}
479
480void GKRMethod::GetItemInfo(const char* keyring, guint id) {
481  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
482  gnome_keyring_item_get_info(keyring, id, OnOperationGetInfo, this, NULL);
483}
484#endif
485
486GnomeKeyringResult GKRMethod::WaitResult() {
487  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
488  event_.Wait();
489  return result_;
490}
491
492GnomeKeyringResult GKRMethod::WaitResult(PasswordFormList* forms) {
493  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
494  event_.Wait();
495  forms->swap(forms_);
496  return result_;
497}
498
499#if defined(GNOME_KEYRING_WORK_AROUND_MEMORY_CORRUPTION)
500GnomeKeyringResult GKRMethod::WaitResult(std::vector<std::string>* keyrings) {
501  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
502  event_.Wait();
503  keyrings->swap(keyrings_);
504  return result_;
505}
506
507GnomeKeyringResult GKRMethod::WaitResult(std::vector<guint>* item_ids) {
508  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
509  event_.Wait();
510  item_ids->swap(item_ids_);
511  return result_;
512}
513
514GnomeKeyringResult GKRMethod::WaitResult(PasswordForm** form) {
515  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
516  event_.Wait();
517  *form = form_.release();
518  return result_;
519}
520
521GnomeKeyringResult GKRMethod::WaitResult(string16* password) {
522  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
523  event_.Wait();
524  *password = password_;
525  return result_;
526}
527#endif
528
529// static
530void GKRMethod::OnOperationDone(GnomeKeyringResult result, gpointer data) {
531  GKRMethod* method = static_cast<GKRMethod*>(data);
532  method->result_ = result;
533  method->event_.Signal();
534}
535
536// static
537void GKRMethod::OnOperationGetList(GnomeKeyringResult result, GList* list,
538                                   gpointer data) {
539  GKRMethod* method = static_cast<GKRMethod*>(data);
540  method->result_ = result;
541  method->forms_.clear();
542  // |list| will be freed after this callback returns, so convert it now.
543  ConvertFormList(list, &method->forms_);
544  method->event_.Signal();
545}
546
547#if defined(GNOME_KEYRING_WORK_AROUND_MEMORY_CORRUPTION)
548// static
549void GKRMethod::OnOperationGetKeyrings(GnomeKeyringResult result, GList* list,
550                                       gpointer data) {
551  GKRMethod* method = static_cast<GKRMethod*>(data);
552  method->result_ = result;
553  method->keyrings_.clear();
554  GList* element = g_list_first(list);
555  while (element != NULL) {
556    const char* data = static_cast<const char*>(element->data);
557    method->keyrings_.push_back(std::string(data));
558    element = g_list_next(element);
559  }
560  method->event_.Signal();
561}
562
563// static
564void GKRMethod::OnOperationGetIds(GnomeKeyringResult result, GList* list,
565                                  gpointer data) {
566  GKRMethod* method = static_cast<GKRMethod*>(data);
567  method->result_ = result;
568  method->item_ids_.clear();
569  // |list| will be freed after this callback returns, so save it now.
570  for (GList* i = list; i; i = i->next) {
571    guint id = GPOINTER_TO_UINT(i->data);
572    method->item_ids_.push_back(id);
573  }
574  method->event_.Signal();
575}
576
577// static
578void GKRMethod::OnOperationGetAttrs(GnomeKeyringResult result,
579                                    GnomeKeyringAttributeList* attrs,
580                                    gpointer data) {
581  GKRMethod* method = static_cast<GKRMethod*>(data);
582  method->result_ = result;
583  // |attrs| will be freed after this callback returns, so convert it now.
584  if (result == GNOME_KEYRING_RESULT_OK)
585    method->form_.reset(FormFromAttributes(attrs));
586  method->event_.Signal();
587}
588
589// static
590void GKRMethod::OnOperationGetInfo(GnomeKeyringResult result,
591                                   GnomeKeyringItemInfo* info,
592                                   gpointer data) {
593  GKRMethod* method = static_cast<GKRMethod*>(data);
594  method->result_ = result;
595  // |info| will be freed after this callback returns, so use it now.
596  if (result == GNOME_KEYRING_RESULT_OK) {
597    char* secret = gnome_keyring_item_info_get_secret(info);
598    if (secret) {
599      method->password_ = UTF8ToUTF16(secret);
600      // gnome_keyring_item_info_get_secret() allocates and returns a new copy
601      // of the secret, so we have to free it afterward.
602      free(secret);
603    } else {
604      LOG(WARNING) << "Unable to access password from item info!";
605    }
606  }
607  method->event_.Signal();
608}
609#endif
610
611}  // namespace
612
613// GKRMethod isn't reference counted, but it always outlasts runnable
614// methods against it because the caller waits for those methods to run.
615template<>
616struct RunnableMethodTraits<GKRMethod> {
617  void RetainCallee(GKRMethod*) {}
618  void ReleaseCallee(GKRMethod*) {}
619};
620
621NativeBackendGnome::NativeBackendGnome() {
622}
623
624NativeBackendGnome::~NativeBackendGnome() {
625}
626
627bool NativeBackendGnome::Init() {
628  return LoadGnomeKeyring() && gnome_keyring_is_available();
629}
630
631bool NativeBackendGnome::RawAddLogin(const PasswordForm& form) {
632  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
633  GKRMethod method;
634  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
635                          NewRunnableMethod(&method,
636                                            &GKRMethod::AddLogin,
637                                            form));
638  GnomeKeyringResult result = method.WaitResult();
639  if (result != GNOME_KEYRING_RESULT_OK) {
640    LOG(ERROR) << "Keyring save failed: "
641               << gnome_keyring_result_to_message(result);
642    return false;
643  }
644  return true;
645}
646
647bool NativeBackendGnome::AddLogin(const PasswordForm& form) {
648  // Based on LoginDatabase::AddLogin(), we search for an existing match based
649  // on origin_url, username_element, username_value, password_element, submit
650  // element, and signon_realm first, remove that, and then add the new entry.
651  // We'd add the new one first, and then delete the original, but then the
652  // delete might actually delete the newly-added entry!
653  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
654  GKRMethod method;
655  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
656                         NewRunnableMethod(&method,
657                                           &GKRMethod::AddLoginSearch,
658                                           form));
659  PasswordFormList forms;
660  GnomeKeyringResult result = method.WaitResult(&forms);
661  if (result != GNOME_KEYRING_RESULT_OK &&
662      result != GNOME_KEYRING_RESULT_NO_MATCH) {
663    LOG(ERROR) << "Keyring find failed: "
664               << gnome_keyring_result_to_message(result);
665    return false;
666  }
667  if (forms.size() > 0) {
668    if (forms.size() > 1) {
669      LOG(WARNING) << "Adding login when there are " << forms.size() <<
670                   " matching logins already! Will replace only the first.";
671    }
672    RemoveLogin(*forms[0]);
673    for (size_t i = 0; i < forms.size(); ++i)
674      delete forms[i];
675  }
676  return RawAddLogin(form);
677}
678
679bool NativeBackendGnome::UpdateLogin(const PasswordForm& form) {
680  // Based on LoginDatabase::UpdateLogin(), we search for forms to update by
681  // origin_url, username_element, username_value, password_element, and
682  // signon_realm. We then compare the result to the updated form. If they
683  // differ in any of the action, password_value, ssl_valid, or preferred
684  // fields, then we remove the original, and then add the new entry. We'd add
685  // the new one first, and then delete the original, but then the delete might
686  // actually delete the newly-added entry!
687  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
688  GKRMethod method;
689  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
690                         NewRunnableMethod(&method,
691                                           &GKRMethod::UpdateLoginSearch,
692                                           form));
693  PasswordFormList forms;
694  GnomeKeyringResult result = method.WaitResult(&forms);
695  if (result != GNOME_KEYRING_RESULT_OK) {
696    LOG(ERROR) << "Keyring find failed: "
697               << gnome_keyring_result_to_message(result);
698    return false;
699  }
700  bool ok = true;
701  for (size_t i = 0; i < forms.size(); ++i) {
702    if (forms[i]->action != form.action ||
703        forms[i]->password_value != form.password_value ||
704        forms[i]->ssl_valid != form.ssl_valid ||
705        forms[i]->preferred != form.preferred) {
706      RemoveLogin(*forms[i]);
707    }
708  }
709  for (size_t i = 0; i < forms.size(); ++i) {
710    if (forms[i]->action != form.action ||
711        forms[i]->password_value != form.password_value ||
712        forms[i]->ssl_valid != form.ssl_valid ||
713        forms[i]->preferred != form.preferred) {
714      forms[i]->action = form.action;
715      forms[i]->password_value = form.password_value;
716      forms[i]->ssl_valid = form.ssl_valid;
717      forms[i]->preferred = form.preferred;
718      if (!RawAddLogin(*forms[i]))
719        ok = false;
720    }
721    delete forms[i];
722  }
723  return ok;
724}
725
726bool NativeBackendGnome::RemoveLogin(const PasswordForm& form) {
727  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
728  GKRMethod method;
729  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
730                          NewRunnableMethod(&method,
731                                            &GKRMethod::RemoveLogin,
732                                            form));
733  GnomeKeyringResult result = method.WaitResult();
734  if (result != GNOME_KEYRING_RESULT_OK) {
735    LOG(ERROR) << "Keyring delete failed: "
736               << gnome_keyring_result_to_message(result);
737    return false;
738  }
739  return true;
740}
741
742bool NativeBackendGnome::RemoveLoginsCreatedBetween(
743    const base::Time& delete_begin,
744    const base::Time& delete_end) {
745  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
746  bool ok = true;
747  // We could walk the list and delete items as we find them, but it is much
748  // easier to build the list and use RemoveLogin() to delete them.
749  PasswordFormList forms;
750  if (!GetAllLogins(&forms))
751    return false;
752
753  for (size_t i = 0; i < forms.size(); ++i) {
754    if (delete_begin <= forms[i]->date_created &&
755        (delete_end.is_null() || forms[i]->date_created < delete_end)) {
756      if (!RemoveLogin(*forms[i]))
757        ok = false;
758    }
759    delete forms[i];
760  }
761  return ok;
762}
763
764bool NativeBackendGnome::GetLogins(const PasswordForm& form,
765                                   PasswordFormList* forms) {
766  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
767  GKRMethod method;
768  BrowserThread::PostTask(
769      BrowserThread::UI,
770      FROM_HERE,
771      NewRunnableMethod(&method, &GKRMethod::GetLogins, form));
772  GnomeKeyringResult result = method.WaitResult(forms);
773  if (result == GNOME_KEYRING_RESULT_NO_MATCH)
774    return true;
775  if (result != GNOME_KEYRING_RESULT_OK) {
776    LOG(ERROR) << "Keyring find failed: "
777               << gnome_keyring_result_to_message(result);
778    return false;
779  }
780  return true;
781}
782
783bool NativeBackendGnome::GetLoginsCreatedBetween(const base::Time& get_begin,
784                                                 const base::Time& get_end,
785                                                 PasswordFormList* forms) {
786  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
787  // We could walk the list and add items as we find them, but it is much
788  // easier to build the list and then filter the results.
789  PasswordFormList all_forms;
790  if (!GetAllLogins(&all_forms))
791    return false;
792
793  forms->reserve(forms->size() + all_forms.size());
794  for (size_t i = 0; i < all_forms.size(); ++i) {
795    if (get_begin <= all_forms[i]->date_created &&
796        (get_end.is_null() || all_forms[i]->date_created < get_end)) {
797      forms->push_back(all_forms[i]);
798    } else {
799      delete all_forms[i];
800    }
801  }
802
803  return true;
804}
805
806bool NativeBackendGnome::GetAutofillableLogins(PasswordFormList* forms) {
807  return GetLoginsList(forms, true);
808}
809
810bool NativeBackendGnome::GetBlacklistLogins(PasswordFormList* forms) {
811  return GetLoginsList(forms, false);
812}
813
814bool NativeBackendGnome::GetLoginsList(PasswordFormList* forms,
815                                       bool autofillable) {
816  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
817
818  uint32_t blacklisted_by_user = !autofillable;
819
820#if !defined(GNOME_KEYRING_WORK_AROUND_MEMORY_CORRUPTION)
821  GKRMethod method;
822  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
823                          NewRunnableMethod(&method,
824                                            &GKRMethod::GetLoginsList,
825                                            blacklisted_by_user));
826  GnomeKeyringResult result = method.WaitResult(forms);
827  if (result == GNOME_KEYRING_RESULT_NO_MATCH)
828    return true;
829  if (result != GNOME_KEYRING_RESULT_OK) {
830    LOG(ERROR) << "Keyring find failed: "
831               << gnome_keyring_result_to_message(result);
832    return false;
833  }
834  return true;
835#else
836  PasswordFormList all_forms;
837  if (!GetAllLogins(&all_forms))
838    return false;
839  // Now manually filter the results for the values we care about.
840  for (size_t i = 0; i < all_forms.size(); ++i) {
841    if (all_forms[i]->blacklisted_by_user == blacklisted_by_user)
842      forms->push_back(all_forms[i]);
843    else
844      delete all_forms[i];
845  }
846  return true;
847#endif
848}
849
850bool NativeBackendGnome::GetAllLogins(PasswordFormList* forms) {
851  GKRMethod method;
852#if !defined(GNOME_KEYRING_WORK_AROUND_MEMORY_CORRUPTION)
853  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
854                          NewRunnableMethod(&method,
855                                            &GKRMethod::GetAllLogins));
856  GnomeKeyringResult result = method.WaitResult(forms);
857  if (result == GNOME_KEYRING_RESULT_NO_MATCH)
858    return true;
859  if (result != GNOME_KEYRING_RESULT_OK) {
860    LOG(ERROR) << "Keyring find failed: "
861               << gnome_keyring_result_to_message(result);
862    return false;
863  }
864  return true;
865#else
866  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
867                          NewRunnableMethod(&method,
868                                            &GKRMethod::GetKeyrings));
869  std::vector<std::string> keyrings;
870  GnomeKeyringResult result = method.WaitResult(&keyrings);
871  if (result != GNOME_KEYRING_RESULT_OK) {
872    LOG(ERROR) << "Keyring list failed: "
873               << gnome_keyring_result_to_message(result);
874    return false;
875  }
876
877  // We could parallelize this, but there probably aren't many keyrings.
878  std::vector<std::pair<const char *, guint> > item_list;
879  for (size_t i = 0; i < keyrings.size(); ++i) {
880    const char *keyring = keyrings[i].c_str();
881    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
882                            NewRunnableMethod(&method,
883                                              &GKRMethod::GetItemIds,
884                                              keyring));
885    std::vector<guint> item_ids;
886    GnomeKeyringResult result = method.WaitResult(&item_ids);
887    if (result != GNOME_KEYRING_RESULT_OK) {
888      LOG(ERROR) << "Keyring itemid list failed: "
889                 << gnome_keyring_result_to_message(result);
890      return false;
891    }
892    for (size_t j = 0; j < item_ids.size(); ++j)
893      item_list.push_back(std::make_pair(keyring, item_ids[j]));
894  }
895
896  // We can parallelize getting the item attributes.
897  GKRMethod* methods = new GKRMethod[item_list.size()];
898  for (size_t i = 0; i < item_list.size(); ++i) {
899    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
900                            NewRunnableMethod(&methods[i],
901                                              &GKRMethod::GetItemAttrs,
902                                              item_list[i].first,
903                                              item_list[i].second));
904  }
905
906  bool success = true;
907
908  // We can also parallelize getting the item info (i.e. passwords).
909  PasswordFormList all_forms;
910  all_forms.resize(item_list.size());
911  for (size_t i = 0; i < item_list.size(); ++i) {
912    result = methods[i].WaitResult(&all_forms[i]);
913    if (result != GNOME_KEYRING_RESULT_OK) {
914      LOG(ERROR) << "Keyring get item attributes failed: "
915                 << gnome_keyring_result_to_message(result);
916      // We explicitly do not break out here. We must wait on all the other
917      // methods first, and we may have already posted new methods. So, we just
918      // note the failure and continue.
919      success = false;
920    }
921    if (all_forms[i]) {
922      BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
923                              NewRunnableMethod(&methods[i],
924                                                &GKRMethod::GetItemInfo,
925                                                item_list[i].first,
926                                                item_list[i].second));
927    }
928  }
929
930  // Now just wait for all the passwords to come in.
931  for (size_t i = 0; i < item_list.size(); ++i) {
932    if (!all_forms[i])
933      continue;
934    result = methods[i].WaitResult(&all_forms[i]->password_value);
935    if (result != GNOME_KEYRING_RESULT_OK) {
936      LOG(ERROR) << "Keyring get item info failed: "
937                 << gnome_keyring_result_to_message(result);
938      delete all_forms[i];
939      all_forms[i] = NULL;
940      // We explicitly do not break out here (see above).
941      success = false;
942    }
943  }
944
945  delete[] methods;
946
947  if (success) {
948    // If we succeeded, output all the forms.
949    for (size_t i = 0; i < item_list.size(); ++i) {
950      if (all_forms[i])
951        forms->push_back(all_forms[i]);
952    }
953  } else {
954    // Otherwise, free them.
955    for (size_t i = 0; i < item_list.size(); ++i)
956      delete all_forms[i];
957  }
958
959  return success;
960#endif
961}
962