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