| OLD | NEW |
| 1 // Copyright (c) 2015 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/password_manager/native_backend_libsecret.h" | 5 #include "chrome/browser/password_manager/native_backend_libsecret.h" |
| 6 | 6 |
| 7 #include <dlfcn.h> | 7 #include <dlfcn.h> |
| 8 #include <list> | 8 #include <list> |
| 9 | 9 |
| 10 #include "base/basictypes.h" | 10 #include "base/basictypes.h" |
| (...skipping 218 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 229 name_values_.push_back(value); | 229 name_values_.push_back(value); |
| 230 gpointer value_str = | 230 gpointer value_str = |
| 231 static_cast<gpointer>(const_cast<char*>(name_values_.back().c_str())); | 231 static_cast<gpointer>(const_cast<char*>(name_values_.back().c_str())); |
| 232 g_hash_table_insert(attrs_, name_str, value_str); | 232 g_hash_table_insert(attrs_, name_str, value_str); |
| 233 } | 233 } |
| 234 | 234 |
| 235 void LibsecretAttributesBuilder::Append(const std::string& name, int64 value) { | 235 void LibsecretAttributesBuilder::Append(const std::string& name, int64 value) { |
| 236 Append(name, base::Int64ToString(value)); | 236 Append(name, base::Int64ToString(value)); |
| 237 } | 237 } |
| 238 | 238 |
| 239 // Generates a profile-specific app string based on profile_id_. |
| 240 std::string GetProfileSpecificAppString(LocalProfileId id) { |
| 241 // Originally, the application string was always just "chrome" and used only |
| 242 // so that we had *something* to search for since GNOME Keyring won't search |
| 243 // for nothing. Now we use it to distinguish passwords for different profiles. |
| 244 return base::StringPrintf("%s-%d", kLibsecretAppString, id); |
| 245 } |
| 246 |
| 239 } // namespace | 247 } // namespace |
| 240 | 248 |
| 241 bool LibsecretLoader::LibsecretIsAvailable() { | 249 bool LibsecretLoader::LibsecretIsAvailable() { |
| 242 if (!libsecret_loaded) | 250 if (!libsecret_loaded) |
| 243 return false; | 251 return false; |
| 244 // A dummy query is made to check for availability, because libsecret doesn't | 252 // A dummy query is made to check for availability, because libsecret doesn't |
| 245 // have a dedicated availability function. For performance reasons, the query | 253 // have a dedicated availability function. For performance reasons, the query |
| 246 // is meant to return an empty result. | 254 // is meant to return an empty result. |
| 247 LibsecretAttributesBuilder attrs; | 255 LibsecretAttributesBuilder attrs; |
| 248 attrs.Append("application", "chrome-string_to_get_empty_result"); | 256 attrs.Append("application", "chrome-string_to_get_empty_result"); |
| (...skipping 24 matching lines...) Expand all Loading... |
| 273 return LoadLibsecret() && LibsecretIsAvailable(); | 281 return LoadLibsecret() && LibsecretIsAvailable(); |
| 274 } | 282 } |
| 275 | 283 |
| 276 password_manager::PasswordStoreChangeList NativeBackendLibsecret::AddLogin( | 284 password_manager::PasswordStoreChangeList NativeBackendLibsecret::AddLogin( |
| 277 const PasswordForm& form) { | 285 const PasswordForm& form) { |
| 278 // Based on LoginDatabase::AddLogin(), we search for an existing match based | 286 // Based on LoginDatabase::AddLogin(), we search for an existing match based |
| 279 // on origin_url, username_element, username_value, password_element, submit | 287 // on origin_url, username_element, username_value, password_element, submit |
| 280 // element, and signon_realm first, remove that, and then add the new entry. | 288 // element, and signon_realm first, remove that, and then add the new entry. |
| 281 // We'd add the new one first, and then delete the original, but then the | 289 // We'd add the new one first, and then delete the original, but then the |
| 282 // delete might actually delete the newly-added entry! | 290 // delete might actually delete the newly-added entry! |
| 283 ScopedVector<autofill::PasswordForm> forms; | 291 ScopedVector<autofill::PasswordForm> forms = |
| 284 AddUpdateLoginSearch(form, SEARCH_USE_SUBMIT, &forms); | 292 AddUpdateLoginSearch(form, SEARCH_USE_SUBMIT); |
| 285 password_manager::PasswordStoreChangeList changes; | 293 password_manager::PasswordStoreChangeList changes; |
| 286 if (forms.size() > 0) { | 294 if (forms.size() > 0) { |
| 287 if (forms.size() > 1) { | 295 if (forms.size() > 1) { |
| 288 VLOG(1) << "Adding login when there are " << forms.size() | 296 VLOG(1) << "Adding login when there are " << forms.size() |
| 289 << " matching logins already! Will replace only the first."; | 297 << " matching logins already! Will replace only the first."; |
| 290 } | 298 } |
| 291 | 299 |
| 292 if (RemoveLogin(*forms[0])) { | 300 if (RemoveLogin(*forms[0])) { |
| 293 changes.push_back(password_manager::PasswordStoreChange( | 301 changes.push_back(password_manager::PasswordStoreChange( |
| 294 password_manager::PasswordStoreChange::REMOVE, *forms[0])); | 302 password_manager::PasswordStoreChange::REMOVE, *forms[0])); |
| (...skipping 11 matching lines...) Expand all Loading... |
| 306 password_manager::PasswordStoreChangeList* changes) { | 314 password_manager::PasswordStoreChangeList* changes) { |
| 307 // Based on LoginDatabase::UpdateLogin(), we search for forms to update by | 315 // Based on LoginDatabase::UpdateLogin(), we search for forms to update by |
| 308 // origin_url, username_element, username_value, password_element, and | 316 // origin_url, username_element, username_value, password_element, and |
| 309 // signon_realm. We then compare the result to the updated form. If they | 317 // signon_realm. We then compare the result to the updated form. If they |
| 310 // differ in any of the mutable fields, then we remove the original, and | 318 // differ in any of the mutable fields, then we remove the original, and |
| 311 // then add the new entry. We'd add the new one first, and then delete the | 319 // then add the new entry. We'd add the new one first, and then delete the |
| 312 // original, but then the delete might actually delete the newly-added entry! | 320 // original, but then the delete might actually delete the newly-added entry! |
| 313 DCHECK(changes); | 321 DCHECK(changes); |
| 314 changes->clear(); | 322 changes->clear(); |
| 315 | 323 |
| 316 ScopedVector<autofill::PasswordForm> forms; | 324 ScopedVector<autofill::PasswordForm> forms = |
| 317 AddUpdateLoginSearch(form, SEARCH_IGNORE_SUBMIT, &forms); | 325 AddUpdateLoginSearch(form, SEARCH_IGNORE_SUBMIT); |
| 318 | 326 |
| 319 bool removed = false; | 327 bool removed = false; |
| 320 for (size_t i = 0; i < forms.size(); ++i) { | 328 for (size_t i = 0; i < forms.size(); ++i) { |
| 321 if (*forms[i] != form) { | 329 if (*forms[i] != form) { |
| 322 RemoveLogin(*forms[i]); | 330 RemoveLogin(*forms[i]); |
| 323 removed = true; | 331 removed = true; |
| 324 } | 332 } |
| 325 } | 333 } |
| 326 if (!removed) | 334 if (!removed) |
| 327 return true; | 335 return true; |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 368 password_manager::PasswordStoreChangeList* changes) { | 376 password_manager::PasswordStoreChangeList* changes) { |
| 369 return RemoveLoginsBetween(delete_begin, delete_end, SYNC_TIMESTAMP, changes); | 377 return RemoveLoginsBetween(delete_begin, delete_end, SYNC_TIMESTAMP, changes); |
| 370 } | 378 } |
| 371 | 379 |
| 372 bool NativeBackendLibsecret::GetLogins( | 380 bool NativeBackendLibsecret::GetLogins( |
| 373 const PasswordForm& form, | 381 const PasswordForm& form, |
| 374 ScopedVector<autofill::PasswordForm>* forms) { | 382 ScopedVector<autofill::PasswordForm>* forms) { |
| 375 return GetLoginsList(&form, ALL_LOGINS, forms); | 383 return GetLoginsList(&form, ALL_LOGINS, forms); |
| 376 } | 384 } |
| 377 | 385 |
| 378 void NativeBackendLibsecret::AddUpdateLoginSearch( | 386 ScopedVector<autofill::PasswordForm> |
| 387 NativeBackendLibsecret::AddUpdateLoginSearch( |
| 379 const autofill::PasswordForm& lookup_form, | 388 const autofill::PasswordForm& lookup_form, |
| 380 AddUpdateLoginSearchOptions options, | 389 AddUpdateLoginSearchOptions options) { |
| 381 ScopedVector<autofill::PasswordForm>* forms) { | |
| 382 LibsecretAttributesBuilder attrs; | 390 LibsecretAttributesBuilder attrs; |
| 383 attrs.Append("origin_url", lookup_form.origin.spec()); | 391 attrs.Append("origin_url", lookup_form.origin.spec()); |
| 384 attrs.Append("username_element", UTF16ToUTF8(lookup_form.username_element)); | 392 attrs.Append("username_element", UTF16ToUTF8(lookup_form.username_element)); |
| 385 attrs.Append("username_value", UTF16ToUTF8(lookup_form.username_value)); | 393 attrs.Append("username_value", UTF16ToUTF8(lookup_form.username_value)); |
| 386 attrs.Append("password_element", UTF16ToUTF8(lookup_form.password_element)); | 394 attrs.Append("password_element", UTF16ToUTF8(lookup_form.password_element)); |
| 387 if (options == SEARCH_USE_SUBMIT) | 395 if (options == SEARCH_USE_SUBMIT) |
| 388 attrs.Append("submit_element", UTF16ToUTF8(lookup_form.submit_element)); | 396 attrs.Append("submit_element", UTF16ToUTF8(lookup_form.submit_element)); |
| 389 attrs.Append("signon_realm", lookup_form.signon_realm); | 397 attrs.Append("signon_realm", lookup_form.signon_realm); |
| 390 attrs.Append("application", app_string_); | 398 attrs.Append("application", app_string_); |
| 391 | 399 |
| 392 GError* error = nullptr; | 400 GError* error = nullptr; |
| 393 GList* found = secret_service_search_sync(nullptr, // default secret service | 401 GList* found = secret_service_search_sync(nullptr, // default secret service |
| 394 &kLibsecretSchema, attrs.Get(), | 402 &kLibsecretSchema, attrs.Get(), |
| 395 SECRET_SEARCH_ALL, | 403 SECRET_SEARCH_ALL, |
| 396 nullptr, // no cancellable ojbect | 404 nullptr, // no cancellable ojbect |
| 397 &error); | 405 &error); |
| 398 if (error) { | 406 if (error) { |
| 399 VLOG(1) << "Unable to get logins " << error->message; | 407 VLOG(1) << "Unable to get logins " << error->message; |
| 400 g_error_free(error); | 408 g_error_free(error); |
| 401 if (found) | 409 if (found) |
| 402 g_list_free(found); | 410 g_list_free(found); |
| 403 return; | 411 return ScopedVector<autofill::PasswordForm>(); |
| 404 } | 412 } |
| 405 | 413 |
| 406 ConvertFormList(found, &lookup_form, forms); | 414 return ConvertFormList(found, &lookup_form); |
| 407 } | 415 } |
| 408 | 416 |
| 409 bool NativeBackendLibsecret::RawAddLogin(const PasswordForm& form) { | 417 bool NativeBackendLibsecret::RawAddLogin(const PasswordForm& form) { |
| 410 int64 date_created = form.date_created.ToInternalValue(); | 418 int64 date_created = form.date_created.ToInternalValue(); |
| 411 // If we are asked to save a password with 0 date, use the current time. | 419 // If we are asked to save a password with 0 date, use the current time. |
| 412 // We don't want to actually save passwords as though on January 1, 1601. | 420 // We don't want to actually save passwords as though on January 1, 1601. |
| 413 if (!date_created) | 421 if (!date_created) |
| 414 date_created = base::Time::Now().ToInternalValue(); | 422 date_created = base::Time::Now().ToInternalValue(); |
| 415 int64 date_synced = form.date_synced.ToInternalValue(); | 423 int64 date_synced = form.date_synced.ToInternalValue(); |
| 416 std::string form_data; | 424 std::string form_data; |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 485 nullptr, // no cancellable ojbect | 493 nullptr, // no cancellable ojbect |
| 486 &error); | 494 &error); |
| 487 if (error) { | 495 if (error) { |
| 488 VLOG(1) << "Unable to get logins " << error->message; | 496 VLOG(1) << "Unable to get logins " << error->message; |
| 489 g_error_free(error); | 497 g_error_free(error); |
| 490 if (found) | 498 if (found) |
| 491 g_list_free(found); | 499 g_list_free(found); |
| 492 return false; | 500 return false; |
| 493 } | 501 } |
| 494 | 502 |
| 495 return ConvertFormList(found, lookup_form, forms); | 503 *forms = ConvertFormList(found, lookup_form); |
| 496 } | 504 return true; |
| 497 | |
| 498 bool NativeBackendLibsecret::GetAllLogins( | |
| 499 ScopedVector<autofill::PasswordForm>* forms) { | |
| 500 return GetLoginsList(nullptr, ALL_LOGINS, forms); | |
| 501 } | 505 } |
| 502 | 506 |
| 503 bool NativeBackendLibsecret::GetLoginsBetween( | 507 bool NativeBackendLibsecret::GetLoginsBetween( |
| 504 base::Time get_begin, | 508 base::Time get_begin, |
| 505 base::Time get_end, | 509 base::Time get_end, |
| 506 TimestampToCompare date_to_compare, | 510 TimestampToCompare date_to_compare, |
| 507 ScopedVector<autofill::PasswordForm>* forms) { | 511 ScopedVector<autofill::PasswordForm>* forms) { |
| 512 forms->clear(); |
| 508 ScopedVector<autofill::PasswordForm> all_forms; | 513 ScopedVector<autofill::PasswordForm> all_forms; |
| 509 if (!GetAllLogins(&all_forms)) | 514 if (!GetLoginsList(nullptr, ALL_LOGINS, &all_forms)) |
| 510 return false; | 515 return false; |
| 511 | 516 |
| 512 base::Time autofill::PasswordForm::*date_member = | 517 base::Time autofill::PasswordForm::*date_member = |
| 513 date_to_compare == CREATION_TIMESTAMP | 518 date_to_compare == CREATION_TIMESTAMP |
| 514 ? &autofill::PasswordForm::date_created | 519 ? &autofill::PasswordForm::date_created |
| 515 : &autofill::PasswordForm::date_synced; | 520 : &autofill::PasswordForm::date_synced; |
| 516 for (auto& saved_form : all_forms) { | 521 for (auto& saved_form : all_forms) { |
| 517 if (get_begin <= saved_form->*date_member && | 522 if (get_begin <= saved_form->*date_member && |
| 518 (get_end.is_null() || saved_form->*date_member < get_end)) { | 523 (get_end.is_null() || saved_form->*date_member < get_end)) { |
| 519 forms->push_back(saved_form); | 524 forms->push_back(saved_form); |
| (...skipping 20 matching lines...) Expand all Loading... |
| 540 if (RemoveLogin(*forms[i])) { | 545 if (RemoveLogin(*forms[i])) { |
| 541 changes->push_back(password_manager::PasswordStoreChange( | 546 changes->push_back(password_manager::PasswordStoreChange( |
| 542 password_manager::PasswordStoreChange::REMOVE, *forms[i])); | 547 password_manager::PasswordStoreChange::REMOVE, *forms[i])); |
| 543 } else { | 548 } else { |
| 544 ok = false; | 549 ok = false; |
| 545 } | 550 } |
| 546 } | 551 } |
| 547 return ok; | 552 return ok; |
| 548 } | 553 } |
| 549 | 554 |
| 550 bool NativeBackendLibsecret::ConvertFormList( | 555 ScopedVector<autofill::PasswordForm> NativeBackendLibsecret::ConvertFormList( |
| 551 GList* found, | 556 GList* found, |
| 552 const PasswordForm* lookup_form, | 557 const PasswordForm* lookup_form) { |
| 553 ScopedVector<autofill::PasswordForm>* forms) { | 558 ScopedVector<autofill::PasswordForm> forms; |
| 554 password_manager::PSLDomainMatchMetric psl_domain_match_metric = | 559 password_manager::PSLDomainMatchMetric psl_domain_match_metric = |
| 555 password_manager::PSL_DOMAIN_MATCH_NONE; | 560 password_manager::PSL_DOMAIN_MATCH_NONE; |
| 556 GError* error = nullptr; | 561 GError* error = nullptr; |
| 557 for (GList* element = g_list_first(found); element != nullptr; | 562 for (GList* element = g_list_first(found); element != nullptr; |
| 558 element = g_list_next(element)) { | 563 element = g_list_next(element)) { |
| 559 SecretItem* secretItem = static_cast<SecretItem*>(element->data); | 564 SecretItem* secretItem = static_cast<SecretItem*>(element->data); |
| 560 LibsecretLoader::secret_item_load_secret_sync(secretItem, nullptr, &error); | 565 LibsecretLoader::secret_item_load_secret_sync(secretItem, nullptr, &error); |
| 561 if (error) { | 566 if (error) { |
| 562 VLOG(1) << "Unable to load secret item" << error->message; | 567 VLOG(1) << "Unable to load secret item" << error->message; |
| 563 g_error_free(error); | 568 g_error_free(error); |
| (...skipping 17 matching lines...) Expand all Loading... |
| 581 form->signon_realm = lookup_form->signon_realm; | 586 form->signon_realm = lookup_form->signon_realm; |
| 582 form->origin = lookup_form->origin; | 587 form->origin = lookup_form->origin; |
| 583 } | 588 } |
| 584 SecretValue* secretValue = secret_item_get_secret(secretItem); | 589 SecretValue* secretValue = secret_item_get_secret(secretItem); |
| 585 if (secretValue) { | 590 if (secretValue) { |
| 586 form->password_value = UTF8ToUTF16(secret_value_get_text(secretValue)); | 591 form->password_value = UTF8ToUTF16(secret_value_get_text(secretValue)); |
| 587 secret_value_unref(secretValue); | 592 secret_value_unref(secretValue); |
| 588 } else { | 593 } else { |
| 589 VLOG(1) << "Unable to access password from list element!"; | 594 VLOG(1) << "Unable to access password from list element!"; |
| 590 } | 595 } |
| 591 forms->push_back(form.release()); | 596 forms.push_back(form.release()); |
| 592 } else { | 597 } else { |
| 593 VLOG(1) << "Could not initialize PasswordForm from attributes!"; | 598 VLOG(1) << "Could not initialize PasswordForm from attributes!"; |
| 594 } | 599 } |
| 595 } | 600 } |
| 596 | 601 |
| 597 if (lookup_form) { | 602 if (lookup_form) { |
| 598 const GURL signon_realm(lookup_form->signon_realm); | 603 const GURL signon_realm(lookup_form->signon_realm); |
| 599 std::string registered_domain = | 604 std::string registered_domain = |
| 600 password_manager::GetRegistryControlledDomain(signon_realm); | 605 password_manager::GetRegistryControlledDomain(signon_realm); |
| 601 UMA_HISTOGRAM_ENUMERATION( | 606 UMA_HISTOGRAM_ENUMERATION( |
| 602 "PasswordManager.PslDomainMatchTriggering", | 607 "PasswordManager.PslDomainMatchTriggering", |
| 603 password_manager::ShouldPSLDomainMatchingApply(registered_domain) | 608 password_manager::ShouldPSLDomainMatchingApply(registered_domain) |
| 604 ? psl_domain_match_metric | 609 ? psl_domain_match_metric |
| 605 : password_manager::PSL_DOMAIN_MATCH_NOT_USED, | 610 : password_manager::PSL_DOMAIN_MATCH_NOT_USED, |
| 606 password_manager::PSL_DOMAIN_MATCH_COUNT); | 611 password_manager::PSL_DOMAIN_MATCH_COUNT); |
| 607 } | 612 } |
| 608 g_list_free(found); | 613 g_list_free(found); |
| 609 return true; | 614 return forms.Pass(); |
| 610 } | 615 } |
| 611 | |
| 612 std::string NativeBackendLibsecret::GetProfileSpecificAppString( | |
| 613 LocalProfileId id) { | |
| 614 // Originally, the application string was always just "chrome" and used only | |
| 615 // so that we had *something* to search for since GNOME Keyring won't search | |
| 616 // for nothing. Now we use it to distinguish passwords for different profiles. | |
| 617 return base::StringPrintf("%s-%d", kLibsecretAppString, id); | |
| 618 } | |
| OLD | NEW |