• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
23 using webkit_glue::PasswordForm;
24 
25 namespace {
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;
58 GNOME_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)},
64 const 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. */
LoadGnomeKeyring()99 bool 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 
133 bool 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.
FormFromAttributes(GnomeKeyringAttributeList * attrs)146 PasswordForm* 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.
ConvertFormList(GList * found,NativeBackendGnome::PasswordFormList * forms)185 void 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.
209 const 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.
245 class GKRMethod {
246  public:
247   typedef NativeBackendGnome::PasswordFormList PasswordFormList;
248 
GKRMethod()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 
AddLogin(const PasswordForm & form)322 void 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 
AddLoginSearch(const PasswordForm & form)353 void 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 
UpdateLoginSearch(const PasswordForm & form)378 void 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 
RemoveLogin(const PasswordForm & form)401 void 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 
GetLogins(const PasswordForm & form)419 void 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)
GetLoginsList(uint32_t blacklisted_by_user)435 void 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 
GetAllLogins()450 void 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
GetKeyrings()464 void GKRMethod::GetKeyrings() {
465   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
466   gnome_keyring_list_keyring_names(OnOperationGetKeyrings, this, NULL);
467 }
468 
GetItemIds(const char * keyring)469 void GKRMethod::GetItemIds(const char* keyring) {
470   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
471   gnome_keyring_list_item_ids(keyring, OnOperationGetIds, this, NULL);
472 }
473 
GetItemAttrs(const char * keyring,guint id)474 void 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 
GetItemInfo(const char * keyring,guint id)480 void 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 
WaitResult()486 GnomeKeyringResult GKRMethod::WaitResult() {
487   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
488   event_.Wait();
489   return result_;
490 }
491 
WaitResult(PasswordFormList * forms)492 GnomeKeyringResult 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)
WaitResult(std::vector<std::string> * keyrings)500 GnomeKeyringResult 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 
WaitResult(std::vector<guint> * item_ids)507 GnomeKeyringResult 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 
WaitResult(PasswordForm ** form)514 GnomeKeyringResult GKRMethod::WaitResult(PasswordForm** form) {
515   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
516   event_.Wait();
517   *form = form_.release();
518   return result_;
519 }
520 
WaitResult(string16 * password)521 GnomeKeyringResult 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
OnOperationDone(GnomeKeyringResult result,gpointer data)530 void 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
OnOperationGetList(GnomeKeyringResult result,GList * list,gpointer data)537 void 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
OnOperationGetKeyrings(GnomeKeyringResult result,GList * list,gpointer data)549 void 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
OnOperationGetIds(GnomeKeyringResult result,GList * list,gpointer data)564 void 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
OnOperationGetAttrs(GnomeKeyringResult result,GnomeKeyringAttributeList * attrs,gpointer data)578 void 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
OnOperationGetInfo(GnomeKeyringResult result,GnomeKeyringItemInfo * info,gpointer data)590 void 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.
615 template<>
616 struct RunnableMethodTraits<GKRMethod> {
RetainCalleeRunnableMethodTraits617   void RetainCallee(GKRMethod*) {}
ReleaseCalleeRunnableMethodTraits618   void ReleaseCallee(GKRMethod*) {}
619 };
620 
NativeBackendGnome()621 NativeBackendGnome::NativeBackendGnome() {
622 }
623 
~NativeBackendGnome()624 NativeBackendGnome::~NativeBackendGnome() {
625 }
626 
Init()627 bool NativeBackendGnome::Init() {
628   return LoadGnomeKeyring() && gnome_keyring_is_available();
629 }
630 
RawAddLogin(const PasswordForm & form)631 bool 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 
AddLogin(const PasswordForm & form)647 bool 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 
UpdateLogin(const PasswordForm & form)679 bool 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 
RemoveLogin(const PasswordForm & form)726 bool 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 
RemoveLoginsCreatedBetween(const base::Time & delete_begin,const base::Time & delete_end)742 bool 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 
GetLogins(const PasswordForm & form,PasswordFormList * forms)764 bool 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 
GetLoginsCreatedBetween(const base::Time & get_begin,const base::Time & get_end,PasswordFormList * forms)783 bool 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 
GetAutofillableLogins(PasswordFormList * forms)806 bool NativeBackendGnome::GetAutofillableLogins(PasswordFormList* forms) {
807   return GetLoginsList(forms, true);
808 }
809 
GetBlacklistLogins(PasswordFormList * forms)810 bool NativeBackendGnome::GetBlacklistLogins(PasswordFormList* forms) {
811   return GetLoginsList(forms, false);
812 }
813 
GetLoginsList(PasswordFormList * forms,bool autofillable)814 bool 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 
GetAllLogins(PasswordFormList * forms)850 bool 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