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. */ 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. 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. 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 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 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 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 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 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 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) 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 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 464 void GKRMethod::GetKeyrings() { 465 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 466 gnome_keyring_list_keyring_names(OnOperationGetKeyrings, this, NULL); 467 } 468 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 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 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 486 GnomeKeyringResult GKRMethod::WaitResult() { 487 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); 488 event_.Wait(); 489 return result_; 490 } 491 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) 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 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 514 GnomeKeyringResult GKRMethod::WaitResult(PasswordForm** form) { 515 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); 516 event_.Wait(); 517 *form = form_.release(); 518 return result_; 519 } 520 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 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 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 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 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 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 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> { 617 void RetainCallee(GKRMethod*) {} 618 void ReleaseCallee(GKRMethod*) {} 619 }; 620 621 NativeBackendGnome::NativeBackendGnome() { 622 } 623 624 NativeBackendGnome::~NativeBackendGnome() { 625 } 626 627 bool NativeBackendGnome::Init() { 628 return LoadGnomeKeyring() && gnome_keyring_is_available(); 629 } 630 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 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 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 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 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 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 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 806 bool NativeBackendGnome::GetAutofillableLogins(PasswordFormList* forms) { 807 return GetLoginsList(forms, true); 808 } 809 810 bool NativeBackendGnome::GetBlacklistLogins(PasswordFormList* forms) { 811 return GetLoginsList(forms, false); 812 } 813 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 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