Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1237)

Side by Side Diff: chrome/browser/password_manager/native_backend_gnome_x.cc

Issue 2953006: Linux: access GNOME Keyring on the GLib main thread, rather than the DB thread. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: '' Created 10 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « chrome/browser/password_manager/native_backend_gnome_x.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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 }
OLDNEW
« no previous file with comments | « chrome/browser/password_manager/native_backend_gnome_x.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698