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