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