| OLD | NEW |
| 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 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/password_store_mac.h" | 5 #include "chrome/browser/password_manager/password_store_mac.h" |
| 6 #include "chrome/browser/password_manager/password_store_mac_internal.h" | 6 #include "chrome/browser/password_manager/password_store_mac_internal.h" |
| 7 | 7 |
| 8 #include <CoreServices/CoreServices.h> | 8 #include <CoreServices/CoreServices.h> |
| 9 #include <string> | 9 #include <string> |
| 10 #include <vector> | 10 #include <vector> |
| 11 | 11 |
| 12 #include "base/logging.h" | 12 #include "base/logging.h" |
| 13 #include "base/stl_util-inl.h" |
| 13 #include "base/string_util.h" | 14 #include "base/string_util.h" |
| 14 #include "base/time.h" | 15 #include "base/time.h" |
| 15 #include "chrome/browser/keychain_mac.h" | 16 #include "chrome/browser/keychain_mac.h" |
| 17 #include "chrome/browser/password_manager/login_database_mac.h" |
| 16 | 18 |
| 17 using webkit_glue::PasswordForm; | 19 using webkit_glue::PasswordForm; |
| 18 | 20 |
| 19 namespace internal_keychain_helpers { | 21 namespace internal_keychain_helpers { |
| 20 | 22 |
| 21 // Utility class to handle the details of constructing and running a keychain | 23 // Utility class to handle the details of constructing and running a keychain |
| 22 // search from a set of attributes. | 24 // search from a set of attributes. |
| 23 class KeychainSearch { | 25 class KeychainSearch { |
| 24 public: | 26 public: |
| 25 KeychainSearch(const MacKeychain& keychain); | 27 KeychainSearch(const MacKeychain& keychain); |
| (...skipping 218 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 244 // on each of them when it is finished with them. | 246 // on each of them when it is finished with them. |
| 245 void FindMatchingKeychainItems(const MacKeychain& keychain, | 247 void FindMatchingKeychainItems(const MacKeychain& keychain, |
| 246 const std::string& signon_realm, | 248 const std::string& signon_realm, |
| 247 PasswordForm::Scheme scheme, | 249 PasswordForm::Scheme scheme, |
| 248 std::vector<SecKeychainItemRef>* items) { | 250 std::vector<SecKeychainItemRef>* items) { |
| 249 // Construct a keychain search based on the signon_realm and scheme. | 251 // Construct a keychain search based on the signon_realm and scheme. |
| 250 std::string server; | 252 std::string server; |
| 251 std::string security_domain; | 253 std::string security_domain; |
| 252 int port; | 254 int port; |
| 253 bool is_secure; | 255 bool is_secure; |
| 254 if (!internal_keychain_helpers::ExtractSignonRealmComponents( | 256 if (!ExtractSignonRealmComponents(signon_realm, &server, &port, &is_secure, |
| 255 signon_realm, &server, &port, &is_secure, &security_domain)) { | 257 &security_domain)) { |
| 256 // TODO(stuartmorgan): Proxies will currently fail here, since their | 258 // TODO(stuartmorgan): Proxies will currently fail here, since their |
| 257 // signon_realm is not a URL. We need to detect the proxy case and handle | 259 // signon_realm is not a URL. We need to detect the proxy case and handle |
| 258 // it specially. | 260 // it specially. |
| 259 return; | 261 return; |
| 260 } | 262 } |
| 261 | 263 |
| 262 SecProtocolType protocol = is_secure ? kSecProtocolTypeHTTPS | 264 SecProtocolType protocol = is_secure ? kSecProtocolTypeHTTPS |
| 263 : kSecProtocolTypeHTTP; | 265 : kSecProtocolTypeHTTP; |
| 264 SecAuthenticationType auth_type = | 266 SecAuthenticationType auth_type = AuthTypeForScheme(scheme); |
| 265 internal_keychain_helpers::AuthTypeForScheme(scheme); | |
| 266 | 267 |
| 267 internal_keychain_helpers::KeychainSearch keychain_search(keychain); | 268 KeychainSearch keychain_search(keychain); |
| 268 keychain_search.Init(server.c_str(), port, protocol, auth_type, | 269 keychain_search.Init(server.c_str(), port, protocol, auth_type, |
| 269 (scheme == PasswordForm::SCHEME_HTML) ? | 270 (scheme == PasswordForm::SCHEME_HTML) ? |
| 270 NULL : security_domain.c_str(), | 271 NULL : security_domain.c_str(), |
| 271 NULL, NULL); | 272 NULL, NULL); |
| 272 keychain_search.FindMatchingItems(items); | 273 keychain_search.FindMatchingItems(items); |
| 273 } | 274 } |
| 274 | 275 |
| 275 SecKeychainItemRef FindMatchingKeychainItem(const MacKeychain& keychain, | 276 SecKeychainItemRef FindMatchingKeychainItem(const MacKeychain& keychain, |
| 276 const PasswordForm& form) { | 277 const PasswordForm& form) { |
| 277 // We don't store blacklist entries in the keychain, so the answer to "what | 278 // We don't store blacklist entries in the keychain, so the answer to "what |
| 278 // Keychain item goes with this form" is always "nothing" for blacklists. | 279 // Keychain item goes with this form" is always "nothing" for blacklists. |
| 279 if (form.blacklisted_by_user) { | 280 if (form.blacklisted_by_user) { |
| 280 return NULL; | 281 return NULL; |
| 281 } | 282 } |
| 282 | 283 |
| 283 // Construct a keychain search based on all the unique attributes. | 284 // Construct a keychain search based on all the unique attributes. |
| 284 std::string server; | 285 std::string server; |
| 285 std::string security_domain; | 286 std::string security_domain; |
| 286 int port; | 287 int port; |
| 287 bool is_secure; | 288 bool is_secure; |
| 288 if (!internal_keychain_helpers::ExtractSignonRealmComponents( | 289 if (!ExtractSignonRealmComponents(form.signon_realm, &server, &port, |
| 289 form.signon_realm, &server, &port, &is_secure, &security_domain)) { | 290 &is_secure, &security_domain)) { |
| 290 // TODO(stuartmorgan): Proxies will currently fail here, since their | 291 // TODO(stuartmorgan): Proxies will currently fail here, since their |
| 291 // signon_realm is not a URL. We need to detect the proxy case and handle | 292 // signon_realm is not a URL. We need to detect the proxy case and handle |
| 292 // it specially. | 293 // it specially. |
| 293 return NULL; | 294 return NULL; |
| 294 } | 295 } |
| 295 | 296 |
| 296 SecProtocolType protocol = is_secure ? kSecProtocolTypeHTTPS | 297 SecProtocolType protocol = is_secure ? kSecProtocolTypeHTTPS |
| 297 : kSecProtocolTypeHTTP; | 298 : kSecProtocolTypeHTTP; |
| 298 SecAuthenticationType auth_type = | 299 SecAuthenticationType auth_type = AuthTypeForScheme(form.scheme); |
| 299 internal_keychain_helpers::AuthTypeForScheme(form.scheme); | |
| 300 std::string path = form.origin.path(); | 300 std::string path = form.origin.path(); |
| 301 std::string username = WideToUTF8(form.username_value); | 301 std::string username = WideToUTF8(form.username_value); |
| 302 | 302 |
| 303 internal_keychain_helpers::KeychainSearch keychain_search(keychain); | 303 KeychainSearch keychain_search(keychain); |
| 304 keychain_search.Init(server.c_str(), port, protocol, auth_type, | 304 keychain_search.Init(server.c_str(), port, protocol, auth_type, |
| 305 (form.scheme == PasswordForm::SCHEME_HTML) ? | 305 (form.scheme == PasswordForm::SCHEME_HTML) ? |
| 306 NULL : security_domain.c_str(), | 306 NULL : security_domain.c_str(), |
| 307 path.c_str(), username.c_str()); | 307 path.c_str(), username.c_str()); |
| 308 | 308 |
| 309 std::vector<SecKeychainItemRef> matches; | 309 std::vector<SecKeychainItemRef> matches; |
| 310 keychain_search.FindMatchingItems(&matches); | 310 keychain_search.FindMatchingItems(&matches); |
| 311 | 311 |
| 312 if (matches.size() == 0) { | 312 if (matches.size() == 0) { |
| 313 return NULL; | 313 return NULL; |
| (...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 386 { | 386 { |
| 387 SecProtocolType protocol = *(static_cast<SecProtocolType*>(attr.data)); | 387 SecProtocolType protocol = *(static_cast<SecProtocolType*>(attr.data)); |
| 388 // TODO(stuartmorgan): Handle proxy types | 388 // TODO(stuartmorgan): Handle proxy types |
| 389 form->ssl_valid = (protocol == kSecProtocolTypeHTTPS); | 389 form->ssl_valid = (protocol == kSecProtocolTypeHTTPS); |
| 390 break; | 390 break; |
| 391 } | 391 } |
| 392 case kSecAuthenticationTypeItemAttr: | 392 case kSecAuthenticationTypeItemAttr: |
| 393 { | 393 { |
| 394 SecAuthenticationType auth_type = | 394 SecAuthenticationType auth_type = |
| 395 *(static_cast<SecAuthenticationType*>(attr.data)); | 395 *(static_cast<SecAuthenticationType*>(attr.data)); |
| 396 form->scheme = internal_keychain_helpers::SchemeForAuthType(auth_type); | 396 form->scheme = SchemeForAuthType(auth_type); |
| 397 break; | 397 break; |
| 398 } | 398 } |
| 399 case kSecSecurityDomainItemAttr: | 399 case kSecSecurityDomainItemAttr: |
| 400 security_domain.assign(static_cast<const char *>(attr.data), | 400 security_domain.assign(static_cast<const char *>(attr.data), |
| 401 attr.length); | 401 attr.length); |
| 402 break; | 402 break; |
| 403 case kSecCreationDateItemAttr: | 403 case kSecCreationDateItemAttr: |
| 404 // The only way to get a date out of Keychain is as a string. Really. | 404 // The only way to get a date out of Keychain is as a string. Really. |
| 405 // (The docs claim it's an int, but the header is correct.) | 405 // (The docs claim it's an int, but the header is correct.) |
| 406 internal_keychain_helpers::TimeFromKeychainTimeString( | 406 TimeFromKeychainTimeString(static_cast<char*>(attr.data), attr.length, |
| 407 static_cast<char*>(attr.data), attr.length, &form->date_created); | 407 &form->date_created); |
| 408 break; | 408 break; |
| 409 case kSecNegativeItemAttr: | 409 case kSecNegativeItemAttr: |
| 410 Boolean negative_item = *(static_cast<Boolean*>(attr.data)); | 410 Boolean negative_item = *(static_cast<Boolean*>(attr.data)); |
| 411 if (negative_item) { | 411 if (negative_item) { |
| 412 form->blacklisted_by_user = true; | 412 form->blacklisted_by_user = true; |
| 413 } | 413 } |
| 414 break; | 414 break; |
| 415 } | 415 } |
| 416 } | 416 } |
| 417 keychain.ItemFreeAttributesAndData(attrList, password_data); | 417 keychain.ItemFreeAttributesAndData(attrList, password_data); |
| 418 | 418 |
| 419 // kSecNegativeItemAttr doesn't seem to actually be in widespread use. In | 419 // kSecNegativeItemAttr doesn't seem to actually be in widespread use. In |
| 420 // practice, other browsers seem to use a "" or " " password (and a special | 420 // practice, other browsers seem to use a "" or " " password (and a special |
| 421 // user name) to indicated blacklist entries. | 421 // user name) to indicated blacklist entries. |
| 422 if (form->password_value.empty() || form->password_value == L" ") { | 422 if (form->password_value.empty() || form->password_value == L" ") { |
| 423 form->blacklisted_by_user = true; | 423 form->blacklisted_by_user = true; |
| 424 } | 424 } |
| 425 | 425 |
| 426 form->origin = internal_keychain_helpers::URLFromComponents(form->ssl_valid, | 426 form->origin = URLFromComponents(form->ssl_valid, server, port, path); |
| 427 server, port, | |
| 428 path); | |
| 429 // TODO(stuartmorgan): Handle proxies, which need a different signon_realm | 427 // TODO(stuartmorgan): Handle proxies, which need a different signon_realm |
| 430 // format. | 428 // format. |
| 431 form->signon_realm = form->origin.GetOrigin().spec(); | 429 form->signon_realm = form->origin.GetOrigin().spec(); |
| 432 if (form->scheme != PasswordForm::SCHEME_HTML) { | 430 if (form->scheme != PasswordForm::SCHEME_HTML) { |
| 433 form->signon_realm.append(security_domain); | 431 form->signon_realm.append(security_domain); |
| 434 } | 432 } |
| 435 return true; | 433 return true; |
| 436 } | 434 } |
| 437 | 435 |
| 438 bool AddKeychainEntryForForm(const MacKeychain& keychain, | 436 bool AddKeychainEntryForForm(const MacKeychain& keychain, |
| 439 const PasswordForm& form) { | 437 const PasswordForm& form) { |
| 440 std::string server; | 438 std::string server; |
| 441 std::string security_domain; | 439 std::string security_domain; |
| 442 int port; | 440 int port; |
| 443 bool is_secure; | 441 bool is_secure; |
| 444 if (!internal_keychain_helpers::ExtractSignonRealmComponents( | 442 if (!ExtractSignonRealmComponents(form.signon_realm, &server, &port, |
| 445 form.signon_realm, &server, &port, &is_secure, &security_domain)) { | 443 &is_secure, &security_domain)) { |
| 446 return false; | 444 return false; |
| 447 } | 445 } |
| 448 std::string username = WideToUTF8(form.username_value); | 446 std::string username = WideToUTF8(form.username_value); |
| 449 std::string password = WideToUTF8(form.password_value); | 447 std::string password = WideToUTF8(form.password_value); |
| 450 std::string path = form.origin.path(); | 448 std::string path = form.origin.path(); |
| 451 SecProtocolType protocol = is_secure ? kSecProtocolTypeHTTPS | 449 SecProtocolType protocol = is_secure ? kSecProtocolTypeHTTPS |
| 452 : kSecProtocolTypeHTTP; | 450 : kSecProtocolTypeHTTP; |
| 453 OSStatus result = keychain.AddInternetPassword( | 451 OSStatus result = keychain.AddInternetPassword( |
| 454 NULL, server.size(), server.c_str(), | 452 NULL, server.size(), server.c_str(), |
| 455 security_domain.size(), security_domain.c_str(), | 453 security_domain.size(), security_domain.c_str(), |
| (...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 557 } else { | 555 } else { |
| 558 if (used_keychain_forms.find(keychain_form) == used_keychain_forms.end()) | 556 if (used_keychain_forms.find(keychain_form) == used_keychain_forms.end()) |
| 559 merged_forms->push_back(keychain_form); | 557 merged_forms->push_back(keychain_form); |
| 560 else | 558 else |
| 561 delete keychain_form; | 559 delete keychain_form; |
| 562 i = keychain_forms->erase(i); | 560 i = keychain_forms->erase(i); |
| 563 } | 561 } |
| 564 } | 562 } |
| 565 } | 563 } |
| 566 | 564 |
| 567 } // internal_keychain_helpers | 565 // Returns PasswordForms constructed from the given Keychain items. |
| 566 // Caller is responsible for deleting the returned forms. |
| 567 std::vector<PasswordForm*> CreateFormsFromKeychainItems( |
| 568 const MacKeychain& keychain, |
| 569 const std::vector<SecKeychainItemRef>& items) { |
| 570 std::vector<PasswordForm*> keychain_forms; |
| 571 for (std::vector<SecKeychainItemRef>::const_iterator i = items.begin(); |
| 572 i != items.end(); ++i) { |
| 573 PasswordForm* form = new PasswordForm(); |
| 574 if (FillPasswordFormFromKeychainItem(keychain, *i, form)) { |
| 575 keychain_forms.push_back(form); |
| 576 } |
| 577 } |
| 578 return keychain_forms; |
| 579 } |
| 580 |
| 581 // Returns PasswordForms for each keychain entry matching |form|. |
| 582 // Caller is responsible for deleting the returned forms. |
| 583 std::vector<PasswordForm*> KeychainFormsMatchingForm( |
| 584 const MacKeychain& keychain, const PasswordForm& query_form) { |
| 585 std::vector<SecKeychainItemRef> keychain_items; |
| 586 FindMatchingKeychainItems(keychain, query_form.signon_realm, |
| 587 query_form.scheme, &keychain_items); |
| 588 |
| 589 std::vector<PasswordForm*> keychain_forms = |
| 590 CreateFormsFromKeychainItems(keychain, keychain_items); |
| 591 for (std::vector<SecKeychainItemRef>::iterator i = keychain_items.begin(); |
| 592 i != keychain_items.end(); ++i) { |
| 593 keychain.Free(*i); |
| 594 } |
| 595 return keychain_forms; |
| 596 } |
| 597 |
| 598 } // namespace internal_keychain_helpers |
| 568 | 599 |
| 569 #pragma mark - | 600 #pragma mark - |
| 570 | 601 |
| 571 PasswordStoreMac::PasswordStoreMac(MacKeychain* keychain) | 602 PasswordStoreMac::PasswordStoreMac(MacKeychain* keychain, |
| 572 : keychain_(keychain) { | 603 LoginDatabaseMac* login_db) |
| 604 : keychain_(keychain), login_metadata_db_(login_db) { |
| 573 DCHECK(keychain_.get()); | 605 DCHECK(keychain_.get()); |
| 606 DCHECK(login_metadata_db_.get()); |
| 574 } | 607 } |
| 575 | 608 |
| 609 PasswordStoreMac::~PasswordStoreMac() {} |
| 610 |
| 576 void PasswordStoreMac::AddLoginImpl(const PasswordForm& form) { | 611 void PasswordStoreMac::AddLoginImpl(const PasswordForm& form) { |
| 577 NOTIMPLEMENTED(); | 612 NOTIMPLEMENTED(); |
| 578 } | 613 } |
| 579 | 614 |
| 580 void PasswordStoreMac::UpdateLoginImpl(const PasswordForm& form) { | 615 void PasswordStoreMac::UpdateLoginImpl(const PasswordForm& form) { |
| 581 NOTIMPLEMENTED(); | 616 NOTIMPLEMENTED(); |
| 582 } | 617 } |
| 583 | 618 |
| 584 void PasswordStoreMac::RemoveLoginImpl(const PasswordForm& form) { | 619 void PasswordStoreMac::RemoveLoginImpl(const PasswordForm& form) { |
| 585 NOTIMPLEMENTED(); | 620 NOTIMPLEMENTED(); |
| 586 } | 621 } |
| 587 | 622 |
| 588 void PasswordStoreMac::GetLoginsImpl(GetLoginsRequest* request) { | 623 void PasswordStoreMac::GetLoginsImpl(GetLoginsRequest* request) { |
| 589 std::vector<SecKeychainItemRef> keychain_items; | 624 std::vector<PasswordForm*> keychain_forms = |
| 625 internal_keychain_helpers::KeychainFormsMatchingForm(*keychain_, |
| 626 request->form); |
| 590 | 627 |
| 591 internal_keychain_helpers::FindMatchingKeychainItems( | 628 std::vector<PasswordForm*> database_forms; |
| 592 *keychain_, request->form.signon_realm, request->form.scheme, | 629 login_metadata_db_->GetLogins(request->form, &database_forms); |
| 593 &keychain_items); | |
| 594 | 630 |
| 595 std::vector<PasswordForm*> forms; | 631 std::vector<PasswordForm*> merged_forms; |
| 632 internal_keychain_helpers::MergePasswordForms(&keychain_forms, |
| 633 &database_forms, |
| 634 &merged_forms); |
| 596 | 635 |
| 597 for (std::vector<SecKeychainItemRef>::iterator i = keychain_items.begin(); | 636 // Clean up any orphaned database entries. |
| 598 i != keychain_items.end(); ++i) { | 637 for (std::vector<PasswordForm*>::iterator i = database_forms.begin(); |
| 599 // Consumer is responsible for deleting the forms when they are done... | 638 i != database_forms.end(); ++i) { |
| 600 PasswordForm* form = new PasswordForm(); | 639 login_metadata_db_->RemoveLogin(**i); |
| 601 SecKeychainItemRef keychain_item = *i; | |
| 602 if (internal_keychain_helpers::FillPasswordFormFromKeychainItem( | |
| 603 *keychain_, keychain_item, form)) { | |
| 604 forms.push_back(form); | |
| 605 } | |
| 606 // ... but we need to clean up the keychain item. | |
| 607 keychain_->Free(keychain_item); | |
| 608 } | 640 } |
| 641 // Delete the forms we aren't returning. |
| 642 STLDeleteElements(&database_forms); |
| 643 STLDeleteElements(&keychain_forms); |
| 609 | 644 |
| 610 NotifyConsumer(request, forms); | 645 NotifyConsumer(request, merged_forms); |
| 611 } | 646 } |
| OLD | NEW |