15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/password_manager/password_store_mac.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/password_manager/password_store_mac_internal.h"
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <CoreServices/CoreServices.h>
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <set>
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <string>
114e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include <utility>
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <vector>
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/callback.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/mac/mac_logging.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/mac/mac_util.h"
18116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#include "base/memory/scoped_vector.h"
199ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch#include "base/message_loop/message_loop.h"
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/stl_util.h"
21868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/string_util.h"
22868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h"
235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "chrome/browser/mac/security_wrappers.h"
245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "components/password_manager/core/browser/login_database.h"
255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "components/password_manager/core/browser/password_store_change.h"
26effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch#include "content/public/browser/browser_thread.h"
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "crypto/apple_keychain.h"
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)using autofill::PasswordForm;
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using crypto::AppleKeychain;
31c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochusing password_manager::PasswordStoreChange;
32c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochusing password_manager::PasswordStoreChangeList;
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
346e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)namespace {
356e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Utility class to handle the details of constructing and running a keychain
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// search from a set of attributes.
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class KeychainSearch {
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  explicit KeychainSearch(const AppleKeychain& keychain);
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ~KeychainSearch();
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Sets up a keycahin search based on an non "null" (NULL for char*,
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The appropriate "Any" entry for other types) arguments.
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // IMPORTANT: Any paramaters passed in *must* remain valid for as long as the
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // KeychainSearch object, since the search uses them by reference.
48116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  void Init(const char* server,
49116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch            const UInt32* port,
50116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch            const SecProtocolType* protocol,
51116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch            const SecAuthenticationType* auth_type,
52116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch            const char* security_domain,
53116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch            const char* path,
54116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch            const char* username,
55116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch            const OSType* creator);
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Fills |items| with all Keychain items that match the Init'd search.
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If the search fails for any reason, |items| will be unchanged.
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void FindMatchingItems(std::vector<SecKeychainItemRef>* matches);
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const AppleKeychain* keychain_;
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SecKeychainAttributeList search_attributes_;
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SecKeychainSearchRef search_ref_;
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)KeychainSearch::KeychainSearch(const AppleKeychain& keychain)
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : keychain_(&keychain), search_ref_(NULL) {
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  search_attributes_.count = 0;
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  search_attributes_.attr = NULL;
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)KeychainSearch::~KeychainSearch() {
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (search_attributes_.attr) {
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    free(search_attributes_.attr);
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
79116680a4aac90f2aa7413d9095a592090648e557Ben Murdochvoid KeychainSearch::Init(const char* server,
80116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                          const UInt32* port,
81116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                          const SecProtocolType* protocol,
82116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                          const SecAuthenticationType* auth_type,
83116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                          const char* security_domain,
84116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                          const char* path,
85116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                          const char* username,
86116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                          const OSType* creator) {
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Allocate enough to hold everything we might use.
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const unsigned int kMaxEntryCount = 8;
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  search_attributes_.attr =
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      static_cast<SecKeychainAttribute*>(calloc(kMaxEntryCount,
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                sizeof(SecKeychainAttribute)));
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  unsigned int entries = 0;
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We only use search_attributes_ with SearchCreateFromAttributes, which takes
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // a "const SecKeychainAttributeList *", so we trust that they won't try
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // to modify the list, and that casting away const-ness is thus safe.
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (server != NULL) {
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK_LT(entries, kMaxEntryCount);
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    search_attributes_.attr[entries].tag = kSecServerItemAttr;
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    search_attributes_.attr[entries].length = strlen(server);
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    search_attributes_.attr[entries].data =
101116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch        const_cast<void*>(static_cast<const void*>(server));
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ++entries;
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
104116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (port != NULL && *port != kAnyPort) {
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK_LE(entries, kMaxEntryCount);
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    search_attributes_.attr[entries].tag = kSecPortItemAttr;
107116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    search_attributes_.attr[entries].length = sizeof(*port);
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    search_attributes_.attr[entries].data =
109116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch        const_cast<void*>(static_cast<const void*>(port));
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ++entries;
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
112116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (protocol != NULL && *protocol != kSecProtocolTypeAny) {
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK_LE(entries, kMaxEntryCount);
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    search_attributes_.attr[entries].tag = kSecProtocolItemAttr;
115116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    search_attributes_.attr[entries].length = sizeof(*protocol);
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    search_attributes_.attr[entries].data =
117116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch        const_cast<void*>(static_cast<const void*>(protocol));
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ++entries;
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
120116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (auth_type != NULL && *auth_type != kSecAuthenticationTypeAny) {
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK_LE(entries, kMaxEntryCount);
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    search_attributes_.attr[entries].tag = kSecAuthenticationTypeItemAttr;
123116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    search_attributes_.attr[entries].length = sizeof(*auth_type);
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    search_attributes_.attr[entries].data =
125116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch        const_cast<void*>(static_cast<const void*>(auth_type));
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ++entries;
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (security_domain != NULL && strlen(security_domain) > 0) {
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK_LE(entries, kMaxEntryCount);
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    search_attributes_.attr[entries].tag = kSecSecurityDomainItemAttr;
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    search_attributes_.attr[entries].length = strlen(security_domain);
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    search_attributes_.attr[entries].data =
133116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch        const_cast<void*>(static_cast<const void*>(security_domain));
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ++entries;
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (path != NULL && strlen(path) > 0 && strcmp(path, "/") != 0) {
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK_LE(entries, kMaxEntryCount);
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    search_attributes_.attr[entries].tag = kSecPathItemAttr;
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    search_attributes_.attr[entries].length = strlen(path);
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    search_attributes_.attr[entries].data =
141116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch        const_cast<void*>(static_cast<const void*>(path));
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ++entries;
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (username != NULL) {
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK_LE(entries, kMaxEntryCount);
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    search_attributes_.attr[entries].tag = kSecAccountItemAttr;
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    search_attributes_.attr[entries].length = strlen(username);
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    search_attributes_.attr[entries].data =
149116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch        const_cast<void*>(static_cast<const void*>(username));
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ++entries;
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
152116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (creator != NULL) {
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK_LE(entries, kMaxEntryCount);
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    search_attributes_.attr[entries].tag = kSecCreatorItemAttr;
155116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    search_attributes_.attr[entries].length = sizeof(*creator);
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    search_attributes_.attr[entries].data =
157116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch        const_cast<void*>(static_cast<const void*>(creator));
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ++entries;
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  search_attributes_.count = entries;
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void KeychainSearch::FindMatchingItems(std::vector<SecKeychainItemRef>* items) {
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  OSStatus result = keychain_->SearchCreateFromAttributes(
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NULL, kSecInternetPasswordItemClass, &search_attributes_, &search_ref_);
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (result != noErr) {
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    OSSTATUS_LOG(ERROR, result) << "Keychain lookup failed";
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SecKeychainItemRef keychain_item;
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while (keychain_->SearchCopyNext(search_ref_, &keychain_item) == noErr) {
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Consumer is responsible for freeing the items.
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    items->push_back(keychain_item);
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  keychain_->Free(search_ref_);
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  search_ref_ = NULL;
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1826e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)PasswordStoreChangeList FormsToRemoveChangeList(
1836e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    const std::vector<PasswordForm*>& forms) {
1846e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  PasswordStoreChangeList changes;
1856e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  for (std::vector<PasswordForm*>::const_iterator i = forms.begin();
1866e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)       i != forms.end(); ++i) {
1876e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    changes.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE, **i));
1886e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  }
1896e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  return changes;
1906e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)}
1916e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1926e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)}  // namespace
1936e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#pragma mark -
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// TODO(stuartmorgan): Convert most of this to private helpers in
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// MacKeychainPasswordFormAdapter once it has sufficient higher-level public
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// methods to provide test coverage.
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace internal_keychain_helpers {
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Returns a URL built from the given components. To create a URL without a
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// port, pass kAnyPort for the |port| parameter.
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)GURL URLFromComponents(bool is_secure, const std::string& host, int port,
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       const std::string& path) {
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GURL::Replacements url_components;
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string scheme(is_secure ? "https" : "http");
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  url_components.SetSchemeStr(scheme);
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  url_components.SetHostStr(host);
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string port_string;  // Must remain in scope until after we do replacing.
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (port != kAnyPort) {
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::ostringstream port_stringstream;
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    port_stringstream << port;
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    port_string = port_stringstream.str();
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    url_components.SetPortStr(port_string);
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  url_components.SetPathStr(path);
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GURL url("http://dummy.com");  // ReplaceComponents needs a valid URL.
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return url.ReplaceComponents(url_components);
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Converts a Keychain time string to a Time object, returning true if
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// time_string_bytes was parsable. If the return value is false, the value of
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// |time| is unchanged.
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool TimeFromKeychainTimeString(const char* time_string_bytes,
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                unsigned int byte_length,
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                base::Time* time) {
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(time);
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  char* time_string = static_cast<char*>(malloc(byte_length + 1));
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  memcpy(time_string, time_string_bytes, byte_length);
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  time_string[byte_length] = '\0';
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::Time::Exploded exploded_time;
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bzero(&exploded_time, sizeof(exploded_time));
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The time string is of the form "yyyyMMddHHmmss'Z", in UTC time.
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int assignments = sscanf(time_string, "%4d%2d%2d%2d%2d%2dZ",
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           &exploded_time.year, &exploded_time.month,
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           &exploded_time.day_of_month, &exploded_time.hour,
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           &exploded_time.minute, &exploded_time.second);
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  free(time_string);
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (assignments == 6) {
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    *time = base::Time::FromUTCExploded(exploded_time);
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return true;
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return false;
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Returns the PasswordForm Scheme corresponding to |auth_type|.
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)PasswordForm::Scheme SchemeForAuthType(SecAuthenticationType auth_type) {
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (auth_type) {
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case kSecAuthenticationTypeHTMLForm:   return PasswordForm::SCHEME_HTML;
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case kSecAuthenticationTypeHTTPBasic:  return PasswordForm::SCHEME_BASIC;
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case kSecAuthenticationTypeHTTPDigest: return PasswordForm::SCHEME_DIGEST;
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:                               return PasswordForm::SCHEME_OTHER;
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool FillPasswordFormFromKeychainItem(const AppleKeychain& keychain,
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                      const SecKeychainItemRef& keychain_item,
2614e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                                      PasswordForm* form,
2624e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                                      bool extract_password_data) {
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(form);
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SecKeychainAttributeInfo attrInfo;
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UInt32 tags[] = { kSecAccountItemAttr,
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    kSecServerItemAttr,
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    kSecPortItemAttr,
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    kSecPathItemAttr,
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    kSecProtocolItemAttr,
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    kSecAuthenticationTypeItemAttr,
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    kSecSecurityDomainItemAttr,
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    kSecCreationDateItemAttr,
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    kSecNegativeItemAttr };
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  attrInfo.count = arraysize(tags);
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  attrInfo.tag = tags;
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  attrInfo.format = NULL;
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SecKeychainAttributeList *attrList;
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UInt32 password_length;
2814e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
2824e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // If |extract_password_data| is false, do not pass in a reference to
2834e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // |password_data|. ItemCopyAttributesAndData will then extract only the
2844e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // attributes of |keychain_item| (doesn't require OS authorization), and not
2854e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // attempt to extract its password data (requires OS authorization).
2864e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  void* password_data = NULL;
2874e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  void** password_data_ref = extract_password_data ? &password_data : NULL;
2884e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  OSStatus result = keychain.ItemCopyAttributesAndData(keychain_item, &attrInfo,
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                       NULL, &attrList,
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                       &password_length,
2924e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                                                       password_data_ref);
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (result != noErr) {
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // We don't log errSecAuthFailed because that just means that the user
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // chose not to allow us access to the item.
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (result != errSecAuthFailed) {
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      OSSTATUS_LOG(ERROR, result) << "Keychain data load failed";
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3034e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  if (extract_password_data) {
3045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    base::UTF8ToUTF16(static_cast<const char *>(password_data), password_length,
3055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                      &(form->password_value));
3064e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  }
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int port = kAnyPort;
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string server;
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string security_domain;
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string path;
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (unsigned int i = 0; i < attrList->count; i++) {
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SecKeychainAttribute attr = attrList->attr[i];
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!attr.data) {
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      continue;
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    switch (attr.tag) {
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case kSecAccountItemAttr:
3195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        base::UTF8ToUTF16(static_cast<const char *>(attr.data), attr.length,
3205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                          &(form->username_value));
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case kSecServerItemAttr:
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        server.assign(static_cast<const char *>(attr.data), attr.length);
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case kSecPortItemAttr:
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        port = *(static_cast<UInt32*>(attr.data));
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case kSecPathItemAttr:
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        path.assign(static_cast<const char *>(attr.data), attr.length);
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case kSecProtocolItemAttr:
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      {
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        SecProtocolType protocol = *(static_cast<SecProtocolType*>(attr.data));
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // TODO(stuartmorgan): Handle proxy types
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        form->ssl_valid = (protocol == kSecProtocolTypeHTTPS);
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case kSecAuthenticationTypeItemAttr:
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      {
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        SecAuthenticationType auth_type =
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            *(static_cast<SecAuthenticationType*>(attr.data));
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        form->scheme = SchemeForAuthType(auth_type);
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case kSecSecurityDomainItemAttr:
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        security_domain.assign(static_cast<const char *>(attr.data),
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                               attr.length);
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case kSecCreationDateItemAttr:
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // The only way to get a date out of Keychain is as a string. Really.
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // (The docs claim it's an int, but the header is correct.)
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        TimeFromKeychainTimeString(static_cast<char*>(attr.data), attr.length,
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                   &form->date_created);
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case kSecNegativeItemAttr:
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        Boolean negative_item = *(static_cast<Boolean*>(attr.data));
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (negative_item) {
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          form->blacklisted_by_user = true;
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  keychain.ItemFreeAttributesAndData(attrList, password_data);
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // kSecNegativeItemAttr doesn't seem to actually be in widespread use. In
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // practice, other browsers seem to use a "" or " " password (and a special
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // user name) to indicated blacklist entries.
3684e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  if (extract_password_data && (form->password_value.empty() ||
3694e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                                EqualsASCII(form->password_value, " "))) {
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    form->blacklisted_by_user = true;
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  form->origin = URLFromComponents(form->ssl_valid, server, port, path);
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // TODO(stuartmorgan): Handle proxies, which need a different signon_realm
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // format.
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  form->signon_realm = form->origin.GetOrigin().spec();
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (form->scheme != PasswordForm::SCHEME_HTML) {
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    form->signon_realm.append(security_domain);
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool FormsMatchForMerge(const PasswordForm& form_a,
3845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        const PasswordForm& form_b,
3855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        FormMatchStrictness strictness) {
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We never merge blacklist entries between our store and the keychain.
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (form_a.blacklisted_by_user || form_b.blacklisted_by_user) {
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  bool equal_realm = form_a.signon_realm == form_b.signon_realm;
3915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (strictness == FUZZY_FORM_MATCH) {
3925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    equal_realm |= (!form_a.original_signon_realm.empty()) &&
3935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                   form_a.original_signon_realm == form_b.signon_realm;
3945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
3955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return form_a.scheme == form_b.scheme && equal_realm &&
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         form_a.username_value == form_b.username_value;
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Returns an the best match for |base_form| from |keychain_forms|, or NULL if
4005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// there is no suitable match.
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)PasswordForm* BestKeychainFormForForm(
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const PasswordForm& base_form,
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::vector<PasswordForm*>* keychain_forms) {
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PasswordForm* partial_match = NULL;
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (std::vector<PasswordForm*>::const_iterator i = keychain_forms->begin();
4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       i != keychain_forms->end(); ++i) {
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // TODO(stuartmorgan): We should really be scoring path matches and picking
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // the best, rather than just checking exact-or-not (although in practice
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // keychain items with paths probably came from us).
4105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (FormsMatchForMerge(base_form, *(*i), FUZZY_FORM_MATCH)) {
4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (base_form.origin == (*i)->origin) {
4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return *i;
4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else if (!partial_match) {
4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        partial_match = *i;
4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return partial_match;
4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Returns entries from |forms| that are blacklist entries, after removing
4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// them from |forms|.
4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)std::vector<PasswordForm*> ExtractBlacklistForms(
4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::vector<PasswordForm*>* forms) {
4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::vector<PasswordForm*> blacklist_forms;
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (std::vector<PasswordForm*>::iterator i = forms->begin();
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       i != forms->end();) {
4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    PasswordForm* form = *i;
4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (form->blacklisted_by_user) {
4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      blacklist_forms.push_back(form);
4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      i = forms->erase(i);
4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ++i;
4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return blacklist_forms;
4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Deletes and removes from v any element that exists in s.
4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)template <class T>
4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void DeleteVectorElementsInSet(std::vector<T*>* v, const std::set<T*>& s) {
4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (typename std::vector<T*>::iterator i = v->begin(); i != v->end();) {
4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    T* element = *i;
4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (s.find(element) != s.end()) {
4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      delete element;
4465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      i = v->erase(i);
4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ++i;
4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void MergePasswordForms(std::vector<PasswordForm*>* keychain_forms,
4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        std::vector<PasswordForm*>* database_forms,
4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        std::vector<PasswordForm*>* merged_forms) {
4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Pull out the database blacklist items, since they are used as-is rather
4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // than being merged with keychain forms.
4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::vector<PasswordForm*> database_blacklist_forms =
4595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ExtractBlacklistForms(database_forms);
4605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Merge the normal entries.
4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::set<PasswordForm*> used_keychain_forms;
4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (std::vector<PasswordForm*>::iterator i = database_forms->begin();
4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       i != database_forms->end();) {
4655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    PasswordForm* db_form = *i;
4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    PasswordForm* best_match = BestKeychainFormForForm(*db_form,
4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                       keychain_forms);
4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (best_match) {
4695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      used_keychain_forms.insert(best_match);
4705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      db_form->password_value = best_match->password_value;
4715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      merged_forms->push_back(db_form);
4725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      i = database_forms->erase(i);
4735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
4745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ++i;
4755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Add in the blacklist entries from the database.
4795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  merged_forms->insert(merged_forms->end(),
4805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       database_blacklist_forms.begin(),
4815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       database_blacklist_forms.end());
4825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Clear out all the Keychain entries we used.
4845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DeleteVectorElementsInSet(keychain_forms, used_keychain_forms);
4855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4874e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)std::vector<ItemFormPair> ExtractAllKeychainItemAttributesIntoPasswordForms(
4884e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    std::vector<SecKeychainItemRef>* keychain_items,
4894e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    const AppleKeychain& keychain) {
4904e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  DCHECK(keychain_items);
4914e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  MacKeychainPasswordFormAdapter keychain_adapter(&keychain);
4924e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  *keychain_items = keychain_adapter.GetAllPasswordFormKeychainItems();
4934e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  std::vector<ItemFormPair> item_form_pairs;
4944e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  for (std::vector<SecKeychainItemRef>::iterator i = keychain_items->begin();
4954e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)       i != keychain_items->end(); ++i) {
4964e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    PasswordForm* form_without_password = new PasswordForm();
4974e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    internal_keychain_helpers::FillPasswordFormFromKeychainItem(
4984e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        keychain,
4994e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        *i,
5004e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        form_without_password,
5014e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        false);  // Load password attributes, but not password data.
5024e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    item_form_pairs.push_back(std::make_pair(&(*i), form_without_password));
5034e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  }
5044e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  return item_form_pairs;
5054e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
5064e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
5075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)std::vector<PasswordForm*> GetPasswordsForForms(
5085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const AppleKeychain& keychain,
5095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::vector<PasswordForm*>* database_forms) {
5104e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // First load the attributes of all items in the keychain without loading
5114e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // their password data, and then match items in |database_forms| against them.
5124e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // This avoids individually searching through the keychain for passwords
5134e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // matching each form in |database_forms|, and results in a significant
5144e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // performance gain, replacing O(N) keychain search operations with a single
5154e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // operation that loads all keychain items, and then selective reads of only
5164e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // the relevant passwords. See crbug.com/263685.
5174e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  std::vector<SecKeychainItemRef> keychain_items;
5184e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  std::vector<ItemFormPair> item_form_pairs =
5194e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      ExtractAllKeychainItemAttributesIntoPasswordForms(&keychain_items,
5204e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                                                        keychain);
5214e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
5224e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // Next, compare the attributes of the PasswordForms in |database_forms|
5234e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // against those in |item_form_pairs|, and extract password data for each
5244e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // matching PasswordForm using its corresponding SecKeychainItemRef.
5255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::vector<PasswordForm*> merged_forms;
5265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (std::vector<PasswordForm*>::iterator i = database_forms->begin();
5275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       i != database_forms->end();) {
5285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::vector<PasswordForm*> db_form_container(1, *i);
5295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::vector<PasswordForm*> keychain_matches =
5304e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        ExtractPasswordsMergeableWithForm(keychain, item_form_pairs, **i);
5315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    MergePasswordForms(&keychain_matches, &db_form_container, &merged_forms);
5325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (db_form_container.empty()) {
5335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      i = database_forms->erase(i);
5345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
5355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ++i;
5365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
5375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    STLDeleteElements(&keychain_matches);
5385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5394e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
5404e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // Clean up temporary PasswordForms and SecKeychainItemRefs.
5414e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  STLDeleteContainerPairSecondPointers(item_form_pairs.begin(),
5424e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                                       item_form_pairs.end());
5434e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  for (std::vector<SecKeychainItemRef>::iterator i = keychain_items.begin();
5444e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)       i != keychain_items.end(); ++i) {
5454e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    keychain.Free(*i);
5464e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  }
5475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return merged_forms;
5485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5504e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// TODO(stuartmorgan): signon_realm for proxies is not yet supported.
551116680a4aac90f2aa7413d9095a592090648e557Ben Murdochbool ExtractSignonRealmComponents(const std::string& signon_realm,
552116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                                  std::string* server,
553116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                                  UInt32* port,
554116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                                  bool* is_secure,
555116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                                  std::string* security_domain) {
5564e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // The signon_realm will be the Origin portion of a URL for an HTML form,
5574e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // and the same but with the security domain as a path for HTTP auth.
5584e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  GURL realm_as_url(signon_realm);
5594e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  if (!realm_as_url.is_valid()) {
5604e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    return false;
561d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  }
5624e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
5634e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  if (server)
5644e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    *server = realm_as_url.host();
5654e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  if (is_secure)
5664e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    *is_secure = realm_as_url.SchemeIsSecure();
5674e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  if (port)
5684e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    *port = realm_as_url.has_port() ? atoi(realm_as_url.port().c_str()) : 0;
5694e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  if (security_domain) {
5704e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    // Strip the leading '/' off of the path to get the security domain.
5714e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    if (realm_as_url.path().length() > 0)
5724e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      *security_domain = realm_as_url.path().substr(1);
5734e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    else
5744e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      security_domain->clear();
5754e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  }
5764e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  return true;
5774e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
5784e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
5794e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)bool FormIsValidAndMatchesOtherForm(const PasswordForm& query_form,
5804e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                                    const PasswordForm& other_form) {
5814e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  std::string server;
5824e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  std::string security_domain;
583116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  UInt32 port;
5844e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  bool is_secure;
5854e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  if (!ExtractSignonRealmComponents(query_form.signon_realm, &server, &port,
5864e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                                    &is_secure, &security_domain)) {
5874e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    return false;
5884e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  }
5895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return internal_keychain_helpers::FormsMatchForMerge(
5905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      query_form, other_form, STRICT_FORM_MATCH);
5914e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
5924e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
5934e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)std::vector<PasswordForm*> ExtractPasswordsMergeableWithForm(
5944e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    const AppleKeychain& keychain,
5954e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    const std::vector<ItemFormPair>& item_form_pairs,
5964e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    const PasswordForm& query_form) {
5974e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  std::vector<PasswordForm*> matches;
5984e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  for (std::vector<ItemFormPair>::const_iterator i = item_form_pairs.begin();
5994e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)       i != item_form_pairs.end(); ++i) {
6004e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    if (FormIsValidAndMatchesOtherForm(query_form, *(i->second))) {
6014e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      // Create a new object, since the caller is responsible for deleting the
6024e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      // returned forms.
6034e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      scoped_ptr<PasswordForm> form_with_password(new PasswordForm());
6044e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      internal_keychain_helpers::FillPasswordFormFromKeychainItem(
6054e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)          keychain,
6064e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)          *(i->first),
6074e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)          form_with_password.get(),
6084e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)          true);  // Load password attributes and data.
6094e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      // Do not include blacklisted items found in the keychain.
6104e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      if (!form_with_password->blacklisted_by_user)
6114e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        matches.push_back(form_with_password.release());
6124e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    }
6134e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  }
6144e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  return matches;
6154e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
616d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
6175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace internal_keychain_helpers
6185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#pragma mark -
6205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)MacKeychainPasswordFormAdapter::MacKeychainPasswordFormAdapter(
6225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const AppleKeychain* keychain)
6235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : keychain_(keychain), finds_only_owned_(false) {
6245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)std::vector<PasswordForm*> MacKeychainPasswordFormAdapter::PasswordsFillingForm(
6275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const std::string& signon_realm,
6285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    PasswordForm::Scheme scheme) {
6295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::vector<SecKeychainItemRef> keychain_items =
6305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      MatchingKeychainItems(signon_realm, scheme, NULL, NULL);
6315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return ConvertKeychainItemsToForms(&keychain_items);
6335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6356e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)bool MacKeychainPasswordFormAdapter::HasPasswordExactlyMatchingForm(
6365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const PasswordForm& query_form) {
6375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SecKeychainItemRef keychain_item = KeychainItemForForm(query_form);
6385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (keychain_item) {
6395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    keychain_->Free(keychain_item);
6406e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    return true;
6415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6426e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  return false;
6435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool MacKeychainPasswordFormAdapter::HasPasswordsMergeableWithForm(
6465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const PasswordForm& query_form) {
6475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  std::string username = base::UTF16ToUTF8(query_form.username_value);
6485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::vector<SecKeychainItemRef> matches =
6495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      MatchingKeychainItems(query_form.signon_realm, query_form.scheme,
6505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                            NULL, username.c_str());
6515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (std::vector<SecKeychainItemRef>::iterator i = matches.begin();
6525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       i != matches.end(); ++i) {
6535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    keychain_->Free(*i);
6545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return !matches.empty();
6575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6594e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)std::vector<SecKeychainItemRef>
6604e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    MacKeychainPasswordFormAdapter::GetAllPasswordFormKeychainItems() {
6615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SecAuthenticationType supported_auth_types[] = {
6625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    kSecAuthenticationTypeHTMLForm,
6635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    kSecAuthenticationTypeHTTPBasic,
6645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    kSecAuthenticationTypeHTTPDigest,
6655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
6665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::vector<SecKeychainItemRef> matches;
6685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (unsigned int i = 0; i < arraysize(supported_auth_types); ++i) {
6695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    KeychainSearch keychain_search(*keychain_);
670116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    OSType creator = CreatorCodeForSearch();
671116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    keychain_search.Init(NULL,
672116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                         NULL,
673116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                         NULL,
674116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                         &supported_auth_types[i],
675116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                         NULL,
676116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                         NULL,
677116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                         NULL,
678116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                         creator ? &creator : NULL);
6795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    keychain_search.FindMatchingItems(&matches);
6805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6814e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  return matches;
6824e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
6835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6844e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)std::vector<PasswordForm*>
6854e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    MacKeychainPasswordFormAdapter::GetAllPasswordFormPasswords() {
6864e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  std::vector<SecKeychainItemRef> items = GetAllPasswordFormKeychainItems();
6874e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  return ConvertKeychainItemsToForms(&items);
6885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool MacKeychainPasswordFormAdapter::AddPassword(const PasswordForm& form) {
6915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We should never be trying to store a blacklist in the keychain.
6925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!form.blacklisted_by_user);
6935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string server;
6955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string security_domain;
696116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  UInt32 port;
6975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool is_secure;
6984e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  if (!internal_keychain_helpers::ExtractSignonRealmComponents(
6994e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)           form.signon_realm, &server, &port, &is_secure, &security_domain)) {
7005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
7015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  std::string username = base::UTF16ToUTF8(form.username_value);
7035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  std::string password = base::UTF16ToUTF8(form.password_value);
7045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string path = form.origin.path();
7055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SecProtocolType protocol = is_secure ? kSecProtocolTypeHTTPS
7065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                       : kSecProtocolTypeHTTP;
7075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SecKeychainItemRef new_item = NULL;
7085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  OSStatus result = keychain_->AddInternetPassword(
7095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NULL, server.size(), server.c_str(),
7105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      security_domain.size(), security_domain.c_str(),
7115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      username.size(), username.c_str(),
7125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      path.size(), path.c_str(),
7135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      port, protocol, AuthTypeForScheme(form.scheme),
7145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      password.size(), password.c_str(), &new_item);
7155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (result == noErr) {
7175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SetKeychainItemCreatorCode(new_item,
7185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                               base::mac::CreatorCodeForApplication());
7195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    keychain_->Free(new_item);
7205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else if (result == errSecDuplicateItem) {
7215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // If we collide with an existing item, find and update it instead.
7225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SecKeychainItemRef existing_item = KeychainItemForForm(form);
7235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!existing_item) {
7245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
7255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
7265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    bool changed = SetKeychainItemPassword(existing_item, password);
7275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    keychain_->Free(existing_item);
7285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return changed;
7295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return result == noErr;
7325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool MacKeychainPasswordFormAdapter::RemovePassword(const PasswordForm& form) {
7355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SecKeychainItemRef keychain_item = KeychainItemForForm(form);
7365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (keychain_item == NULL)
7375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
7385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  OSStatus result = keychain_->ItemDelete(keychain_item);
7395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  keychain_->Free(keychain_item);
7405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return result == noErr;
7415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void MacKeychainPasswordFormAdapter::SetFindsOnlyOwnedItems(
7445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    bool finds_only_owned) {
7455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  finds_only_owned_ = finds_only_owned;
7465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)std::vector<PasswordForm*>
7495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    MacKeychainPasswordFormAdapter::ConvertKeychainItemsToForms(
7505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        std::vector<SecKeychainItemRef>* items) {
7515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::vector<PasswordForm*> keychain_forms;
7525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (std::vector<SecKeychainItemRef>::const_iterator i = items->begin();
7535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       i != items->end(); ++i) {
7545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    PasswordForm* form = new PasswordForm();
7554e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    if (internal_keychain_helpers::FillPasswordFormFromKeychainItem(
7564e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)            *keychain_, *i, form, true)) {
7575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      keychain_forms.push_back(form);
7585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
7595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    keychain_->Free(*i);
7605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  items->clear();
7625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return keychain_forms;
7635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SecKeychainItemRef MacKeychainPasswordFormAdapter::KeychainItemForForm(
7665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const PasswordForm& form) {
7675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We don't store blacklist entries in the keychain, so the answer to "what
7685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Keychain item goes with this form" is always "nothing" for blacklists.
7695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (form.blacklisted_by_user) {
7705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NULL;
7715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string path = form.origin.path();
7745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  std::string username = base::UTF16ToUTF8(form.username_value);
7755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::vector<SecKeychainItemRef> matches = MatchingKeychainItems(
7765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      form.signon_realm, form.scheme, path.c_str(), username.c_str());
7775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (matches.empty()) {
7795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NULL;
7805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Free all items after the first, since we won't be returning them.
7825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (std::vector<SecKeychainItemRef>::iterator i = matches.begin() + 1;
7835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       i != matches.end(); ++i) {
7845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    keychain_->Free(*i);
7855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return matches[0];
7875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)std::vector<SecKeychainItemRef>
7905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    MacKeychainPasswordFormAdapter::MatchingKeychainItems(
7915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        const std::string& signon_realm,
79258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        autofill::PasswordForm::Scheme scheme,
7935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        const char* path, const char* username) {
7945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::vector<SecKeychainItemRef> matches;
7955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string server;
7975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string security_domain;
798116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  UInt32 port;
7995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool is_secure;
8004e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  if (!internal_keychain_helpers::ExtractSignonRealmComponents(
8014e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)           signon_realm, &server, &port, &is_secure, &security_domain)) {
8025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // TODO(stuartmorgan): Proxies will currently fail here, since their
8035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // signon_realm is not a URL. We need to detect the proxy case and handle
8045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // it specially.
8055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return matches;
8065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
8075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SecProtocolType protocol = is_secure ? kSecProtocolTypeHTTPS
8085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                       : kSecProtocolTypeHTTP;
8095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SecAuthenticationType auth_type = AuthTypeForScheme(scheme);
8105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const char* auth_domain = (scheme == PasswordForm::SCHEME_HTML) ?
8115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NULL : security_domain.c_str();
812116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  OSType creator = CreatorCodeForSearch();
8135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  KeychainSearch keychain_search(*keychain_);
814116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  keychain_search.Init(server.c_str(),
815116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                       &port,
816116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                       &protocol,
817116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                       &auth_type,
818116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                       auth_domain,
819116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                       path,
820116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                       username,
821116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                       creator ? &creator : NULL);
8225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  keychain_search.FindMatchingItems(&matches);
8235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return matches;
8245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Returns the Keychain SecAuthenticationType type corresponding to |scheme|.
8275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SecAuthenticationType MacKeychainPasswordFormAdapter::AuthTypeForScheme(
8285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    PasswordForm::Scheme scheme) {
8295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (scheme) {
8305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case PasswordForm::SCHEME_HTML:   return kSecAuthenticationTypeHTMLForm;
8315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case PasswordForm::SCHEME_BASIC:  return kSecAuthenticationTypeHTTPBasic;
8325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case PasswordForm::SCHEME_DIGEST: return kSecAuthenticationTypeHTTPDigest;
8335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case PasswordForm::SCHEME_OTHER:  return kSecAuthenticationTypeDefault;
8345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
8355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  NOTREACHED();
8365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return kSecAuthenticationTypeDefault;
8375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool MacKeychainPasswordFormAdapter::SetKeychainItemPassword(
8405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const SecKeychainItemRef& keychain_item, const std::string& password) {
8415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  OSStatus result = keychain_->ItemModifyAttributesAndData(keychain_item, NULL,
8425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                           password.size(),
8435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                           password.c_str());
8445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return result == noErr;
8455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool MacKeychainPasswordFormAdapter::SetKeychainItemCreatorCode(
8485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const SecKeychainItemRef& keychain_item, OSType creator_code) {
8495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SecKeychainAttribute attr = { kSecCreatorItemAttr, sizeof(creator_code),
8505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                &creator_code };
8515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SecKeychainAttributeList attrList = { 1, &attr };
8525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  OSStatus result = keychain_->ItemModifyAttributesAndData(keychain_item,
8535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                           &attrList, 0, NULL);
8545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return result == noErr;
8555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)OSType MacKeychainPasswordFormAdapter::CreatorCodeForSearch() {
8585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return finds_only_owned_ ? base::mac::CreatorCodeForApplication() : 0;
8595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#pragma mark -
8625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)PasswordStoreMac::PasswordStoreMac(
8645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    scoped_refptr<base::SingleThreadTaskRunner> main_thread_runner,
8655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    scoped_refptr<base::SingleThreadTaskRunner> db_thread_runner,
8665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    AppleKeychain* keychain,
867c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    password_manager::LoginDatabase* login_db)
868c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    : password_manager::PasswordStore(main_thread_runner, db_thread_runner),
8695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      keychain_(keychain),
8705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      login_metadata_db_(login_db) {
8715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(keychain_.get());
8725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(login_metadata_db_.get());
8735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)PasswordStoreMac::~PasswordStoreMac() {}
8765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
877a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)bool PasswordStoreMac::Init(
878116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    const syncer::SyncableService::StartSyncFlare& flare,
879116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    const std::string& sync_username) {
880effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
8814e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  thread_.reset(new base::Thread("Chrome_PasswordStore_Thread"));
8825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!thread_->Start()) {
8845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    thread_.reset(NULL);
8855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
8865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
887116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  return password_manager::PasswordStore::Init(flare, sync_username);
8885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
890effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochvoid PasswordStoreMac::Shutdown() {
891effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
892c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  password_manager::PasswordStore::Shutdown();
893effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  thread_->Stop();
894effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch}
895effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
8962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Mac stores passwords in the system keychain, which can block for an
8972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// arbitrarily long time (most notably, it can block on user confirmation
8985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// from a dialog). Run tasks on a dedicated thread to avoid blocking the DB
8995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// thread.
9005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)scoped_refptr<base::SingleThreadTaskRunner>
9015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)PasswordStoreMac::GetBackgroundTaskRunner() {
9025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return (thread_.get()) ? thread_->message_loop_proxy() : NULL;
9035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
9045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
905116680a4aac90f2aa7413d9095a592090648e557Ben Murdochvoid PasswordStoreMac::ReportMetricsImpl(const std::string& sync_username) {
906116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  login_metadata_db_->ReportMetrics(sync_username);
9075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
9085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)PasswordStoreChangeList PasswordStoreMac::AddLoginImpl(
9105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const PasswordForm& form) {
911010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  DCHECK(thread_->message_loop() == base::MessageLoop::current());
9125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  PasswordStoreChangeList changes;
9135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (AddToKeychainIfNecessary(form)) {
914cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    changes = login_metadata_db_->AddLogin(form);
9155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
9165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return changes;
9175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
9185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)PasswordStoreChangeList PasswordStoreMac::UpdateLoginImpl(
9205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const PasswordForm& form) {
921010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  DCHECK(thread_->message_loop() == base::MessageLoop::current());
922cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  PasswordStoreChangeList changes = login_metadata_db_->UpdateLogin(form);
9235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MacKeychainPasswordFormAdapter keychain_adapter(keychain_.get());
925cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  if (changes.empty() &&
9265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      !keychain_adapter.HasPasswordsMergeableWithForm(form)) {
9275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // If the password isn't in either the DB or the keychain, then it must have
9285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // been deleted after autofill happened, and should not be re-added.
9295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return changes;
9305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
9315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The keychain add will update if there is a collision and add if there
9335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // isn't, which is the behavior we want, so there's no separate update call.
934cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  if (AddToKeychainIfNecessary(form) && changes.empty()) {
935cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    changes = login_metadata_db_->AddLogin(form);
9365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
9375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return changes;
9385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
9395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)PasswordStoreChangeList PasswordStoreMac::RemoveLoginImpl(
9415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const PasswordForm& form) {
942010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  DCHECK(thread_->message_loop() == base::MessageLoop::current());
9435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  PasswordStoreChangeList changes;
9445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (login_metadata_db_->RemoveLogin(form)) {
9455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // See if we own a Keychain item associated with this item. We can do an
9465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // exact search rather than messing around with trying to do fuzzy matching
9475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // because passwords that we created will always have an exact-match
9485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // database entry.
9495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // (If a user does lose their profile but not their keychain we'll treat the
9505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // entries we find like other imported entries anyway, so it's reasonable to
9515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // handle deletes on them the way we would for an imported item.)
9525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_.get());
9535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    owned_keychain_adapter.SetFindsOnlyOwnedItems(true);
9546e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    if (owned_keychain_adapter.HasPasswordExactlyMatchingForm(form)) {
9555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // If we don't have other forms using it (i.e., a form differing only by
9565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // the names of the form elements), delete the keychain entry.
9575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!DatabaseHasFormMatchingKeychainForm(form)) {
9585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        owned_keychain_adapter.RemovePassword(form);
9595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
9605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
9615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    changes.push_back(PasswordStoreChang