| Index: chrome/browser/password_manager/password_store_mac.cc
|
| ===================================================================
|
| --- chrome/browser/password_manager/password_store_mac.cc (revision 19737)
|
| +++ chrome/browser/password_manager/password_store_mac.cc (working copy)
|
| @@ -18,8 +18,6 @@
|
|
|
| using webkit_glue::PasswordForm;
|
|
|
| -namespace internal_keychain_helpers {
|
| -
|
| // Utility class to handle the details of constructing and running a keychain
|
| // search from a set of attributes.
|
| class KeychainSearch {
|
| @@ -153,6 +151,17 @@
|
|
|
| #pragma mark -
|
|
|
| +// TODO(stuartmorgan): Convert most of this to private helpers in
|
| +// MacKeychainPaswordFormAdapter once it has sufficient higher-level public
|
| +// methods to provide test coverage.
|
| +namespace internal_keychain_helpers {
|
| +
|
| +// Takes a PasswordForm's signon_realm and parses it into its component parts,
|
| +// which are returned though the appropriate out parameters.
|
| +// Returns true if it can be successfully parsed, in which case all out params
|
| +// that are non-NULL will be set. If there is no port, port will be 0.
|
| +// If the return value is false, the state of the our params is undefined.
|
| +//
|
| // TODO(stuartmorgan): signon_realm for proxies is not yet supported.
|
| bool ExtractSignonRealmComponents(const std::string& signon_realm,
|
| std::string* server, int* port,
|
| @@ -178,6 +187,8 @@
|
| return true;
|
| }
|
|
|
| +// Returns a URL built from the given components. To create a URL without a
|
| +// port, pass kAnyPort for the |port| parameter.
|
| GURL URLFromComponents(bool is_secure, const std::string& host, int port,
|
| const std::string& path) {
|
| GURL::Replacements url_components;
|
| @@ -197,7 +208,9 @@
|
| return url.ReplaceComponents(url_components);
|
| }
|
|
|
| -// The time string is of the form "yyyyMMddHHmmss'Z", in UTC time.
|
| +// Converts a Keychain time string to a Time object, returning true if
|
| +// time_string_bytes was parsable. If the return value is false, the value of
|
| +// |time| is unchanged.
|
| bool TimeFromKeychainTimeString(const char* time_string_bytes,
|
| unsigned int byte_length,
|
| base::Time* time) {
|
| @@ -208,6 +221,7 @@
|
| time_string[byte_length] = '\0';
|
| base::Time::Exploded exploded_time;
|
| bzero(&exploded_time, sizeof(exploded_time));
|
| + // The time string is of the form "yyyyMMddHHmmss'Z", in UTC time.
|
| int assignments = sscanf(time_string, "%4d%2d%2d%2d%2d%2dZ",
|
| &exploded_time.year, &exploded_time.month,
|
| &exploded_time.day_of_month, &exploded_time.hour,
|
| @@ -221,6 +235,7 @@
|
| return false;
|
| }
|
|
|
| +// Returns the Keychain SecAuthenticationType type corresponding to |scheme|.
|
| SecAuthenticationType AuthTypeForScheme(PasswordForm::Scheme scheme) {
|
| switch (scheme) {
|
| case PasswordForm::SCHEME_HTML: return kSecAuthenticationTypeHTMLForm;
|
| @@ -232,6 +247,7 @@
|
| return kSecAuthenticationTypeDefault;
|
| }
|
|
|
| +// Returns the PasswordForm Scheme corresponding to |auth_type|.
|
| PasswordForm::Scheme SchemeForAuthType(SecAuthenticationType auth_type) {
|
| switch (auth_type) {
|
| case kSecAuthenticationTypeHTMLForm: return PasswordForm::SCHEME_HTML;
|
| @@ -241,40 +257,8 @@
|
| }
|
| }
|
|
|
| -// Searches |keychain| for all items usable for the given signon_realm, and
|
| -// puts them in |items|. The caller is responsible for calling keychain->Free
|
| -// on each of them when it is finished with them.
|
| -void FindMatchingKeychainItems(const MacKeychain& keychain,
|
| - const std::string& signon_realm,
|
| - PasswordForm::Scheme scheme,
|
| - std::vector<SecKeychainItemRef>* items) {
|
| - // Construct a keychain search based on the signon_realm and scheme.
|
| - std::string server;
|
| - std::string security_domain;
|
| - int port;
|
| - bool is_secure;
|
| - if (!ExtractSignonRealmComponents(signon_realm, &server, &port, &is_secure,
|
| - &security_domain)) {
|
| - // TODO(stuartmorgan): Proxies will currently fail here, since their
|
| - // signon_realm is not a URL. We need to detect the proxy case and handle
|
| - // it specially.
|
| - return;
|
| - }
|
| -
|
| - SecProtocolType protocol = is_secure ? kSecProtocolTypeHTTPS
|
| - : kSecProtocolTypeHTTP;
|
| - SecAuthenticationType auth_type = AuthTypeForScheme(scheme);
|
| -
|
| - KeychainSearch keychain_search(keychain);
|
| - keychain_search.Init(server.c_str(), port, protocol, auth_type,
|
| - (scheme == PasswordForm::SCHEME_HTML) ?
|
| - NULL : security_domain.c_str(),
|
| - NULL, NULL);
|
| - keychain_search.FindMatchingItems(items);
|
| -}
|
| -
|
| -SecKeychainItemRef FindMatchingKeychainItem(const MacKeychain& keychain,
|
| - const PasswordForm& form) {
|
| +SecKeychainItemRef MatchingKeychainItem(const MacKeychain& keychain,
|
| + const PasswordForm& form) {
|
| // We don't store blacklist entries in the keychain, so the answer to "what
|
| // Keychain item goes with this form" is always "nothing" for blacklists.
|
| if (form.blacklisted_by_user) {
|
| @@ -433,52 +417,6 @@
|
| return true;
|
| }
|
|
|
| -bool AddKeychainEntryForForm(const MacKeychain& keychain,
|
| - const PasswordForm& form) {
|
| - std::string server;
|
| - std::string security_domain;
|
| - int port;
|
| - bool is_secure;
|
| - if (!ExtractSignonRealmComponents(form.signon_realm, &server, &port,
|
| - &is_secure, &security_domain)) {
|
| - return false;
|
| - }
|
| - std::string username = WideToUTF8(form.username_value);
|
| - std::string password = WideToUTF8(form.password_value);
|
| - std::string path = form.origin.path();
|
| - SecProtocolType protocol = is_secure ? kSecProtocolTypeHTTPS
|
| - : kSecProtocolTypeHTTP;
|
| - OSStatus result = keychain.AddInternetPassword(
|
| - NULL, server.size(), server.c_str(),
|
| - security_domain.size(), security_domain.c_str(),
|
| - username.size(), username.c_str(),
|
| - path.size(), path.c_str(),
|
| - port, protocol, AuthTypeForScheme(form.scheme),
|
| - password.size(), password.c_str(), NULL);
|
| -
|
| - // If we collide with an existing item, find and update it instead.
|
| - if (result == errSecDuplicateItem) {
|
| - SecKeychainItemRef existing_item = FindMatchingKeychainItem(keychain, form);
|
| - if (!existing_item) {
|
| - return false;
|
| - }
|
| - bool changed = SetKeychainItemPassword(keychain, existing_item, password);
|
| - keychain.Free(existing_item);
|
| - return changed;
|
| - }
|
| -
|
| - return (result == noErr);
|
| -}
|
| -
|
| -bool SetKeychainItemPassword(const MacKeychain& keychain,
|
| - const SecKeychainItemRef& keychain_item,
|
| - const std::string& password) {
|
| - OSStatus result = keychain.ItemModifyAttributesAndData(keychain_item, NULL,
|
| - password.size(),
|
| - password.c_str());
|
| - return (result == noErr);
|
| -}
|
| -
|
| bool FormsMatchForMerge(const PasswordForm& form_a, const PasswordForm& form_b,
|
| bool* path_matches) {
|
| // We never merge blacklist entries between our store and the keychain.
|
| @@ -562,40 +500,124 @@
|
| }
|
| }
|
|
|
| -// Returns PasswordForms constructed from the given Keychain items.
|
| +} // namespace internal_keychain_helpers
|
| +
|
| +#pragma mark -
|
| +
|
| +MacKeychainPasswordFormAdapter::MacKeychainPasswordFormAdapter(
|
| + MacKeychain* keychain) : keychain_(keychain) {
|
| +}
|
| +
|
| +// Returns PasswordForms for each keychain entry matching |form|.
|
| // Caller is responsible for deleting the returned forms.
|
| -std::vector<PasswordForm*> CreateFormsFromKeychainItems(
|
| - const MacKeychain& keychain,
|
| - const std::vector<SecKeychainItemRef>& items) {
|
| +std::vector<PasswordForm*>
|
| + MacKeychainPasswordFormAdapter::PasswordsMatchingForm(
|
| + const PasswordForm& query_form) {
|
| + std::vector<SecKeychainItemRef> keychain_items =
|
| + MatchingKeychainItems(query_form.signon_realm, query_form.scheme);
|
| +
|
| + std::vector<PasswordForm*> keychain_forms =
|
| + CreateFormsFromKeychainItems(keychain_items);
|
| + for (std::vector<SecKeychainItemRef>::iterator i = keychain_items.begin();
|
| + i != keychain_items.end(); ++i) {
|
| + keychain_->Free(*i);
|
| + }
|
| + return keychain_forms;
|
| +}
|
| +
|
| +bool MacKeychainPasswordFormAdapter::AddLogin(const PasswordForm& form) {
|
| + std::string server;
|
| + std::string security_domain;
|
| + int port;
|
| + bool is_secure;
|
| + if (!internal_keychain_helpers::ExtractSignonRealmComponents(
|
| + form.signon_realm, &server, &port, &is_secure, &security_domain)) {
|
| + return false;
|
| + }
|
| + std::string username = WideToUTF8(form.username_value);
|
| + std::string password = WideToUTF8(form.password_value);
|
| + std::string path = form.origin.path();
|
| + SecProtocolType protocol = is_secure ? kSecProtocolTypeHTTPS
|
| + : kSecProtocolTypeHTTP;
|
| + OSStatus result = keychain_->AddInternetPassword(
|
| + NULL, server.size(), server.c_str(),
|
| + security_domain.size(), security_domain.c_str(),
|
| + username.size(), username.c_str(),
|
| + path.size(), path.c_str(),
|
| + port, protocol, internal_keychain_helpers::AuthTypeForScheme(form.scheme),
|
| + password.size(), password.c_str(), NULL);
|
| +
|
| + // If we collide with an existing item, find and update it instead.
|
| + if (result == errSecDuplicateItem) {
|
| + SecKeychainItemRef existing_item =
|
| + internal_keychain_helpers::MatchingKeychainItem(*keychain_, form);
|
| + if (!existing_item) {
|
| + return false;
|
| + }
|
| + bool changed = SetKeychainItemPassword(existing_item, password);
|
| + keychain_->Free(existing_item);
|
| + return changed;
|
| + }
|
| +
|
| + return (result == noErr);
|
| +}
|
| +
|
| +std::vector<PasswordForm*>
|
| + MacKeychainPasswordFormAdapter::CreateFormsFromKeychainItems(
|
| + const std::vector<SecKeychainItemRef>& items) {
|
| std::vector<PasswordForm*> keychain_forms;
|
| for (std::vector<SecKeychainItemRef>::const_iterator i = items.begin();
|
| i != items.end(); ++i) {
|
| PasswordForm* form = new PasswordForm();
|
| - if (FillPasswordFormFromKeychainItem(keychain, *i, form)) {
|
| + if (internal_keychain_helpers::FillPasswordFormFromKeychainItem(*keychain_,
|
| + *i, form)) {
|
| keychain_forms.push_back(form);
|
| }
|
| }
|
| return keychain_forms;
|
| }
|
|
|
| -// Returns PasswordForms for each keychain entry matching |form|.
|
| -// Caller is responsible for deleting the returned forms.
|
| -std::vector<PasswordForm*> KeychainFormsMatchingForm(
|
| - const MacKeychain& keychain, const PasswordForm& query_form) {
|
| - std::vector<SecKeychainItemRef> keychain_items;
|
| - FindMatchingKeychainItems(keychain, query_form.signon_realm,
|
| - query_form.scheme, &keychain_items);
|
| +// Searches |keychain| for all items usable for the given signon_realm, and
|
| +// returns them. The caller is responsible for calling keychain->Free
|
| +// on each of them when it is finished with them.
|
| +std::vector<SecKeychainItemRef>
|
| + MacKeychainPasswordFormAdapter::MatchingKeychainItems(
|
| + const std::string& signon_realm, PasswordForm::Scheme scheme) {
|
| + std::vector<SecKeychainItemRef> matches;
|
| + // Construct a keychain search based on the signon_realm and scheme.
|
| + std::string server;
|
| + std::string security_domain;
|
| + int port;
|
| + bool is_secure;
|
| + if (!internal_keychain_helpers::ExtractSignonRealmComponents(
|
| + signon_realm, &server, &port, &is_secure, &security_domain)) {
|
| + // TODO(stuartmorgan): Proxies will currently fail here, since their
|
| + // signon_realm is not a URL. We need to detect the proxy case and handle
|
| + // it specially.
|
| + return matches;
|
| + }
|
|
|
| - std::vector<PasswordForm*> keychain_forms =
|
| - CreateFormsFromKeychainItems(keychain, keychain_items);
|
| - for (std::vector<SecKeychainItemRef>::iterator i = keychain_items.begin();
|
| - i != keychain_items.end(); ++i) {
|
| - keychain.Free(*i);
|
| - }
|
| - return keychain_forms;
|
| + SecProtocolType protocol = is_secure ? kSecProtocolTypeHTTPS
|
| + : kSecProtocolTypeHTTP;
|
| + SecAuthenticationType auth_type =
|
| + internal_keychain_helpers::AuthTypeForScheme(scheme);
|
| +
|
| + KeychainSearch keychain_search(*keychain_);
|
| + keychain_search.Init(server.c_str(), port, protocol, auth_type,
|
| + (scheme == PasswordForm::SCHEME_HTML) ?
|
| + NULL : security_domain.c_str(),
|
| + NULL, NULL);
|
| + keychain_search.FindMatchingItems(&matches);
|
| + return matches;
|
| }
|
|
|
| -} // namespace internal_keychain_helpers
|
| +bool MacKeychainPasswordFormAdapter::SetKeychainItemPassword(
|
| + const SecKeychainItemRef& keychain_item, const std::string& password) {
|
| + OSStatus result = keychain_->ItemModifyAttributesAndData(keychain_item, NULL,
|
| + password.size(),
|
| + password.c_str());
|
| + return (result == noErr);
|
| +}
|
|
|
| #pragma mark -
|
|
|
| @@ -621,9 +643,9 @@
|
| }
|
|
|
| void PasswordStoreMac::GetLoginsImpl(GetLoginsRequest* request) {
|
| + MacKeychainPasswordFormAdapter keychainAdapter(keychain_.get());
|
| std::vector<PasswordForm*> keychain_forms =
|
| - internal_keychain_helpers::KeychainFormsMatchingForm(*keychain_,
|
| - request->form);
|
| + keychainAdapter.PasswordsMatchingForm(request->form);
|
|
|
| std::vector<PasswordForm*> database_forms;
|
| login_metadata_db_->GetLogins(request->form, &database_forms);
|
|
|