1 // Copyright (c) 2012 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 <dlfcn.h> 8 #include <gnome-keyring.h> 9 10 #include <map> 11 #include <string> 12 #include <vector> 13 14 #include "base/basictypes.h" 15 #include "base/logging.h" 16 #include "base/memory/scoped_ptr.h" 17 #include "base/metrics/histogram.h" 18 #include "base/strings/string_number_conversions.h" 19 #include "base/strings/string_piece.h" 20 #include "base/strings/string_util.h" 21 #include "base/strings/stringprintf.h" 22 #include "base/strings/utf_string_conversions.h" 23 #include "base/synchronization/waitable_event.h" 24 #include "base/time/time.h" 25 #include "components/autofill/core/common/password_form.h" 26 #include "components/password_manager/core/browser/psl_matching_helper.h" 27 #include "content/public/browser/browser_thread.h" 28 29 using autofill::PasswordForm; 30 using base::UTF8ToUTF16; 31 using base::UTF16ToUTF8; 32 using content::BrowserThread; 33 using password_manager::PSLMatchingHelper; 34 35 #define GNOME_KEYRING_DEFINE_POINTER(name) \ 36 typeof(&::gnome_keyring_##name) GnomeKeyringLoader::gnome_keyring_##name; 37 GNOME_KEYRING_FOR_EACH_FUNC(GNOME_KEYRING_DEFINE_POINTER) 38 #undef GNOME_KEYRING_DEFINE_POINTER 39 40 bool GnomeKeyringLoader::keyring_loaded = false; 41 42 #if defined(DLOPEN_GNOME_KEYRING) 43 44 #define GNOME_KEYRING_FUNCTION_INFO(name) \ 45 {"gnome_keyring_"#name, reinterpret_cast<void**>(&gnome_keyring_##name)}, 46 const GnomeKeyringLoader::FunctionInfo GnomeKeyringLoader::functions[] = { 47 GNOME_KEYRING_FOR_EACH_FUNC(GNOME_KEYRING_FUNCTION_INFO) 48 {NULL, NULL} 49 }; 50 #undef GNOME_KEYRING_FUNCTION_INFO 51 52 /* Load the library and initialize the function pointers. */ 53 bool GnomeKeyringLoader::LoadGnomeKeyring() { 54 if (keyring_loaded) 55 return true; 56 57 void* handle = dlopen("libgnome-keyring.so.0", RTLD_NOW | RTLD_GLOBAL); 58 if (!handle) { 59 // We wanted to use GNOME Keyring, but we couldn't load it. Warn, because 60 // either the user asked for this, or we autodetected it incorrectly. (Or 61 // the system has broken libraries, which is also good to warn about.) 62 LOG(WARNING) << "Could not load libgnome-keyring.so.0: " << dlerror(); 63 return false; 64 } 65 66 for (size_t i = 0; functions[i].name; ++i) { 67 dlerror(); 68 *functions[i].pointer = dlsym(handle, functions[i].name); 69 const char* error = dlerror(); 70 if (error) { 71 LOG(ERROR) << "Unable to load symbol " 72 << functions[i].name << ": " << error; 73 dlclose(handle); 74 return false; 75 } 76 } 77 78 keyring_loaded = true; 79 // We leak the library handle. That's OK: this function is called only once. 80 return true; 81 } 82 83 #else // defined(DLOPEN_GNOME_KEYRING) 84 85 bool GnomeKeyringLoader::LoadGnomeKeyring() { 86 if (keyring_loaded) 87 return true; 88 #define GNOME_KEYRING_ASSIGN_POINTER(name) \ 89 gnome_keyring_##name = &::gnome_keyring_##name; 90 GNOME_KEYRING_FOR_EACH_FUNC(GNOME_KEYRING_ASSIGN_POINTER) 91 #undef GNOME_KEYRING_ASSIGN_POINTER 92 keyring_loaded = true; 93 return true; 94 } 95 96 #endif // defined(DLOPEN_GNOME_KEYRING) 97 98 namespace { 99 100 const char kGnomeKeyringAppString[] = "chrome"; 101 102 // Convert the attributes of a given keyring entry into a new PasswordForm. 103 // Note: does *not* get the actual password, as that is not a key attribute! 104 // Returns NULL if the attributes are for the wrong application. 105 scoped_ptr<PasswordForm> FormFromAttributes(GnomeKeyringAttributeList* attrs) { 106 // Read the string and int attributes into the appropriate map. 107 std::map<std::string, std::string> string_attr_map; 108 std::map<std::string, uint32_t> uint_attr_map; 109 for (guint i = 0; i < attrs->len; ++i) { 110 GnomeKeyringAttribute attr = gnome_keyring_attribute_list_index(attrs, i); 111 if (attr.type == GNOME_KEYRING_ATTRIBUTE_TYPE_STRING) 112 string_attr_map[attr.name] = attr.value.string; 113 else if (attr.type == GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32) 114 uint_attr_map[attr.name] = attr.value.integer; 115 } 116 // Check to make sure this is a password we care about. 117 const std::string& app_value = string_attr_map["application"]; 118 if (!base::StringPiece(app_value).starts_with(kGnomeKeyringAppString)) 119 return scoped_ptr<PasswordForm>(); 120 121 scoped_ptr<PasswordForm> form(new PasswordForm()); 122 form->origin = GURL(string_attr_map["origin_url"]); 123 form->action = GURL(string_attr_map["action_url"]); 124 form->username_element = UTF8ToUTF16(string_attr_map["username_element"]); 125 form->username_value = UTF8ToUTF16(string_attr_map["username_value"]); 126 form->password_element = UTF8ToUTF16(string_attr_map["password_element"]); 127 form->submit_element = UTF8ToUTF16(string_attr_map["submit_element"]); 128 form->signon_realm = string_attr_map["signon_realm"]; 129 form->ssl_valid = uint_attr_map["ssl_valid"]; 130 form->preferred = uint_attr_map["preferred"]; 131 int64 date_created = 0; 132 bool date_ok = base::StringToInt64(string_attr_map["date_created"], 133 &date_created); 134 DCHECK(date_ok); 135 form->date_created = base::Time::FromTimeT(date_created); 136 form->blacklisted_by_user = uint_attr_map["blacklisted_by_user"]; 137 form->type = static_cast<PasswordForm::Type>(uint_attr_map["type"]); 138 form->times_used = uint_attr_map["times_used"]; 139 form->scheme = static_cast<PasswordForm::Scheme>(uint_attr_map["scheme"]); 140 int64 date_synced = 0; 141 base::StringToInt64(string_attr_map["date_synced"], &date_synced); 142 form->date_synced = base::Time::FromInternalValue(date_synced); 143 144 return form.Pass(); 145 } 146 147 // Parse all the results from the given GList into a PasswordFormList, and free 148 // the GList. PasswordForms are allocated on the heap, and should be deleted by 149 // the consumer. If not NULL, |lookup_form| is used to filter out results -- 150 // only credentials with signon realms passing the PSL matching (done by 151 // |helper|) against |lookup_form->signon_realm| will be kept. PSL matched 152 // results get their signon_realm, origin, and action rewritten to those of 153 // |lookup_form_|, with the original signon_realm saved into the result's 154 // original_signon_realm data member. 155 void ConvertFormList(GList* found, 156 const PasswordForm* lookup_form, 157 const PSLMatchingHelper& helper, 158 NativeBackendGnome::PasswordFormList* forms) { 159 PSLMatchingHelper::PSLDomainMatchMetric psl_domain_match_metric = 160 PSLMatchingHelper::PSL_DOMAIN_MATCH_NONE; 161 for (GList* element = g_list_first(found); element != NULL; 162 element = g_list_next(element)) { 163 GnomeKeyringFound* data = static_cast<GnomeKeyringFound*>(element->data); 164 GnomeKeyringAttributeList* attrs = data->attributes; 165 166 scoped_ptr<PasswordForm> form(FormFromAttributes(attrs)); 167 if (form) { 168 if (lookup_form && form->signon_realm != lookup_form->signon_realm) { 169 // This is not an exact match, we try PSL matching. 170 if (lookup_form->scheme != PasswordForm::SCHEME_HTML || 171 form->scheme != PasswordForm::SCHEME_HTML || 172 !(PSLMatchingHelper::IsPublicSuffixDomainMatch( 173 lookup_form->signon_realm, form->signon_realm))) { 174 continue; 175 } 176 psl_domain_match_metric = PSLMatchingHelper::PSL_DOMAIN_MATCH_FOUND; 177 form->original_signon_realm = form->signon_realm; 178 form->signon_realm = lookup_form->signon_realm; 179 form->origin = lookup_form->origin; 180 form->action = lookup_form->action; 181 } 182 if (data->secret) { 183 form->password_value = UTF8ToUTF16(data->secret); 184 } else { 185 LOG(WARNING) << "Unable to access password from list element!"; 186 } 187 forms->push_back(form.release()); 188 } else { 189 LOG(WARNING) << "Could not initialize PasswordForm from attributes!"; 190 } 191 } 192 if (lookup_form) { 193 UMA_HISTOGRAM_ENUMERATION( 194 "PasswordManager.PslDomainMatchTriggering", 195 helper.IsMatchingEnabled() 196 ? psl_domain_match_metric 197 : PSLMatchingHelper::PSL_DOMAIN_MATCH_DISABLED, 198 PSLMatchingHelper::PSL_DOMAIN_MATCH_COUNT); 199 } 200 } 201 202 // Schema is analagous to the fields in PasswordForm. 203 // TODO(gcasto): Adding 'form_data' would be nice, but we would need to 204 // serialize in a way that is guaranteed to not have any embedded NULLs. Pickle 205 // doesn't make this guarantee, so we just don't serialize this field. Since 206 // it's only used to crowd source data collection it doesn't matter that much 207 // if it's not available on this platform. 208 const GnomeKeyringPasswordSchema kGnomeSchema = { 209 GNOME_KEYRING_ITEM_GENERIC_SECRET, { 210 { "origin_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING }, 211 { "action_url", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING }, 212 { "username_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING }, 213 { "username_value", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING }, 214 { "password_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING }, 215 { "submit_element", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING }, 216 { "signon_realm", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING }, 217 { "ssl_valid", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 }, 218 { "preferred", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 }, 219 { "date_created", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING }, 220 { "blacklisted_by_user", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 }, 221 { "scheme", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 }, 222 { "type", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 }, 223 { "times_used", GNOME_KEYRING_ATTRIBUTE_TYPE_UINT32 }, 224 { "date_synced", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING }, 225 // This field is always "chrome" so that we can search for it. 226 { "application", GNOME_KEYRING_ATTRIBUTE_TYPE_STRING }, 227 { NULL } 228 } 229 }; 230 231 // Sadly, PasswordStore goes to great lengths to switch from the originally 232 // calling thread to the DB thread, and to provide an asynchronous API to 233 // callers while using a synchronous (virtual) API provided by subclasses like 234 // PasswordStoreX -- but GNOME Keyring really wants to be on the GLib main 235 // thread, which is the UI thread to us. So we end up having to switch threads 236 // again, possibly back to the very same thread (in case the UI thread is the 237 // caller, e.g. in the password management UI), and *block* the DB thread 238 // waiting for a response from the UI thread to provide the synchronous API 239 // PasswordStore expects of us. (It will then in turn switch back to the 240 // original caller to send the asynchronous reply to the original request.) 241 242 // This class represents a call to a GNOME Keyring method. A RunnableMethod 243 // should be posted to the UI thread to call one of its action methods, and then 244 // a WaitResult() method should be called to wait for the result. Each instance 245 // supports only one outstanding method at a time, though multiple instances may 246 // be used in parallel. 247 class GKRMethod : public GnomeKeyringLoader { 248 public: 249 typedef NativeBackendGnome::PasswordFormList PasswordFormList; 250 251 GKRMethod() : event_(false, false), result_(GNOME_KEYRING_RESULT_CANCELLED) {} 252 253 // Action methods. These call gnome_keyring_* functions. Call from UI thread. 254 // See GetProfileSpecificAppString() for more information on the app string. 255 void AddLogin(const PasswordForm& form, const char* app_string); 256 void AddLoginSearch(const PasswordForm& form, const char* app_string); 257 void UpdateLoginSearch(const PasswordForm& form, const char* app_string); 258 void RemoveLogin(const PasswordForm& form, const char* app_string); 259 void GetLogins(const PasswordForm& form, const char* app_string); 260 void GetLoginsList(uint32_t blacklisted_by_user, const char* app_string); 261 void GetAllLogins(const char* app_string); 262 263 // Use after AddLogin, RemoveLogin. 264 GnomeKeyringResult WaitResult(); 265 266 // Use after AddLoginSearch, UpdateLoginSearch, GetLogins, GetLoginsList, 267 // GetAllLogins. 268 GnomeKeyringResult WaitResult(PasswordFormList* forms); 269 270 private: 271 struct GnomeKeyringAttributeListFreeDeleter { 272 inline void operator()(void* list) const { 273 gnome_keyring_attribute_list_free( 274 static_cast<GnomeKeyringAttributeList*>(list)); 275 } 276 }; 277 278 typedef scoped_ptr<GnomeKeyringAttributeList, 279 GnomeKeyringAttributeListFreeDeleter> ScopedAttributeList; 280 281 // Helper methods to abbreviate Gnome Keyring long API names. 282 static void AppendString(ScopedAttributeList* list, 283 const char* name, 284 const char* value); 285 static void AppendString(ScopedAttributeList* list, 286 const char* name, 287 const std::string& value); 288 static void AppendUint32(ScopedAttributeList* list, 289 const char* name, 290 guint32 value); 291 292 // All these callbacks are called on UI thread. 293 static void OnOperationDone(GnomeKeyringResult result, gpointer data); 294 295 static void OnOperationGetList(GnomeKeyringResult result, GList* list, 296 gpointer data); 297 298 base::WaitableEvent event_; 299 GnomeKeyringResult result_; 300 NativeBackendGnome::PasswordFormList forms_; 301 // If the credential search is specified by a single form and needs to use PSL 302 // matching, then the specifying form is stored in |lookup_form_|. If PSL 303 // matching is used to find a result, then the results signon realm, origin 304 // and action are stored are replaced by those of |lookup_form_|. 305 // Additionally, |lookup_form_->signon_realm| is also used to narrow down the 306 // found logins to those which indeed PSL-match the look-up. And finally, 307 // |lookup_form_| set to NULL means that PSL matching is not required. 308 scoped_ptr<PasswordForm> lookup_form_; 309 const PSLMatchingHelper helper_; 310 }; 311 312 void GKRMethod::AddLogin(const PasswordForm& form, const char* app_string) { 313 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 314 time_t date_created = form.date_created.ToTimeT(); 315 // If we are asked to save a password with 0 date, use the current time. 316 // We don't want to actually save passwords as though on January 1, 1970. 317 if (!date_created) 318 date_created = time(NULL); 319 int64 date_synced = form.date_synced.ToInternalValue(); 320 gnome_keyring_store_password( 321 &kGnomeSchema, 322 NULL, // Default keyring. 323 form.origin.spec().c_str(), // Display name. 324 UTF16ToUTF8(form.password_value).c_str(), 325 OnOperationDone, 326 this, // data 327 NULL, // destroy_data 328 "origin_url", form.origin.spec().c_str(), 329 "action_url", form.action.spec().c_str(), 330 "username_element", UTF16ToUTF8(form.username_element).c_str(), 331 "username_value", UTF16ToUTF8(form.username_value).c_str(), 332 "password_element", UTF16ToUTF8(form.password_element).c_str(), 333 "submit_element", UTF16ToUTF8(form.submit_element).c_str(), 334 "signon_realm", form.signon_realm.c_str(), 335 "ssl_valid", form.ssl_valid, 336 "preferred", form.preferred, 337 "date_created", base::Int64ToString(date_created).c_str(), 338 "blacklisted_by_user", form.blacklisted_by_user, 339 "type", form.type, 340 "times_used", form.times_used, 341 "scheme", form.scheme, 342 "date_synced", base::Int64ToString(date_synced).c_str(), 343 "application", app_string, 344 NULL); 345 } 346 347 void GKRMethod::AddLoginSearch(const PasswordForm& form, 348 const char* app_string) { 349 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 350 lookup_form_.reset(NULL); 351 // Search GNOME Keyring for matching passwords to update. 352 ScopedAttributeList attrs(gnome_keyring_attribute_list_new()); 353 AppendString(&attrs, "origin_url", form.origin.spec()); 354 AppendString(&attrs, "username_element", UTF16ToUTF8(form.username_element)); 355 AppendString(&attrs, "username_value", UTF16ToUTF8(form.username_value)); 356 AppendString(&attrs, "password_element", UTF16ToUTF8(form.password_element)); 357 AppendString(&attrs, "submit_element", UTF16ToUTF8(form.submit_element)); 358 AppendString(&attrs, "signon_realm", form.signon_realm); 359 AppendString(&attrs, "application", app_string); 360 gnome_keyring_find_items(GNOME_KEYRING_ITEM_GENERIC_SECRET, 361 attrs.get(), 362 OnOperationGetList, 363 /*data=*/this, 364 /*destroy_data=*/NULL); 365 } 366 367 void GKRMethod::UpdateLoginSearch(const PasswordForm& form, 368 const char* app_string) { 369 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 370 lookup_form_.reset(NULL); 371 // Search GNOME Keyring for matching passwords to update. 372 ScopedAttributeList attrs(gnome_keyring_attribute_list_new()); 373 AppendString(&attrs, "origin_url", form.origin.spec()); 374 AppendString(&attrs, "username_element", UTF16ToUTF8(form.username_element)); 375 AppendString(&attrs, "username_value", UTF16ToUTF8(form.username_value)); 376 AppendString(&attrs, "password_element", UTF16ToUTF8(form.password_element)); 377 AppendString(&attrs, "signon_realm", form.signon_realm); 378 AppendString(&attrs, "application", app_string); 379 gnome_keyring_find_items(GNOME_KEYRING_ITEM_GENERIC_SECRET, 380 attrs.get(), 381 OnOperationGetList, 382 /*data=*/this, 383 /*destroy_data=*/NULL); 384 } 385 386 void GKRMethod::RemoveLogin(const PasswordForm& form, const char* app_string) { 387 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 388 // We find forms using the same fields as LoginDatabase::RemoveLogin(). 389 gnome_keyring_delete_password( 390 &kGnomeSchema, 391 OnOperationDone, 392 this, // data 393 NULL, // destroy_data 394 "origin_url", form.origin.spec().c_str(), 395 "action_url", form.action.spec().c_str(), 396 "username_element", UTF16ToUTF8(form.username_element).c_str(), 397 "username_value", UTF16ToUTF8(form.username_value).c_str(), 398 "password_element", UTF16ToUTF8(form.password_element).c_str(), 399 "submit_element", UTF16ToUTF8(form.submit_element).c_str(), 400 "signon_realm", form.signon_realm.c_str(), 401 "application", app_string, 402 NULL); 403 } 404 405 void GKRMethod::GetLogins(const PasswordForm& form, const char* app_string) { 406 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 407 lookup_form_.reset(new PasswordForm(form)); 408 // Search GNOME Keyring for matching passwords. 409 ScopedAttributeList attrs(gnome_keyring_attribute_list_new()); 410 if (!helper_.ShouldPSLDomainMatchingApply( 411 PSLMatchingHelper::GetRegistryControlledDomain( 412 GURL(form.signon_realm)))) { 413 AppendString(&attrs, "signon_realm", form.signon_realm); 414 } 415 AppendString(&attrs, "application", app_string); 416 gnome_keyring_find_items(GNOME_KEYRING_ITEM_GENERIC_SECRET, 417 attrs.get(), 418 OnOperationGetList, 419 /*data=*/this, 420 /*destroy_data=*/NULL); 421 } 422 423 void GKRMethod::GetLoginsList(uint32_t blacklisted_by_user, 424 const char* app_string) { 425 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 426 lookup_form_.reset(NULL); 427 // Search GNOME Keyring for matching passwords. 428 ScopedAttributeList attrs(gnome_keyring_attribute_list_new()); 429 AppendUint32(&attrs, "blacklisted_by_user", blacklisted_by_user); 430 AppendString(&attrs, "application", app_string); 431 gnome_keyring_find_items(GNOME_KEYRING_ITEM_GENERIC_SECRET, 432 attrs.get(), 433 OnOperationGetList, 434 /*data=*/this, 435 /*destroy_data=*/NULL); 436 } 437 438 void GKRMethod::GetAllLogins(const char* app_string) { 439 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 440 lookup_form_.reset(NULL); 441 // We need to search for something, otherwise we get no results - so 442 // we search for the fixed application string. 443 ScopedAttributeList attrs(gnome_keyring_attribute_list_new()); 444 AppendString(&attrs, "application", app_string); 445 gnome_keyring_find_items(GNOME_KEYRING_ITEM_GENERIC_SECRET, 446 attrs.get(), 447 OnOperationGetList, 448 /*data=*/this, 449 /*destroy_data=*/NULL); 450 } 451 452 GnomeKeyringResult GKRMethod::WaitResult() { 453 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); 454 event_.Wait(); 455 return result_; 456 } 457 458 GnomeKeyringResult GKRMethod::WaitResult(PasswordFormList* forms) { 459 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); 460 event_.Wait(); 461 if (forms->empty()) { 462 // Normal case. Avoid extra allocation by swapping. 463 forms->swap(forms_); 464 } else { 465 // Rare case. Append forms_ to *forms. 466 forms->insert(forms->end(), forms_.begin(), forms_.end()); 467 forms_.clear(); 468 } 469 return result_; 470 } 471 472 // static 473 void GKRMethod::AppendString(GKRMethod::ScopedAttributeList* list, 474 const char* name, 475 const char* value) { 476 gnome_keyring_attribute_list_append_string(list->get(), name, value); 477 } 478 479 // static 480 void GKRMethod::AppendString(GKRMethod::ScopedAttributeList* list, 481 const char* name, 482 const std::string& value) { 483 AppendString(list, name, value.c_str()); 484 } 485 486 // static 487 void GKRMethod::AppendUint32(GKRMethod::ScopedAttributeList* list, 488 const char* name, 489 guint32 value) { 490 gnome_keyring_attribute_list_append_uint32(list->get(), name, value); 491 } 492 493 // static 494 void GKRMethod::OnOperationDone(GnomeKeyringResult result, gpointer data) { 495 GKRMethod* method = static_cast<GKRMethod*>(data); 496 method->result_ = result; 497 method->event_.Signal(); 498 } 499 500 // static 501 void GKRMethod::OnOperationGetList(GnomeKeyringResult result, GList* list, 502 gpointer data) { 503 GKRMethod* method = static_cast<GKRMethod*>(data); 504 method->result_ = result; 505 method->forms_.clear(); 506 // |list| will be freed after this callback returns, so convert it now. 507 ConvertFormList( 508 list, method->lookup_form_.get(), method->helper_, &method->forms_); 509 method->lookup_form_.reset(NULL); 510 method->event_.Signal(); 511 } 512 513 } // namespace 514 515 NativeBackendGnome::NativeBackendGnome(LocalProfileId id) 516 : profile_id_(id) { 517 app_string_ = GetProfileSpecificAppString(); 518 } 519 520 NativeBackendGnome::~NativeBackendGnome() { 521 } 522 523 bool NativeBackendGnome::Init() { 524 return LoadGnomeKeyring() && gnome_keyring_is_available(); 525 } 526 527 bool NativeBackendGnome::RawAddLogin(const PasswordForm& form) { 528 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); 529 GKRMethod method; 530 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 531 base::Bind(&GKRMethod::AddLogin, 532 base::Unretained(&method), 533 form, app_string_.c_str())); 534 GnomeKeyringResult result = method.WaitResult(); 535 if (result != GNOME_KEYRING_RESULT_OK) { 536 LOG(ERROR) << "Keyring save failed: " 537 << gnome_keyring_result_to_message(result); 538 return false; 539 } 540 return true; 541 } 542 543 password_manager::PasswordStoreChangeList NativeBackendGnome::AddLogin( 544 const PasswordForm& form) { 545 // Based on LoginDatabase::AddLogin(), we search for an existing match based 546 // on origin_url, username_element, username_value, password_element, submit 547 // element, and signon_realm first, remove that, and then add the new entry. 548 // We'd add the new one first, and then delete the original, but then the 549 // delete might actually delete the newly-added entry! 550 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); 551 GKRMethod method; 552 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 553 base::Bind(&GKRMethod::AddLoginSearch, 554 base::Unretained(&method), 555 form, app_string_.c_str())); 556 ScopedVector<autofill::PasswordForm> forms; 557 GnomeKeyringResult result = method.WaitResult(&forms.get()); 558 if (result != GNOME_KEYRING_RESULT_OK && 559 result != GNOME_KEYRING_RESULT_NO_MATCH) { 560 LOG(ERROR) << "Keyring find failed: " 561 << gnome_keyring_result_to_message(result); 562 return password_manager::PasswordStoreChangeList(); 563 } 564 password_manager::PasswordStoreChangeList changes; 565 if (forms.size() > 0) { 566 if (forms.size() > 1) { 567 LOG(WARNING) << "Adding login when there are " << forms.size() 568 << " matching logins already! Will replace only the first."; 569 } 570 571 if (RemoveLogin(*forms[0])) { 572 changes.push_back(password_manager::PasswordStoreChange( 573 password_manager::PasswordStoreChange::REMOVE, *forms[0])); 574 } 575 } 576 if (RawAddLogin(form)) { 577 changes.push_back(password_manager::PasswordStoreChange( 578 password_manager::PasswordStoreChange::ADD, form)); 579 } 580 return changes; 581 } 582 583 bool NativeBackendGnome::UpdateLogin( 584 const PasswordForm& form, 585 password_manager::PasswordStoreChangeList* changes) { 586 // Based on LoginDatabase::UpdateLogin(), we search for forms to update by 587 // origin_url, username_element, username_value, password_element, and 588 // signon_realm. We then compare the result to the updated form. If they 589 // differ in any of the mutable fields, then we remove the original, and 590 // then add the new entry. We'd add the new one first, and then delete the 591 // original, but then the delete might actually delete the newly-added entry! 592 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); 593 DCHECK(changes); 594 changes->clear(); 595 GKRMethod method; 596 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 597 base::Bind(&GKRMethod::UpdateLoginSearch, 598 base::Unretained(&method), 599 form, app_string_.c_str())); 600 ScopedVector<autofill::PasswordForm> forms; 601 GnomeKeyringResult result = method.WaitResult(&forms.get()); 602 if (result != GNOME_KEYRING_RESULT_OK) { 603 LOG(ERROR) << "Keyring find failed: " 604 << gnome_keyring_result_to_message(result); 605 return false; 606 } 607 608 bool removed = false; 609 for (size_t i = 0; i < forms.size(); ++i) { 610 if (*forms[i] != form) { 611 RemoveLogin(*forms[i]); 612 removed = true; 613 } 614 } 615 if (!removed) 616 return true; 617 618 if (RawAddLogin(form)) { 619 password_manager::PasswordStoreChange change( 620 password_manager::PasswordStoreChange::UPDATE, form); 621 changes->push_back(change); 622 return true; 623 } 624 return false; 625 } 626 627 bool NativeBackendGnome::RemoveLogin(const PasswordForm& form) { 628 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); 629 GKRMethod method; 630 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 631 base::Bind(&GKRMethod::RemoveLogin, 632 base::Unretained(&method), 633 form, app_string_.c_str())); 634 GnomeKeyringResult result = method.WaitResult(); 635 if (result != GNOME_KEYRING_RESULT_OK) { 636 // Warning, not error, because this can sometimes happen due to the user 637 // racing with the daemon to delete the password a second time. 638 LOG(WARNING) << "Keyring delete failed: " 639 << gnome_keyring_result_to_message(result); 640 return false; 641 } 642 return true; 643 } 644 645 bool NativeBackendGnome::RemoveLoginsCreatedBetween( 646 base::Time delete_begin, 647 base::Time delete_end, 648 password_manager::PasswordStoreChangeList* changes) { 649 return RemoveLoginsBetween( 650 delete_begin, delete_end, CREATION_TIMESTAMP, changes); 651 } 652 653 bool NativeBackendGnome::RemoveLoginsSyncedBetween( 654 base::Time delete_begin, 655 base::Time delete_end, 656 password_manager::PasswordStoreChangeList* changes) { 657 return RemoveLoginsBetween(delete_begin, delete_end, SYNC_TIMESTAMP, changes); 658 } 659 660 bool NativeBackendGnome::GetLogins(const PasswordForm& form, 661 PasswordFormList* forms) { 662 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); 663 GKRMethod method; 664 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 665 base::Bind(&GKRMethod::GetLogins, 666 base::Unretained(&method), 667 form, app_string_.c_str())); 668 GnomeKeyringResult result = method.WaitResult(forms); 669 if (result == GNOME_KEYRING_RESULT_NO_MATCH) 670 return true; 671 if (result != GNOME_KEYRING_RESULT_OK) { 672 LOG(ERROR) << "Keyring find failed: " 673 << gnome_keyring_result_to_message(result); 674 return false; 675 } 676 return true; 677 } 678 679 bool NativeBackendGnome::GetAutofillableLogins(PasswordFormList* forms) { 680 return GetLoginsList(forms, true); 681 } 682 683 bool NativeBackendGnome::GetBlacklistLogins(PasswordFormList* forms) { 684 return GetLoginsList(forms, false); 685 } 686 687 bool NativeBackendGnome::GetLoginsList(PasswordFormList* forms, 688 bool autofillable) { 689 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); 690 691 uint32_t blacklisted_by_user = !autofillable; 692 693 GKRMethod method; 694 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 695 base::Bind(&GKRMethod::GetLoginsList, 696 base::Unretained(&method), 697 blacklisted_by_user, app_string_.c_str())); 698 GnomeKeyringResult result = method.WaitResult(forms); 699 if (result == GNOME_KEYRING_RESULT_NO_MATCH) 700 return true; 701 if (result != GNOME_KEYRING_RESULT_OK) { 702 LOG(ERROR) << "Keyring find failed: " 703 << gnome_keyring_result_to_message(result); 704 return false; 705 } 706 return true; 707 } 708 709 bool NativeBackendGnome::GetAllLogins(PasswordFormList* forms) { 710 GKRMethod method; 711 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 712 base::Bind(&GKRMethod::GetAllLogins, 713 base::Unretained(&method), 714 app_string_.c_str())); 715 GnomeKeyringResult result = method.WaitResult(forms); 716 if (result == GNOME_KEYRING_RESULT_NO_MATCH) 717 return true; 718 if (result != GNOME_KEYRING_RESULT_OK) { 719 LOG(ERROR) << "Keyring find failed: " 720 << gnome_keyring_result_to_message(result); 721 return false; 722 } 723 return true; 724 } 725 726 bool NativeBackendGnome::GetLoginsBetween(base::Time get_begin, 727 base::Time get_end, 728 TimestampToCompare date_to_compare, 729 PasswordFormList* forms) { 730 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); 731 // We could walk the list and add items as we find them, but it is much 732 // easier to build the list and then filter the results. 733 PasswordFormList all_forms; 734 if (!GetAllLogins(&all_forms)) 735 return false; 736 737 base::Time autofill::PasswordForm::*date_member = 738 date_to_compare == CREATION_TIMESTAMP 739 ? &autofill::PasswordForm::date_created 740 : &autofill::PasswordForm::date_synced; 741 for (size_t i = 0; i < all_forms.size(); ++i) { 742 if (get_begin <= all_forms[i]->*date_member && 743 (get_end.is_null() || all_forms[i]->*date_member < get_end)) { 744 forms->push_back(all_forms[i]); 745 } else { 746 delete all_forms[i]; 747 } 748 } 749 750 return true; 751 } 752 753 bool NativeBackendGnome::RemoveLoginsBetween( 754 base::Time get_begin, 755 base::Time get_end, 756 TimestampToCompare date_to_compare, 757 password_manager::PasswordStoreChangeList* changes) { 758 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); 759 DCHECK(changes); 760 changes->clear(); 761 // We could walk the list and delete items as we find them, but it is much 762 // easier to build the list and use RemoveLogin() to delete them. 763 ScopedVector<autofill::PasswordForm> forms; 764 if (!GetLoginsBetween(get_begin, get_end, date_to_compare, &forms.get())) 765 return false; 766 767 bool ok = true; 768 for (size_t i = 0; i < forms.size(); ++i) { 769 if (RemoveLogin(*forms[i])) { 770 changes->push_back(password_manager::PasswordStoreChange( 771 password_manager::PasswordStoreChange::REMOVE, *forms[i])); 772 } else { 773 ok = false; 774 } 775 } 776 return ok; 777 } 778 779 std::string NativeBackendGnome::GetProfileSpecificAppString() const { 780 // Originally, the application string was always just "chrome" and used only 781 // so that we had *something* to search for since GNOME Keyring won't search 782 // for nothing. Now we use it to distinguish passwords for different profiles. 783 return base::StringPrintf("%s-%d", kGnomeKeyringAppString, profile_id_); 784 } 785