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