| Index: chrome/browser/password_manager/native_backend_gnome_x.cc
|
| ===================================================================
|
| --- chrome/browser/password_manager/native_backend_gnome_x.cc (revision 52689)
|
| +++ chrome/browser/password_manager/native_backend_gnome_x.cc (working copy)
|
| @@ -4,56 +4,57 @@
|
|
|
| #include "chrome/browser/password_manager/native_backend_gnome_x.h"
|
|
|
| +#include <dbus/dbus-glib.h>
|
| +#include <dlfcn.h>
|
| +#include <gnome-keyring.h>
|
| +
|
| #include <map>
|
| #include <string>
|
| +#include <vector>
|
|
|
| -#include <dbus/dbus-glib.h>
|
| -#include <dlfcn.h>
|
| -
|
| #include "base/logging.h"
|
| #include "base/string_util.h"
|
| #include "base/time.h"
|
| #include "base/utf_string_conversions.h"
|
| +#include "base/waitable_event.h"
|
| #include "chrome/browser/chrome_thread.h"
|
|
|
| using webkit_glue::PasswordForm;
|
|
|
| namespace {
|
|
|
| -/* Many of the gnome_keyring_* functions use variable arguments, which makes
|
| - * them difficult if not impossible to wrap in C. Therefore, we want the
|
| - * actual uses below to either call the functions directly (if we are linking
|
| - * against libgnome-keyring), or call them via appropriately-typed function
|
| - * pointers (if we are dynamically loading libgnome-keyring).
|
| - *
|
| - * Thus, instead of making a wrapper class with two implementations, we use
|
| - * the preprocessor to rename the calls below in the dynamic load case, and
|
| - * provide a function to initialize a set of function pointers that have the
|
| - * alternate names. We also make sure the types are correct, since otherwise
|
| - * dynamic loading like this would leave us vulnerable to signature changes. */
|
| +// Many of the gnome_keyring_* functions use variable arguments, which makes
|
| +// them difficult if not impossible to wrap in C. Therefore, we want the
|
| +// actual uses below to either call the functions directly (if we are linking
|
| +// against libgnome-keyring), or call them via appropriately-typed function
|
| +// pointers (if we are dynamically loading libgnome-keyring).
|
|
|
| +// Thus, instead of making a wrapper class with two implementations, we use
|
| +// the preprocessor to rename the calls below in the dynamic load case, and
|
| +// provide a function to initialize a set of function pointers that have the
|
| +// alternate names. We also make sure the types are correct, since otherwise
|
| +// dynamic loading like this would leave us vulnerable to signature changes.
|
| +
|
| #if defined(DLOPEN_GNOME_KEYRING)
|
|
|
| -// Call a given parameter with the name of each func we use from
|
| -// gnome keyring.
|
| +// Call a given parameter with the name of each function we use from GNOME
|
| +// Keyring.
|
| #define GNOME_KEYRING_FOR_EACH_FUNC(F) \
|
| F(is_available) \
|
| - F(store_password_sync) \
|
| - F(delete_password_sync) \
|
| - F(find_itemsv_sync) \
|
| + F(store_password) \
|
| + F(delete_password) \
|
| + F(find_itemsv) \
|
| F(result_to_message) \
|
| - F(found_list_free) \
|
| - F(list_item_ids_sync) \
|
| - F(item_get_attributes_sync) \
|
| - F(attribute_list_free) \
|
| - F(item_get_info_sync) \
|
| - F(item_info_get_secret) \
|
| - F(item_info_free)
|
| + F(list_item_ids) \
|
| + F(item_get_attributes) \
|
| + F(item_get_info) \
|
| + F(item_info_get_secret)
|
|
|
| -#define GNOME_KEYRING_DECLARE_TYPE(name) \
|
| +// Define the actual function pointers that we'll use in application code.
|
| +#define GNOME_KEYRING_DEFINE_WRAPPER(name) \
|
| typeof(&gnome_keyring_##name) wrap_gnome_keyring_##name;
|
| -GNOME_KEYRING_FOR_EACH_FUNC(GNOME_KEYRING_DECLARE_TYPE)
|
| -#undef GNOME_KEYRING_DECLARE_TYPE
|
| +GNOME_KEYRING_FOR_EACH_FUNC(GNOME_KEYRING_DEFINE_WRAPPER)
|
| +#undef GNOME_KEYRING_DEFINE_WRAPPER
|
|
|
| // Make it easy to initialize the function pointers above with a loop below.
|
| #define GNOME_KEYRING_FUNCTION(name) \
|
| @@ -73,28 +74,22 @@
|
| // end up using the function pointers above instead.
|
| #define gnome_keyring_is_available \
|
| wrap_gnome_keyring_is_available
|
| -#define gnome_keyring_store_password_sync \
|
| - wrap_gnome_keyring_store_password_sync
|
| -#define gnome_keyring_delete_password_sync \
|
| - wrap_gnome_keyring_delete_password_sync
|
| -#define gnome_keyring_find_itemsv_sync \
|
| - wrap_gnome_keyring_find_itemsv_sync
|
| +#define gnome_keyring_store_password \
|
| + wrap_gnome_keyring_store_password
|
| +#define gnome_keyring_delete_password \
|
| + wrap_gnome_keyring_delete_password
|
| +#define gnome_keyring_find_itemsv \
|
| + wrap_gnome_keyring_find_itemsv
|
| #define gnome_keyring_result_to_message \
|
| wrap_gnome_keyring_result_to_message
|
| -#define gnome_keyring_found_list_free \
|
| - wrap_gnome_keyring_found_list_free
|
| -#define gnome_keyring_list_item_ids_sync \
|
| - wrap_gnome_keyring_list_item_ids_sync
|
| -#define gnome_keyring_item_get_attributes_sync \
|
| - wrap_gnome_keyring_item_get_attributes_sync
|
| -#define gnome_keyring_attribute_list_free \
|
| - wrap_gnome_keyring_attribute_list_free
|
| -#define gnome_keyring_item_get_info_sync \
|
| - wrap_gnome_keyring_item_get_info_sync
|
| +#define gnome_keyring_list_item_ids \
|
| + wrap_gnome_keyring_list_item_ids
|
| +#define gnome_keyring_item_get_attributes \
|
| + wrap_gnome_keyring_item_get_attributes
|
| +#define gnome_keyring_item_get_info \
|
| + wrap_gnome_keyring_item_get_info
|
| #define gnome_keyring_item_info_get_secret \
|
| wrap_gnome_keyring_item_info_get_secret
|
| -#define gnome_keyring_item_info_free \
|
| - wrap_gnome_keyring_item_info_free
|
|
|
| /* Load the library and initialize the function pointers. */
|
| bool LoadGnomeKeyring() {
|
| @@ -112,8 +107,8 @@
|
| dlsym(handle, gnome_keyring_functions[i].name);
|
| const char* error = dlerror();
|
| if (error) {
|
| - LOG(ERROR) << "Unable to load symbol " <<
|
| - gnome_keyring_functions[i].name << ": " << error;
|
| + LOG(ERROR) << "Unable to load symbol "
|
| + << gnome_keyring_functions[i].name << ": " << error;
|
| dlclose(handle);
|
| return false;
|
| }
|
| @@ -122,20 +117,18 @@
|
| return true;
|
| }
|
|
|
| -// Older versions of GNOME Keyring have bugs that prevent them from
|
| -// working correctly with the find_itemsv API. (In particular, the
|
| -// non-pageable memory allocator is rather busted.) There is no
|
| -// official way to check the version, nor could we figure out any
|
| -// reasonable unofficial way to do it. So we work around it by using
|
| -// a much slower API.
|
| -#define GNOME_KEYRING_WORKAROUND_MEMORY_CORRUPTION
|
| +// Older versions of GNOME Keyring have bugs that prevent them from working
|
| +// correctly with the find_itemsv API. (In particular, the non-pageable memory
|
| +// allocator is rather busted.) There is no official way to check the version,
|
| +// nor could we figure out any reasonable unofficial way to do it. So we work
|
| +// around it by using a much slower API.
|
| +#define GNOME_KEYRING_WORK_AROUND_MEMORY_CORRUPTION
|
|
|
| #else // !defined(DLOPEN_GNOME_KEYRING)
|
|
|
| bool LoadGnomeKeyring() {
|
| - // We don't need to do anything here. When linking directly, we
|
| - // assume that whoever is compiling this code has checked that the
|
| - // version is OK.
|
| + // We don't need to do anything here. When linking directly, we also assume
|
| + // that whoever is compiling this code has checked that the version is OK.
|
| return true;
|
| }
|
|
|
| @@ -143,27 +136,23 @@
|
|
|
| #define GNOME_KEYRING_APPLICATION_CHROME "chrome"
|
|
|
| -// Convert the attributes of a given keyring entry into a new
|
| -// PasswordForm. Note: does *not* get the actual password, as that is
|
| -// not a key attribute! Returns NULL if the attributes are for the
|
| -// wrong application.
|
| +// Convert the attributes of a given keyring entry into a new PasswordForm.
|
| +// Note: does *not* get the actual password, as that is not a key attribute!
|
| +// Returns NULL if the attributes are for the wrong application.
|
| PasswordForm* FormFromAttributes(GnomeKeyringAttributeList* attrs) {
|
| // Read the string and int attributes into the appropriate map.
|
| std::map<std::string, std::string> string_attr_map;
|
| std::map<std::string, uint32_t> uint_attr_map;
|
| for (guint i = 0; i < attrs->len; ++i) {
|
| GnomeKeyringAttribute attr = gnome_keyring_attribute_list_index(attrs, i);
|
| - if (attr.type == GNOME_KEYRING_ATTRIBUTE_TYPE_STRING) {
|
| - if (std::string(attr.name) == "application" &&
|
| - std::string(attr.value.string) != GNOME_KEYRING_APPLICATION_CHROME) {
|
| - // This is not a password we care about.
|
| - return NULL;
|
| - }
|
| + if (attr.type == GNOME_KEYRING_ATTRIBUTE_TYPE_STRING)
|
| string_attr_map[attr.name] = attr.value.string;
|
| - } else if (attr.type == GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32) {
|
| + else if (attr.type == GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32)
|
| uint_attr_map[attr.name] = attr.value.integer;
|
| - }
|
| }
|
| + // Check to make sure this is a password we care about.
|
| + if (string_attr_map["application"] != GNOME_KEYRING_APPLICATION_CHROME)
|
| + return NULL;
|
|
|
| PasswordForm* form = new PasswordForm();
|
| form->origin = GURL(string_attr_map["origin_url"]);
|
| @@ -187,10 +176,30 @@
|
| return form;
|
| }
|
|
|
| -} // namespace
|
| +// Parse all the results from the given GList into a PasswordFormList, and free
|
| +// the GList. PasswordForms are allocated on the heap, and should be deleted by
|
| +// the consumer.
|
| +void ConvertFormList(GList* found,
|
| + NativeBackendGnome::PasswordFormList* forms) {
|
| + GList* element = g_list_first(found);
|
| + while (element != NULL) {
|
| + GnomeKeyringFound* data = static_cast<GnomeKeyringFound*>(element->data);
|
| + GnomeKeyringAttributeList* attrs = data->attributes;
|
|
|
| + PasswordForm* form = FormFromAttributes(attrs);
|
| + if (form) {
|
| + form->password_value = UTF8ToUTF16(data->secret);
|
| + forms->push_back(form);
|
| + } else {
|
| + LOG(WARNING) << "Could not initialize PasswordForm from attributes!";
|
| + }
|
| +
|
| + element = g_list_next(element);
|
| + }
|
| +}
|
| +
|
| // Schema is analagous to the fields in PasswordForm.
|
| -const GnomeKeyringPasswordSchema NativeBackendGnome::kGnomeSchema = {
|
| +const GnomeKeyringPasswordSchema kGnomeSchema = {
|
| GNOME_KEYRING_ITEM_GENERIC_SECRET, {
|
| { "origin_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
|
| { "action_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING },
|
| @@ -210,23 +219,99 @@
|
| }
|
| };
|
|
|
| -NativeBackendGnome::NativeBackendGnome() {
|
| -}
|
| +// Sadly, PasswordStore goes to great lengths to switch from the originally
|
| +// calling thread to the DB thread, and to provide an asynchronous API to
|
| +// callers while using a synchronous (virtual) API provided by subclasses like
|
| +// PasswordStoreX -- but GNOME Keyring really wants to be on the GLib main
|
| +// thread, which is the UI thread to us. So we end up having to switch threads
|
| +// again, possibly back to the very same thread (in case the UI thread is the
|
| +// caller, e.g. in the password management UI), and *block* the DB thread
|
| +// waiting for a response from the UI thread to provide the synchronous API
|
| +// PasswordStore expects of us. (It will then in turn switch back to the
|
| +// original caller to send the asynchronous reply to the original request.)
|
|
|
| -NativeBackendGnome::~NativeBackendGnome() {
|
| -}
|
| +// This class represents a call to a GNOME Keyring method. A RunnableMethod
|
| +// should be posted to the UI thread to call one of its action methods, and then
|
| +// a WaitResult() method should be called to wait for the result. Each instance
|
| +// supports only one outstanding method at a time, though multiple instances may
|
| +// be used in parallel.
|
| +class GKRMethod {
|
| + public:
|
| + typedef NativeBackendGnome::PasswordFormList PasswordFormList;
|
|
|
| -bool NativeBackendGnome::Init() {
|
| - return LoadGnomeKeyring() && gnome_keyring_is_available();
|
| -}
|
| + GKRMethod() : event_(false, false), result_(GNOME_KEYRING_RESULT_CANCELLED) {}
|
|
|
| -bool NativeBackendGnome::AddLogin(const PasswordForm& form) {
|
| - DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB));
|
| - GnomeKeyringResult result = gnome_keyring_store_password_sync(
|
| + // Action methods. These call gnome_keyring_* functions. Call from UI thread.
|
| + void AddLogin(const PasswordForm& form);
|
| + void UpdateLoginSearch(const PasswordForm& form);
|
| + void RemoveLogin(const PasswordForm& form);
|
| + void GetLogins(const PasswordForm& form);
|
| +#if !defined(GNOME_KEYRING_WORK_AROUND_MEMORY_CORRUPTION)
|
| + void GetLoginsList(uint32_t blacklisted_by_user);
|
| + void GetAllLogins();
|
| +#else
|
| + void GetItemIds();
|
| + void GetItemAttrs(guint id);
|
| + void GetItemInfo(guint id);
|
| +#endif
|
| +
|
| + // Use after AddLogin, RemoveLogin.
|
| + GnomeKeyringResult WaitResult();
|
| +
|
| + // Use after UpdateLoginSearch, GetLogins, GetLoginsList, GetAllLogins.
|
| + GnomeKeyringResult WaitResult(PasswordFormList* forms);
|
| +
|
| +#if defined(GNOME_KEYRING_WORK_AROUND_MEMORY_CORRUPTION)
|
| + // Use after GetItemIds().
|
| + GnomeKeyringResult WaitResult(std::vector<guint>* item_ids);
|
| +
|
| + // Use after GetItemAttrs().
|
| + GnomeKeyringResult WaitResult(PasswordForm** form);
|
| +
|
| + // Use after GetItemInfo().
|
| + GnomeKeyringResult WaitResult(string16* password);
|
| +#endif
|
| +
|
| + private:
|
| + // All these callbacks are called on UI thread.
|
| + static void OnOperationDone(GnomeKeyringResult result, gpointer data);
|
| +
|
| + static void OnOperationGetList(GnomeKeyringResult result, GList* list,
|
| + gpointer data);
|
| +
|
| +#if defined(GNOME_KEYRING_WORK_AROUND_MEMORY_CORRUPTION)
|
| + static void OnOperationGetIds(GnomeKeyringResult result, GList* list,
|
| + gpointer data);
|
| +
|
| + static void OnOperationGetAttrs(GnomeKeyringResult result,
|
| + GnomeKeyringAttributeList* attrs,
|
| + gpointer data);
|
| +
|
| + static void OnOperationGetInfo(GnomeKeyringResult result,
|
| + GnomeKeyringItemInfo* info,
|
| + gpointer data);
|
| +#endif
|
| +
|
| + base::WaitableEvent event_;
|
| + GnomeKeyringResult result_;
|
| + NativeBackendGnome::PasswordFormList forms_;
|
| +#if defined(GNOME_KEYRING_WORK_AROUND_MEMORY_CORRUPTION)
|
| + std::vector<guint> item_ids_;
|
| + scoped_ptr<PasswordForm> form_;
|
| + string16 password_;
|
| +#endif
|
| +};
|
| +
|
| +void GKRMethod::AddLogin(const PasswordForm& form) {
|
| + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
|
| + gnome_keyring_store_password(
|
| &kGnomeSchema,
|
| NULL, // Default keyring.
|
| form.origin.spec().c_str(), // Display name.
|
| UTF16ToUTF8(form.password_value).c_str(),
|
| + OnOperationDone,
|
| + this, // data
|
| + NULL, // destroy_data
|
| "origin_url", form.origin.spec().c_str(),
|
| "action_url", form.action.spec().c_str(),
|
| "username_element", UTF16ToUTF8(form.username_element).c_str(),
|
| @@ -241,7 +326,237 @@
|
| "scheme", form.scheme,
|
| "application", GNOME_KEYRING_APPLICATION_CHROME,
|
| NULL);
|
| +}
|
|
|
| +void GKRMethod::UpdateLoginSearch(const PasswordForm& form) {
|
| + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
|
| + // Search GNOME Keyring for matching passwords to update.
|
| + gnome_keyring_find_itemsv(
|
| + GNOME_KEYRING_ITEM_GENERIC_SECRET,
|
| + OnOperationGetList,
|
| + this, // data
|
| + NULL, // destroy_data
|
| + "origin_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
|
| + form.origin.spec().c_str(),
|
| + "username_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
|
| + UTF16ToUTF8(form.username_element).c_str(),
|
| + "username_value", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
|
| + UTF16ToUTF8(form.username_value).c_str(),
|
| + "password_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
|
| + UTF16ToUTF8(form.password_element).c_str(),
|
| + "signon_realm", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
|
| + form.signon_realm.c_str(),
|
| + "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
|
| + GNOME_KEYRING_APPLICATION_CHROME,
|
| + NULL);
|
| +}
|
| +
|
| +void GKRMethod::RemoveLogin(const PasswordForm& form) {
|
| + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
|
| + // We find forms using the same fields as LoginDatabase::RemoveLogin().
|
| + gnome_keyring_delete_password(
|
| + &kGnomeSchema,
|
| + OnOperationDone,
|
| + this, // data
|
| + NULL, // destroy_data
|
| + "origin_url", form.origin.spec().c_str(),
|
| + "action_url", form.action.spec().c_str(),
|
| + "username_element", UTF16ToUTF8(form.username_element).c_str(),
|
| + "username_value", UTF16ToUTF8(form.username_value).c_str(),
|
| + "password_element", UTF16ToUTF8(form.password_element).c_str(),
|
| + "submit_element", UTF16ToUTF8(form.submit_element).c_str(),
|
| + "signon_realm", form.signon_realm.c_str(),
|
| + NULL);
|
| +}
|
| +
|
| +void GKRMethod::GetLogins(const PasswordForm& form) {
|
| + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
|
| + // Search GNOME Keyring for matching passwords.
|
| + gnome_keyring_find_itemsv(
|
| + GNOME_KEYRING_ITEM_GENERIC_SECRET,
|
| + OnOperationGetList,
|
| + this, // data
|
| + NULL, // destroy_data
|
| + "signon_realm", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
|
| + form.signon_realm.c_str(),
|
| + "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
|
| + GNOME_KEYRING_APPLICATION_CHROME,
|
| + NULL);
|
| +}
|
| +
|
| +#if !defined(GNOME_KEYRING_WORK_AROUND_MEMORY_CORRUPTION)
|
| +void GKRMethod::GetLoginsList(uint32_t blacklisted_by_user) {
|
| + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
|
| + // Search GNOME Keyring for matching passwords.
|
| + gnome_keyring_find_itemsv(
|
| + GNOME_KEYRING_ITEM_GENERIC_SECRET,
|
| + OnOperationGetList,
|
| + this, // data
|
| + NULL, // destroy_data
|
| + "blacklisted_by_user", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32,
|
| + blacklisted_by_user,
|
| + "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
|
| + GNOME_KEYRING_APPLICATION_CHROME,
|
| + NULL);
|
| +}
|
| +
|
| +void GKRMethod::GetAllLogins() {
|
| + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
|
| + // We need to search for something, otherwise we get no results - so
|
| + // we search for the fixed application string.
|
| + gnome_keyring_find_itemsv(
|
| + GNOME_KEYRING_ITEM_GENERIC_SECRET,
|
| + OnOperationGetList,
|
| + this, // data
|
| + NULL, // destroy_data
|
| + "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
|
| + GNOME_KEYRING_APPLICATION_CHROME,
|
| + NULL);
|
| +}
|
| +#else
|
| +void GKRMethod::GetItemIds() {
|
| + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
|
| + gnome_keyring_list_item_ids(NULL, OnOperationGetIds, this, NULL);
|
| +}
|
| +
|
| +void GKRMethod::GetItemAttrs(guint id) {
|
| + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
|
| + gnome_keyring_item_get_attributes(NULL, id, OnOperationGetAttrs, this, NULL);
|
| +}
|
| +
|
| +void GKRMethod::GetItemInfo(guint id) {
|
| + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
|
| + gnome_keyring_item_get_info(NULL, id, OnOperationGetInfo, this, NULL);
|
| +}
|
| +#endif
|
| +
|
| +GnomeKeyringResult GKRMethod::WaitResult() {
|
| + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB));
|
| + event_.Wait();
|
| + return result_;
|
| +}
|
| +
|
| +GnomeKeyringResult GKRMethod::WaitResult(PasswordFormList* forms) {
|
| + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB));
|
| + event_.Wait();
|
| + forms->swap(forms_);
|
| + return result_;
|
| +}
|
| +
|
| +#if defined(GNOME_KEYRING_WORK_AROUND_MEMORY_CORRUPTION)
|
| +GnomeKeyringResult GKRMethod::WaitResult(std::vector<guint>* item_ids) {
|
| + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB));
|
| + event_.Wait();
|
| + item_ids->swap(item_ids_);
|
| + return result_;
|
| +}
|
| +
|
| +GnomeKeyringResult GKRMethod::WaitResult(PasswordForm** form) {
|
| + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB));
|
| + event_.Wait();
|
| + *form = form_.release();
|
| + return result_;
|
| +}
|
| +
|
| +GnomeKeyringResult GKRMethod::WaitResult(string16* password) {
|
| + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB));
|
| + event_.Wait();
|
| + *password = password_;
|
| + return result_;
|
| +}
|
| +#endif
|
| +
|
| +// static
|
| +void GKRMethod::OnOperationDone(GnomeKeyringResult result, gpointer data) {
|
| + GKRMethod* method = static_cast<GKRMethod*>(data);
|
| + method->result_ = result;
|
| + method->event_.Signal();
|
| +}
|
| +
|
| +// static
|
| +void GKRMethod::OnOperationGetList(GnomeKeyringResult result, GList* list,
|
| + gpointer data) {
|
| + GKRMethod* method = static_cast<GKRMethod*>(data);
|
| + method->result_ = result;
|
| + method->forms_.clear();
|
| + // |list| will be freed after this callback returns, so convert it now.
|
| + ConvertFormList(list, &method->forms_);
|
| + method->event_.Signal();
|
| +}
|
| +
|
| +#if defined(GNOME_KEYRING_WORK_AROUND_MEMORY_CORRUPTION)
|
| +// static
|
| +void GKRMethod::OnOperationGetIds(GnomeKeyringResult result, GList* list,
|
| + gpointer data) {
|
| + GKRMethod* method = static_cast<GKRMethod*>(data);
|
| + method->result_ = result;
|
| + method->item_ids_.clear();
|
| + // |list| will be freed after this callback returns, so save it now.
|
| + for (GList* i = list; i; i = i->next) {
|
| + guint id = GPOINTER_TO_UINT(i->data);
|
| + method->item_ids_.push_back(id);
|
| + }
|
| + method->event_.Signal();
|
| +}
|
| +
|
| +// static
|
| +void GKRMethod::OnOperationGetAttrs(GnomeKeyringResult result,
|
| + GnomeKeyringAttributeList* attrs,
|
| + gpointer data) {
|
| + GKRMethod* method = static_cast<GKRMethod*>(data);
|
| + method->result_ = result;
|
| + // |attrs| will be freed after this callback returns, so convert it now.
|
| + if (result == GNOME_KEYRING_RESULT_OK)
|
| + method->form_.reset(FormFromAttributes(attrs));
|
| + method->event_.Signal();
|
| +}
|
| +
|
| +// static
|
| +void GKRMethod::OnOperationGetInfo(GnomeKeyringResult result,
|
| + GnomeKeyringItemInfo* info,
|
| + gpointer data) {
|
| + GKRMethod* method = static_cast<GKRMethod*>(data);
|
| + method->result_ = result;
|
| + // |info| will be freed after this callback returns, so use it now.
|
| + if (result == GNOME_KEYRING_RESULT_OK) {
|
| + char* secret = gnome_keyring_item_info_get_secret(info);
|
| + method->password_ = UTF8ToUTF16(secret);
|
| + // gnome_keyring_item_info_get_secret() allocates and returns a new copy
|
| + // of the secret, so we have to free it afterward.
|
| + free(secret);
|
| + }
|
| + method->event_.Signal();
|
| +}
|
| +#endif
|
| +
|
| +} // namespace
|
| +
|
| +// GKRMethod isn't reference counted, but it always outlasts runnable
|
| +// methods against it because the caller waits for those methods to run.
|
| +template<>
|
| +struct RunnableMethodTraits<GKRMethod> {
|
| + void RetainCallee(GKRMethod*) {}
|
| + void ReleaseCallee(GKRMethod*) {}
|
| +};
|
| +
|
| +NativeBackendGnome::NativeBackendGnome() {
|
| +}
|
| +
|
| +NativeBackendGnome::~NativeBackendGnome() {
|
| +}
|
| +
|
| +bool NativeBackendGnome::Init() {
|
| + return LoadGnomeKeyring() && gnome_keyring_is_available();
|
| +}
|
| +
|
| +bool NativeBackendGnome::AddLogin(const PasswordForm& form) {
|
| + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB));
|
| + GKRMethod method;
|
| + ChromeThread::PostTask(ChromeThread::UI, FROM_HERE,
|
| + NewRunnableMethod(&method,
|
| + &GKRMethod::AddLogin,
|
| + form));
|
| + GnomeKeyringResult result = method.WaitResult();
|
| if (result != GNOME_KEYRING_RESULT_OK) {
|
| LOG(ERROR) << "Keyring save failed: "
|
| << gnome_keyring_result_to_message(result);
|
| @@ -258,32 +573,19 @@
|
| // fields, then we add a new login with those fields updated and only delete
|
| // the original on success.
|
| DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB));
|
| - GList* found = NULL;
|
| - // Search gnome keyring for matching passwords.
|
| - GnomeKeyringResult result = gnome_keyring_find_itemsv_sync(
|
| - GNOME_KEYRING_ITEM_GENERIC_SECRET,
|
| - &found,
|
| - "origin_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
|
| - form.origin.spec().c_str(),
|
| - "username_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
|
| - UTF16ToUTF8(form.username_element).c_str(),
|
| - "username_value", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
|
| - UTF16ToUTF8(form.username_value).c_str(),
|
| - "password_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
|
| - UTF16ToUTF8(form.password_element).c_str(),
|
| - "signon_realm", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
|
| - form.signon_realm.c_str(),
|
| - "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
|
| - GNOME_KEYRING_APPLICATION_CHROME,
|
| - NULL);
|
| + GKRMethod method;
|
| + ChromeThread::PostTask(ChromeThread::UI, FROM_HERE,
|
| + NewRunnableMethod(&method,
|
| + &GKRMethod::UpdateLoginSearch,
|
| + form));
|
| + PasswordFormList forms;
|
| + GnomeKeyringResult result = method.WaitResult(&forms);
|
| if (result != GNOME_KEYRING_RESULT_OK) {
|
| LOG(ERROR) << "Keyring find failed: "
|
| << gnome_keyring_result_to_message(result);
|
| return false;
|
| }
|
| bool ok = true;
|
| - PasswordFormList forms;
|
| - ConvertFormList(found, &forms);
|
| for (size_t i = 0; i < forms.size(); ++i) {
|
| if (forms[i]->action != form.action ||
|
| forms[i]->password_value != form.password_value ||
|
| @@ -306,17 +608,12 @@
|
|
|
| bool NativeBackendGnome::RemoveLogin(const PasswordForm& form) {
|
| DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB));
|
| - // We find forms using the same fields as LoginDatabase::RemoveLogin().
|
| - GnomeKeyringResult result = gnome_keyring_delete_password_sync(
|
| - &kGnomeSchema,
|
| - "origin_url", form.origin.spec().c_str(),
|
| - "action_url", form.action.spec().c_str(),
|
| - "username_element", UTF16ToUTF8(form.username_element).c_str(),
|
| - "username_value", UTF16ToUTF8(form.username_value).c_str(),
|
| - "password_element", UTF16ToUTF8(form.password_element).c_str(),
|
| - "submit_element", UTF16ToUTF8(form.submit_element).c_str(),
|
| - "signon_realm", form.signon_realm.c_str(),
|
| - NULL);
|
| + GKRMethod method;
|
| + ChromeThread::PostTask(ChromeThread::UI, FROM_HERE,
|
| + NewRunnableMethod(&method,
|
| + &GKRMethod::RemoveLogin,
|
| + form));
|
| + GnomeKeyringResult result = method.WaitResult();
|
| if (result != GNOME_KEYRING_RESULT_OK) {
|
| LOG(ERROR) << "Keyring delete failed: "
|
| << gnome_keyring_result_to_message(result);
|
| @@ -350,16 +647,12 @@
|
| bool NativeBackendGnome::GetLogins(const PasswordForm& form,
|
| PasswordFormList* forms) {
|
| DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB));
|
| - GList* found = NULL;
|
| - // Search gnome keyring for matching passwords.
|
| - GnomeKeyringResult result = gnome_keyring_find_itemsv_sync(
|
| - GNOME_KEYRING_ITEM_GENERIC_SECRET,
|
| - &found,
|
| - "signon_realm", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
|
| - form.signon_realm.c_str(),
|
| - "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
|
| - GNOME_KEYRING_APPLICATION_CHROME,
|
| - NULL);
|
| + GKRMethod method;
|
| + ChromeThread::PostTask(ChromeThread::UI, FROM_HERE,
|
| + NewRunnableMethod(&method,
|
| + &GKRMethod::GetLogins,
|
| + form));
|
| + GnomeKeyringResult result = method.WaitResult(forms);
|
| if (result == GNOME_KEYRING_RESULT_NO_MATCH)
|
| return true;
|
| if (result != GNOME_KEYRING_RESULT_OK) {
|
| @@ -367,7 +660,6 @@
|
| << gnome_keyring_result_to_message(result);
|
| return false;
|
| }
|
| - ConvertFormList(found, forms);
|
| return true;
|
| }
|
|
|
| @@ -408,17 +700,13 @@
|
|
|
| uint32_t blacklisted_by_user = !autofillable;
|
|
|
| -#if !defined(GNOME_KEYRING_WORKAROUND_MEMORY_CORRUPTION)
|
| - GList* found = NULL;
|
| - // Search gnome keyring for matching passwords.
|
| - GnomeKeyringResult result = gnome_keyring_find_itemsv_sync(
|
| - GNOME_KEYRING_ITEM_GENERIC_SECRET,
|
| - &found,
|
| - "blacklisted_by_user", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32,
|
| - blacklisted_by_user,
|
| - "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
|
| - GNOME_KEYRING_APPLICATION_CHROME,
|
| - NULL);
|
| +#if !defined(GNOME_KEYRING_WORK_AROUND_MEMORY_CORRUPTION)
|
| + GKRMethod method;
|
| + ChromeThread::PostTask(ChromeThread::UI, FROM_HERE,
|
| + NewRunnableMethod(&method,
|
| + &GKRMethod::GetLoginsList,
|
| + blacklisted_by_user));
|
| + GnomeKeyringResult result = method.WaitResult(forms);
|
| if (result == GNOME_KEYRING_RESULT_NO_MATCH)
|
| return true;
|
| if (result != GNOME_KEYRING_RESULT_OK) {
|
| @@ -426,13 +714,12 @@
|
| << gnome_keyring_result_to_message(result);
|
| return false;
|
| }
|
| - ConvertFormList(found, forms);
|
| return true;
|
| #else
|
| PasswordFormList all_forms;
|
| if (!GetAllLogins(&all_forms))
|
| return false;
|
| - // Now manually filter the result for the values we care about.
|
| + // Now manually filter the results for the values we care about.
|
| for (size_t i = 0; i < all_forms.size(); ++i) {
|
| if (all_forms[i]->blacklisted_by_user == blacklisted_by_user)
|
| forms->push_back(all_forms[i]);
|
| @@ -444,23 +731,12 @@
|
| }
|
|
|
| bool NativeBackendGnome::GetAllLogins(PasswordFormList* forms) {
|
| - // Older versions of GNOME Keyring have bugs that prevent them from
|
| - // working correctly with the find_itemsv API. (In particular, the
|
| - // non-pageable memory allocator is rather busted.) There is no
|
| - // official way to check the version, nor could we figure out any
|
| - // reasonable unofficial way to do it. So we work around it by
|
| - // using a much slower API.
|
| -
|
| -#if !defined(GNOME_KEYRING_WORKAROUND_MEMORY_CORRUPTION)
|
| - GList* found = NULL;
|
| - // We need to search for something, otherwise we get no results - so
|
| - // we search for the fixed application string.
|
| - GnomeKeyringResult result = gnome_keyring_find_itemsv_sync(
|
| - GNOME_KEYRING_ITEM_GENERIC_SECRET,
|
| - &found,
|
| - "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
|
| - GNOME_KEYRING_APPLICATION_CHROME,
|
| - NULL);
|
| + GKRMethod method;
|
| +#if !defined(GNOME_KEYRING_WORK_AROUND_MEMORY_CORRUPTION)
|
| + ChromeThread::PostTask(ChromeThread::UI, FROM_HERE,
|
| + NewRunnableMethod(&method,
|
| + &GKRMethod::GetAllLogins));
|
| + GnomeKeyringResult result = method.WaitResult(forms);
|
| if (result == GNOME_KEYRING_RESULT_NO_MATCH)
|
| return true;
|
| if (result != GNOME_KEYRING_RESULT_OK) {
|
| @@ -468,63 +744,80 @@
|
| << gnome_keyring_result_to_message(result);
|
| return false;
|
| }
|
| - ConvertFormList(found, forms);
|
| return true;
|
| #else
|
| - GList* ids = NULL;
|
| - GnomeKeyringResult result = gnome_keyring_list_item_ids_sync(NULL, &ids);
|
| + ChromeThread::PostTask(ChromeThread::UI, FROM_HERE,
|
| + NewRunnableMethod(&method,
|
| + &GKRMethod::GetItemIds));
|
| + std::vector<guint> item_ids;
|
| + GnomeKeyringResult result = method.WaitResult(&item_ids);
|
| if (result != GNOME_KEYRING_RESULT_OK) {
|
| LOG(ERROR) << "Keyring itemid list failed: "
|
| << gnome_keyring_result_to_message(result);
|
| return false;
|
| }
|
|
|
| - for (GList* i = ids; i; i = i->next) {
|
| - int id = GPOINTER_TO_INT(i->data);
|
| - GnomeKeyringAttributeList* attrs = NULL;
|
| - result = gnome_keyring_item_get_attributes_sync(NULL, id, &attrs);
|
| + // We can parallelize getting the item attributes.
|
| + GKRMethod* methods = new GKRMethod[item_ids.size()];
|
| + for (size_t i = 0; i < item_ids.size(); ++i) {
|
| + ChromeThread::PostTask(ChromeThread::UI, FROM_HERE,
|
| + NewRunnableMethod(&methods[i],
|
| + &GKRMethod::GetItemAttrs,
|
| + item_ids[i]));
|
| + }
|
| +
|
| + bool success = true;
|
| +
|
| + // We can also parallelize getting the item info (i.e. passwords).
|
| + PasswordFormList all_forms;
|
| + all_forms.resize(item_ids.size());
|
| + for (size_t i = 0; i < item_ids.size(); ++i) {
|
| + result = methods[i].WaitResult(&all_forms[i]);
|
| if (result != GNOME_KEYRING_RESULT_OK) {
|
| LOG(ERROR) << "Keyring get item attributes failed:"
|
| << gnome_keyring_result_to_message(result);
|
| - gnome_keyring_attribute_list_free(attrs);
|
| - break;
|
| + // We explicitly do not break out here. We must wait on all the other
|
| + // methods first, and we may have already posted new methods. So, we just
|
| + // note the failure and continue.
|
| + success = false;
|
| }
|
| + if (all_forms[i]) {
|
| + ChromeThread::PostTask(ChromeThread::UI, FROM_HERE,
|
| + NewRunnableMethod(&methods[i],
|
| + &GKRMethod::GetItemInfo,
|
| + item_ids[i]));
|
| + }
|
| + }
|
|
|
| - PasswordForm* form = FormFromAttributes(attrs);
|
| - gnome_keyring_attribute_list_free(attrs);
|
| + // Now just wait for all the passwords to come in.
|
| + for (size_t i = 0; i < item_ids.size(); ++i) {
|
| + if (!all_forms[i])
|
| + continue;
|
| + result = methods[i].WaitResult(&all_forms[i]->password_value);
|
| + if (result != GNOME_KEYRING_RESULT_OK) {
|
| + LOG(ERROR) << "Keyring get item info failed:"
|
| + << gnome_keyring_result_to_message(result);
|
| + delete all_forms[i];
|
| + all_forms[i] = NULL;
|
| + // We explicitly do not break out here (see above).
|
| + success = false;
|
| + }
|
| + }
|
|
|
| - if (form) {
|
| - GnomeKeyringItemInfo* info = NULL;
|
| - result = gnome_keyring_item_get_info_sync(NULL, id, &info);
|
| - if (result != GNOME_KEYRING_RESULT_OK) {
|
| - delete form;
|
| - break;
|
| - }
|
| - form->password_value = UTF8ToUTF16(
|
| - gnome_keyring_item_info_get_secret(info));
|
| + delete[] methods;
|
|
|
| - gnome_keyring_item_info_free(info);
|
| - forms->push_back(form);
|
| + if (success) {
|
| + // If we succeeded, output all the forms.
|
| + for (size_t i = 0; i < item_ids.size(); ++i) {
|
| + if (all_forms[i])
|
| + forms->push_back(all_forms[i]);
|
| }
|
| + } else {
|
| + // Otherwise, free them.
|
| + for (size_t i = 0; i < item_ids.size(); ++i)
|
| + delete all_forms[i];
|
| }
|
| - g_list_free(ids);
|
|
|
| - return result == GNOME_KEYRING_RESULT_OK;
|
| + return success;
|
| #endif
|
| }
|
| -
|
| -void NativeBackendGnome::ConvertFormList(GList* found,
|
| - PasswordFormList* forms) {
|
| - GList* element = g_list_first(found);
|
| - while (element != NULL) {
|
| - GnomeKeyringFound* data = static_cast<GnomeKeyringFound*>(element->data);
|
| - GnomeKeyringAttributeList* attrs = data->attributes;
|
| -
|
| - PasswordForm* form = FormFromAttributes(attrs);
|
| - form->password_value = UTF8ToUTF16(data->secret);
|
| - forms->push_back(form);
|
| -
|
| - element = g_list_next(element);
|
| - }
|
| - gnome_keyring_found_list_free(found);
|
| -}
|
|
|