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 261 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
272 return LoadLibsecret() && LibsecretIsAvailable(); | 272 return LoadLibsecret() && LibsecretIsAvailable(); |
273 } | 273 } |
274 | 274 |
275 password_manager::PasswordStoreChangeList NativeBackendLibsecret::AddLogin( | 275 password_manager::PasswordStoreChangeList NativeBackendLibsecret::AddLogin( |
276 const PasswordForm& form) { | 276 const PasswordForm& form) { |
277 // Based on LoginDatabase::AddLogin(), we search for an existing match based | 277 // Based on LoginDatabase::AddLogin(), we search for an existing match based |
278 // on origin_url, username_element, username_value, password_element, submit | 278 // on origin_url, username_element, username_value, password_element, submit |
279 // element, and signon_realm first, remove that, and then add the new entry. | 279 // element, and signon_realm first, remove that, and then add the new entry. |
280 // We'd add the new one first, and then delete the original, but then the | 280 // We'd add the new one first, and then delete the original, but then the |
281 // delete might actually delete the newly-added entry! | 281 // delete might actually delete the newly-added entry! |
282 ScopedVector<autofill::PasswordForm> forms; | 282 ScopedVector<autofill::PasswordForm> forms = |
283 AddUpdateLoginSearch(form, SEARCH_USE_SUBMIT, &forms); | 283 AddUpdateLoginSearch(form, SEARCH_USE_SUBMIT); |
284 password_manager::PasswordStoreChangeList changes; | 284 password_manager::PasswordStoreChangeList changes; |
285 if (forms.size() > 0) { | 285 if (forms.size() > 0) { |
286 if (forms.size() > 1) { | 286 if (forms.size() > 1) { |
287 VLOG(1) << "Adding login when there are " << forms.size() | 287 VLOG(1) << "Adding login when there are " << forms.size() |
288 << " matching logins already! Will replace only the first."; | 288 << " matching logins already! Will replace only the first."; |
289 } | 289 } |
290 | 290 |
291 if (RemoveLogin(*forms[0])) { | 291 if (RemoveLogin(*forms[0])) { |
292 changes.push_back(password_manager::PasswordStoreChange( | 292 changes.push_back(password_manager::PasswordStoreChange( |
293 password_manager::PasswordStoreChange::REMOVE, *forms[0])); | 293 password_manager::PasswordStoreChange::REMOVE, *forms[0])); |
(...skipping 11 matching lines...) Expand all Loading... |
305 password_manager::PasswordStoreChangeList* changes) { | 305 password_manager::PasswordStoreChangeList* changes) { |
306 // Based on LoginDatabase::UpdateLogin(), we search for forms to update by | 306 // Based on LoginDatabase::UpdateLogin(), we search for forms to update by |
307 // origin_url, username_element, username_value, password_element, and | 307 // origin_url, username_element, username_value, password_element, and |
308 // signon_realm. We then compare the result to the updated form. If they | 308 // signon_realm. We then compare the result to the updated form. If they |
309 // differ in any of the mutable fields, then we remove the original, and | 309 // differ in any of the mutable fields, then we remove the original, and |
310 // then add the new entry. We'd add the new one first, and then delete the | 310 // then add the new entry. We'd add the new one first, and then delete the |
311 // original, but then the delete might actually delete the newly-added entry! | 311 // original, but then the delete might actually delete the newly-added entry! |
312 DCHECK(changes); | 312 DCHECK(changes); |
313 changes->clear(); | 313 changes->clear(); |
314 | 314 |
315 ScopedVector<autofill::PasswordForm> forms; | 315 ScopedVector<autofill::PasswordForm> forms = |
316 AddUpdateLoginSearch(form, SEARCH_IGNORE_SUBMIT, &forms); | 316 AddUpdateLoginSearch(form, SEARCH_IGNORE_SUBMIT); |
317 | 317 |
318 bool removed = false; | 318 bool removed = false; |
319 for (size_t i = 0; i < forms.size(); ++i) { | 319 for (size_t i = 0; i < forms.size(); ++i) { |
320 if (*forms[i] != form) { | 320 if (*forms[i] != form) { |
321 RemoveLogin(*forms[i]); | 321 RemoveLogin(*forms[i]); |
322 removed = true; | 322 removed = true; |
323 } | 323 } |
324 } | 324 } |
325 if (!removed) | 325 if (!removed) |
326 return true; | 326 return true; |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
367 password_manager::PasswordStoreChangeList* changes) { | 367 password_manager::PasswordStoreChangeList* changes) { |
368 return RemoveLoginsBetween(delete_begin, delete_end, SYNC_TIMESTAMP, changes); | 368 return RemoveLoginsBetween(delete_begin, delete_end, SYNC_TIMESTAMP, changes); |
369 } | 369 } |
370 | 370 |
371 bool NativeBackendLibsecret::GetLogins( | 371 bool NativeBackendLibsecret::GetLogins( |
372 const PasswordForm& form, | 372 const PasswordForm& form, |
373 ScopedVector<autofill::PasswordForm>* forms) { | 373 ScopedVector<autofill::PasswordForm>* forms) { |
374 return GetLoginsList(&form, ALL_LOGINS, forms); | 374 return GetLoginsList(&form, ALL_LOGINS, forms); |
375 } | 375 } |
376 | 376 |
377 void NativeBackendLibsecret::AddUpdateLoginSearch( | 377 ScopedVector<autofill::PasswordForm> |
| 378 NativeBackendLibsecret::AddUpdateLoginSearch( |
378 const autofill::PasswordForm& lookup_form, | 379 const autofill::PasswordForm& lookup_form, |
379 AddUpdateLoginSearchOptions options, | 380 AddUpdateLoginSearchOptions options) { |
380 ScopedVector<autofill::PasswordForm>* forms) { | |
381 LibsecretAttributesBuilder attrs; | 381 LibsecretAttributesBuilder attrs; |
382 attrs.Append("origin_url", lookup_form.origin.spec()); | 382 attrs.Append("origin_url", lookup_form.origin.spec()); |
383 attrs.Append("username_element", UTF16ToUTF8(lookup_form.username_element)); | 383 attrs.Append("username_element", UTF16ToUTF8(lookup_form.username_element)); |
384 attrs.Append("username_value", UTF16ToUTF8(lookup_form.username_value)); | 384 attrs.Append("username_value", UTF16ToUTF8(lookup_form.username_value)); |
385 attrs.Append("password_element", UTF16ToUTF8(lookup_form.password_element)); | 385 attrs.Append("password_element", UTF16ToUTF8(lookup_form.password_element)); |
386 if (options == SEARCH_USE_SUBMIT) | 386 if (options == SEARCH_USE_SUBMIT) |
387 attrs.Append("submit_element", UTF16ToUTF8(lookup_form.submit_element)); | 387 attrs.Append("submit_element", UTF16ToUTF8(lookup_form.submit_element)); |
388 attrs.Append("signon_realm", lookup_form.signon_realm); | 388 attrs.Append("signon_realm", lookup_form.signon_realm); |
389 attrs.Append("application", app_string_); | 389 attrs.Append("application", app_string_); |
390 | 390 |
391 GError* error = nullptr; | 391 GError* error = nullptr; |
392 GList* found = secret_service_search_sync(nullptr, // default secret service | 392 GList* found = secret_service_search_sync(nullptr, // default secret service |
393 &kLibsecretSchema, attrs.Get(), | 393 &kLibsecretSchema, attrs.Get(), |
394 SECRET_SEARCH_ALL, | 394 SECRET_SEARCH_ALL, |
395 nullptr, // no cancellable ojbect | 395 nullptr, // no cancellable ojbect |
396 &error); | 396 &error); |
397 if (error) { | 397 if (error) { |
398 VLOG(1) << "Unable to get logins " << error->message; | 398 VLOG(1) << "Unable to get logins " << error->message; |
399 g_error_free(error); | 399 g_error_free(error); |
400 if (found) | 400 if (found) |
401 g_list_free(found); | 401 g_list_free(found); |
402 return; | 402 return ScopedVector<autofill::PasswordForm>(); |
403 } | 403 } |
404 | 404 |
405 ConvertFormList(found, &lookup_form, forms); | 405 return ConvertFormList(found, &lookup_form); |
406 } | 406 } |
407 | 407 |
408 bool NativeBackendLibsecret::RawAddLogin(const PasswordForm& form) { | 408 bool NativeBackendLibsecret::RawAddLogin(const PasswordForm& form) { |
409 int64 date_created = form.date_created.ToInternalValue(); | 409 int64 date_created = form.date_created.ToInternalValue(); |
410 // If we are asked to save a password with 0 date, use the current time. | 410 // If we are asked to save a password with 0 date, use the current time. |
411 // We don't want to actually save passwords as though on January 1, 1601. | 411 // We don't want to actually save passwords as though on January 1, 1601. |
412 if (!date_created) | 412 if (!date_created) |
413 date_created = base::Time::Now().ToInternalValue(); | 413 date_created = base::Time::Now().ToInternalValue(); |
414 int64 date_synced = form.date_synced.ToInternalValue(); | 414 int64 date_synced = form.date_synced.ToInternalValue(); |
415 GError* error = nullptr; | 415 GError* error = nullptr; |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
456 | 456 |
457 bool NativeBackendLibsecret::GetBlacklistLogins( | 457 bool NativeBackendLibsecret::GetBlacklistLogins( |
458 ScopedVector<autofill::PasswordForm>* forms) { | 458 ScopedVector<autofill::PasswordForm>* forms) { |
459 return GetLoginsList(nullptr, BLACKLISTED_LOGINS, forms); | 459 return GetLoginsList(nullptr, BLACKLISTED_LOGINS, forms); |
460 } | 460 } |
461 | 461 |
462 bool NativeBackendLibsecret::GetLoginsList( | 462 bool NativeBackendLibsecret::GetLoginsList( |
463 const PasswordForm* lookup_form, | 463 const PasswordForm* lookup_form, |
464 GetLoginsListOptions options, | 464 GetLoginsListOptions options, |
465 ScopedVector<autofill::PasswordForm>* forms) { | 465 ScopedVector<autofill::PasswordForm>* forms) { |
| 466 forms->clear(); |
466 LibsecretAttributesBuilder attrs; | 467 LibsecretAttributesBuilder attrs; |
467 attrs.Append("application", app_string_); | 468 attrs.Append("application", app_string_); |
468 if (options != ALL_LOGINS) | 469 if (options != ALL_LOGINS) |
469 attrs.Append("blacklisted_by_user", options == BLACKLISTED_LOGINS); | 470 attrs.Append("blacklisted_by_user", options == BLACKLISTED_LOGINS); |
470 if (lookup_form && | 471 if (lookup_form && |
471 !password_manager::ShouldPSLDomainMatchingApply( | 472 !password_manager::ShouldPSLDomainMatchingApply( |
472 password_manager::GetRegistryControlledDomain( | 473 password_manager::GetRegistryControlledDomain( |
473 GURL(lookup_form->signon_realm)))) | 474 GURL(lookup_form->signon_realm)))) |
474 attrs.Append("signon_realm", lookup_form->signon_realm); | 475 attrs.Append("signon_realm", lookup_form->signon_realm); |
475 | 476 |
476 GError* error = nullptr; | 477 GError* error = nullptr; |
477 GList* found = secret_service_search_sync(nullptr, // default secret service | 478 GList* found = secret_service_search_sync(nullptr, // default secret service |
478 &kLibsecretSchema, attrs.Get(), | 479 &kLibsecretSchema, attrs.Get(), |
479 SECRET_SEARCH_ALL, | 480 SECRET_SEARCH_ALL, |
480 nullptr, // no cancellable ojbect | 481 nullptr, // no cancellable ojbect |
481 &error); | 482 &error); |
482 if (error) { | 483 if (error) { |
483 VLOG(1) << "Unable to get logins " << error->message; | 484 VLOG(1) << "Unable to get logins " << error->message; |
484 g_error_free(error); | 485 g_error_free(error); |
485 if (found) | 486 if (found) |
486 g_list_free(found); | 487 g_list_free(found); |
487 return false; | 488 return false; |
488 } | 489 } |
489 | 490 |
490 return ConvertFormList(found, lookup_form, forms); | 491 *forms = ConvertFormList(found, lookup_form); |
491 } | 492 return true; |
492 | |
493 bool NativeBackendLibsecret::GetAllLogins( | |
494 ScopedVector<autofill::PasswordForm>* forms) { | |
495 return GetLoginsList(nullptr, ALL_LOGINS, forms); | |
496 } | 493 } |
497 | 494 |
498 bool NativeBackendLibsecret::GetLoginsBetween( | 495 bool NativeBackendLibsecret::GetLoginsBetween( |
499 base::Time get_begin, | 496 base::Time get_begin, |
500 base::Time get_end, | 497 base::Time get_end, |
501 TimestampToCompare date_to_compare, | 498 TimestampToCompare date_to_compare, |
502 ScopedVector<autofill::PasswordForm>* forms) { | 499 ScopedVector<autofill::PasswordForm>* forms) { |
503 ScopedVector<autofill::PasswordForm> all_forms; | 500 ScopedVector<autofill::PasswordForm> all_forms; |
504 if (!GetAllLogins(&all_forms)) | 501 if (!GetLoginsList(nullptr, ALL_LOGINS, forms)) |
505 return false; | 502 return false; |
506 | 503 |
507 base::Time autofill::PasswordForm::*date_member = | 504 base::Time autofill::PasswordForm::*date_member = |
508 date_to_compare == CREATION_TIMESTAMP | 505 date_to_compare == CREATION_TIMESTAMP |
509 ? &autofill::PasswordForm::date_created | 506 ? &autofill::PasswordForm::date_created |
510 : &autofill::PasswordForm::date_synced; | 507 : &autofill::PasswordForm::date_synced; |
511 for (auto& saved_form : all_forms) { | 508 for (auto& saved_form : all_forms) { |
512 if (get_begin <= saved_form->*date_member && | 509 if (get_begin <= saved_form->*date_member && |
513 (get_end.is_null() || saved_form->*date_member < get_end)) { | 510 (get_end.is_null() || saved_form->*date_member < get_end)) { |
514 forms->push_back(saved_form); | 511 forms->push_back(saved_form); |
(...skipping 20 matching lines...) Expand all Loading... |
535 if (RemoveLogin(*forms[i])) { | 532 if (RemoveLogin(*forms[i])) { |
536 changes->push_back(password_manager::PasswordStoreChange( | 533 changes->push_back(password_manager::PasswordStoreChange( |
537 password_manager::PasswordStoreChange::REMOVE, *forms[i])); | 534 password_manager::PasswordStoreChange::REMOVE, *forms[i])); |
538 } else { | 535 } else { |
539 ok = false; | 536 ok = false; |
540 } | 537 } |
541 } | 538 } |
542 return ok; | 539 return ok; |
543 } | 540 } |
544 | 541 |
545 bool NativeBackendLibsecret::ConvertFormList( | 542 ScopedVector<autofill::PasswordForm> NativeBackendLibsecret::ConvertFormList( |
546 GList* found, | 543 GList* found, |
547 const PasswordForm* lookup_form, | 544 const PasswordForm* lookup_form) { |
548 ScopedVector<autofill::PasswordForm>* forms) { | 545 ScopedVector<autofill::PasswordForm> forms; |
549 password_manager::PSLDomainMatchMetric psl_domain_match_metric = | 546 password_manager::PSLDomainMatchMetric psl_domain_match_metric = |
550 password_manager::PSL_DOMAIN_MATCH_NONE; | 547 password_manager::PSL_DOMAIN_MATCH_NONE; |
551 GError* error = nullptr; | 548 GError* error = nullptr; |
552 for (GList* element = g_list_first(found); element != nullptr; | 549 for (GList* element = g_list_first(found); element != nullptr; |
553 element = g_list_next(element)) { | 550 element = g_list_next(element)) { |
554 SecretItem* secretItem = static_cast<SecretItem*>(element->data); | 551 SecretItem* secretItem = static_cast<SecretItem*>(element->data); |
555 LibsecretLoader::secret_item_load_secret_sync(secretItem, nullptr, &error); | 552 LibsecretLoader::secret_item_load_secret_sync(secretItem, nullptr, &error); |
556 if (error) { | 553 if (error) { |
557 VLOG(1) << "Unable to load secret item" << error->message; | 554 VLOG(1) << "Unable to load secret item" << error->message; |
558 g_error_free(error); | 555 g_error_free(error); |
(...skipping 17 matching lines...) Expand all Loading... |
576 form->signon_realm = lookup_form->signon_realm; | 573 form->signon_realm = lookup_form->signon_realm; |
577 form->origin = lookup_form->origin; | 574 form->origin = lookup_form->origin; |
578 } | 575 } |
579 SecretValue* secretValue = secret_item_get_secret(secretItem); | 576 SecretValue* secretValue = secret_item_get_secret(secretItem); |
580 if (secretValue) { | 577 if (secretValue) { |
581 form->password_value = UTF8ToUTF16(secret_value_get_text(secretValue)); | 578 form->password_value = UTF8ToUTF16(secret_value_get_text(secretValue)); |
582 secret_value_unref(secretValue); | 579 secret_value_unref(secretValue); |
583 } else { | 580 } else { |
584 VLOG(1) << "Unable to access password from list element!"; | 581 VLOG(1) << "Unable to access password from list element!"; |
585 } | 582 } |
586 forms->push_back(form.release()); | 583 forms.push_back(form.release()); |
587 } else { | 584 } else { |
588 VLOG(1) << "Could not initialize PasswordForm from attributes!"; | 585 VLOG(1) << "Could not initialize PasswordForm from attributes!"; |
589 } | 586 } |
590 } | 587 } |
591 | 588 |
592 if (lookup_form) { | 589 if (lookup_form) { |
593 const GURL signon_realm(lookup_form->signon_realm); | 590 const GURL signon_realm(lookup_form->signon_realm); |
594 std::string registered_domain = | 591 std::string registered_domain = |
595 password_manager::GetRegistryControlledDomain(signon_realm); | 592 password_manager::GetRegistryControlledDomain(signon_realm); |
596 UMA_HISTOGRAM_ENUMERATION( | 593 UMA_HISTOGRAM_ENUMERATION( |
597 "PasswordManager.PslDomainMatchTriggering", | 594 "PasswordManager.PslDomainMatchTriggering", |
598 password_manager::ShouldPSLDomainMatchingApply(registered_domain) | 595 password_manager::ShouldPSLDomainMatchingApply(registered_domain) |
599 ? psl_domain_match_metric | 596 ? psl_domain_match_metric |
600 : password_manager::PSL_DOMAIN_MATCH_NOT_USED, | 597 : password_manager::PSL_DOMAIN_MATCH_NOT_USED, |
601 password_manager::PSL_DOMAIN_MATCH_COUNT); | 598 password_manager::PSL_DOMAIN_MATCH_COUNT); |
602 } | 599 } |
603 g_list_free(found); | 600 g_list_free(found); |
604 return true; | 601 return forms.Pass(); |
605 } | 602 } |
606 | 603 |
607 std::string NativeBackendLibsecret::GetProfileSpecificAppString( | 604 std::string NativeBackendLibsecret::GetProfileSpecificAppString( |
608 LocalProfileId id) { | 605 LocalProfileId id) { |
609 // Originally, the application string was always just "chrome" and used only | 606 // Originally, the application string was always just "chrome" and used only |
610 // so that we had *something* to search for since GNOME Keyring won't search | 607 // so that we had *something* to search for since GNOME Keyring won't search |
611 // for nothing. Now we use it to distinguish passwords for different profiles. | 608 // for nothing. Now we use it to distinguish passwords for different profiles. |
612 return base::StringPrintf("%s-%d", kLibsecretAppString, id); | 609 return base::StringPrintf("%s-%d", kLibsecretAppString, id); |
613 } | 610 } |
OLD | NEW |