Home | History | Annotate | Download | only in password_manager
      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. */
     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.
    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.
    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 
    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 
    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 
    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 
    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 
    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 
    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)
    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 
    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
    464 void GKRMethod::GetKeyrings() {
    465   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    466   gnome_keyring_list_keyring_names(OnOperationGetKeyrings, this, NULL);
    467 }
    468 
    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 
    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 
    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 
    486 GnomeKeyringResult GKRMethod::WaitResult() {
    487   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
    488   event_.Wait();
    489   return result_;
    490 }
    491 
    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)
    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 
    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 
    514 GnomeKeyringResult GKRMethod::WaitResult(PasswordForm** form) {
    515   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
    516   event_.Wait();
    517   *form = form_.release();
    518   return result_;
    519 }
    520 
    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
    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
    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
    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
    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
    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
    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> {
    617   void RetainCallee(GKRMethod*) {}
    618   void ReleaseCallee(GKRMethod*) {}
    619 };
    620 
    621 NativeBackendGnome::NativeBackendGnome() {
    622 }
    623 
    624 NativeBackendGnome::~NativeBackendGnome() {
    625 }
    626 
    627 bool NativeBackendGnome::Init() {
    628   return LoadGnomeKeyring() && gnome_keyring_is_available();
    629 }
    630 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    806 bool NativeBackendGnome::GetAutofillableLogins(PasswordFormList* forms) {
    807   return GetLoginsList(forms, true);
    808 }
    809 
    810 bool NativeBackendGnome::GetBlacklistLogins(PasswordFormList* forms) {
    811   return GetLoginsList(forms, false);
    812 }
    813 
    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 
    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