1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/password_manager/password_store_mac.h"
6 #include "chrome/browser/password_manager/password_store_mac_internal.h"
7
8 #include <CoreServices/CoreServices.h>
9 #include <set>
10 #include <string>
11 #include <utility>
12 #include <vector>
13
14 #include "base/callback.h"
15 #include "base/logging.h"
16 #include "base/mac/mac_logging.h"
17 #include "base/mac/mac_util.h"
18 #include "base/message_loop/message_loop.h"
19 #include "base/stl_util.h"
20 #include "base/strings/string_util.h"
21 #include "base/strings/utf_string_conversions.h"
22 #include "chrome/browser/mac/security_wrappers.h"
23 #include "components/password_manager/core/browser/login_database.h"
24 #include "components/password_manager/core/browser/password_store_change.h"
25 #include "content/public/browser/browser_thread.h"
26 #include "crypto/apple_keychain.h"
27
28 using autofill::PasswordForm;
29 using crypto::AppleKeychain;
30 using password_manager::PasswordStoreChange;
31 using password_manager::PasswordStoreChangeList;
32
33 // Utility class to handle the details of constructing and running a keychain
34 // search from a set of attributes.
35 class KeychainSearch {
36 public:
37 explicit KeychainSearch(const AppleKeychain& keychain);
38 ~KeychainSearch();
39
40 // Sets up a keycahin search based on an non "null" (NULL for char*,
41 // The appropriate "Any" entry for other types) arguments.
42 //
43 // IMPORTANT: Any paramaters passed in *must* remain valid for as long as the
44 // KeychainSearch object, since the search uses them by reference.
45 void Init(const char* server, const UInt32& port,
46 const SecProtocolType& protocol,
47 const SecAuthenticationType& auth_type, const char* security_domain,
48 const char* path, const char* username, OSType creator);
49
50 // Fills |items| with all Keychain items that match the Init'd search.
51 // If the search fails for any reason, |items| will be unchanged.
52 void FindMatchingItems(std::vector<SecKeychainItemRef>* matches);
53
54 private:
55 const AppleKeychain* keychain_;
56 SecKeychainAttributeList search_attributes_;
57 SecKeychainSearchRef search_ref_;
58 };
59
KeychainSearch(const AppleKeychain & keychain)60 KeychainSearch::KeychainSearch(const AppleKeychain& keychain)
61 : keychain_(&keychain), search_ref_(NULL) {
62 search_attributes_.count = 0;
63 search_attributes_.attr = NULL;
64 }
65
~KeychainSearch()66 KeychainSearch::~KeychainSearch() {
67 if (search_attributes_.attr) {
68 free(search_attributes_.attr);
69 }
70 }
71
Init(const char * server,const UInt32 & port,const SecProtocolType & protocol,const SecAuthenticationType & auth_type,const char * security_domain,const char * path,const char * username,OSType creator)72 void KeychainSearch::Init(const char* server, const UInt32& port,
73 const SecProtocolType& protocol,
74 const SecAuthenticationType& auth_type,
75 const char* security_domain, const char* path,
76 const char* username, OSType creator) {
77 // Allocate enough to hold everything we might use.
78 const unsigned int kMaxEntryCount = 8;
79 search_attributes_.attr =
80 static_cast<SecKeychainAttribute*>(calloc(kMaxEntryCount,
81 sizeof(SecKeychainAttribute)));
82 unsigned int entries = 0;
83 // We only use search_attributes_ with SearchCreateFromAttributes, which takes
84 // a "const SecKeychainAttributeList *", so we trust that they won't try
85 // to modify the list, and that casting away const-ness is thus safe.
86 if (server != NULL) {
87 DCHECK_LT(entries, kMaxEntryCount);
88 search_attributes_.attr[entries].tag = kSecServerItemAttr;
89 search_attributes_.attr[entries].length = strlen(server);
90 search_attributes_.attr[entries].data =
91 const_cast<void*>(reinterpret_cast<const void*>(server));
92 ++entries;
93 }
94 if (port != kAnyPort) {
95 DCHECK_LE(entries, kMaxEntryCount);
96 search_attributes_.attr[entries].tag = kSecPortItemAttr;
97 search_attributes_.attr[entries].length = sizeof(port);
98 search_attributes_.attr[entries].data =
99 const_cast<void*>(reinterpret_cast<const void*>(&port));
100 ++entries;
101 }
102 if (protocol != kSecProtocolTypeAny) {
103 DCHECK_LE(entries, kMaxEntryCount);
104 search_attributes_.attr[entries].tag = kSecProtocolItemAttr;
105 search_attributes_.attr[entries].length = sizeof(protocol);
106 search_attributes_.attr[entries].data =
107 const_cast<void*>(reinterpret_cast<const void*>(&protocol));
108 ++entries;
109 }
110 if (auth_type != kSecAuthenticationTypeAny) {
111 DCHECK_LE(entries, kMaxEntryCount);
112 search_attributes_.attr[entries].tag = kSecAuthenticationTypeItemAttr;
113 search_attributes_.attr[entries].length = sizeof(auth_type);
114 search_attributes_.attr[entries].data =
115 const_cast<void*>(reinterpret_cast<const void*>(&auth_type));
116 ++entries;
117 }
118 if (security_domain != NULL && strlen(security_domain) > 0) {
119 DCHECK_LE(entries, kMaxEntryCount);
120 search_attributes_.attr[entries].tag = kSecSecurityDomainItemAttr;
121 search_attributes_.attr[entries].length = strlen(security_domain);
122 search_attributes_.attr[entries].data =
123 const_cast<void*>(reinterpret_cast<const void*>(security_domain));
124 ++entries;
125 }
126 if (path != NULL && strlen(path) > 0 && strcmp(path, "/") != 0) {
127 DCHECK_LE(entries, kMaxEntryCount);
128 search_attributes_.attr[entries].tag = kSecPathItemAttr;
129 search_attributes_.attr[entries].length = strlen(path);
130 search_attributes_.attr[entries].data =
131 const_cast<void*>(reinterpret_cast<const void*>(path));
132 ++entries;
133 }
134 if (username != NULL) {
135 DCHECK_LE(entries, kMaxEntryCount);
136 search_attributes_.attr[entries].tag = kSecAccountItemAttr;
137 search_attributes_.attr[entries].length = strlen(username);
138 search_attributes_.attr[entries].data =
139 const_cast<void*>(reinterpret_cast<const void*>(username));
140 ++entries;
141 }
142 if (creator != 0) {
143 DCHECK_LE(entries, kMaxEntryCount);
144 search_attributes_.attr[entries].tag = kSecCreatorItemAttr;
145 search_attributes_.attr[entries].length = sizeof(creator);
146 search_attributes_.attr[entries].data =
147 const_cast<void*>(reinterpret_cast<const void*>(&creator));
148 ++entries;
149 }
150 search_attributes_.count = entries;
151 }
152
FindMatchingItems(std::vector<SecKeychainItemRef> * items)153 void KeychainSearch::FindMatchingItems(std::vector<SecKeychainItemRef>* items) {
154 OSStatus result = keychain_->SearchCreateFromAttributes(
155 NULL, kSecInternetPasswordItemClass, &search_attributes_, &search_ref_);
156
157 if (result != noErr) {
158 OSSTATUS_LOG(ERROR, result) << "Keychain lookup failed";
159 return;
160 }
161
162 SecKeychainItemRef keychain_item;
163 while (keychain_->SearchCopyNext(search_ref_, &keychain_item) == noErr) {
164 // Consumer is responsible for freeing the items.
165 items->push_back(keychain_item);
166 }
167
168 keychain_->Free(search_ref_);
169 search_ref_ = NULL;
170 }
171
172 #pragma mark -
173
174 // TODO(stuartmorgan): Convert most of this to private helpers in
175 // MacKeychainPasswordFormAdapter once it has sufficient higher-level public
176 // methods to provide test coverage.
177 namespace internal_keychain_helpers {
178
179 // Returns a URL built from the given components. To create a URL without a
180 // port, pass kAnyPort for the |port| parameter.
URLFromComponents(bool is_secure,const std::string & host,int port,const std::string & path)181 GURL URLFromComponents(bool is_secure, const std::string& host, int port,
182 const std::string& path) {
183 GURL::Replacements url_components;
184 std::string scheme(is_secure ? "https" : "http");
185 url_components.SetSchemeStr(scheme);
186 url_components.SetHostStr(host);
187 std::string port_string; // Must remain in scope until after we do replacing.
188 if (port != kAnyPort) {
189 std::ostringstream port_stringstream;
190 port_stringstream << port;
191 port_string = port_stringstream.str();
192 url_components.SetPortStr(port_string);
193 }
194 url_components.SetPathStr(path);
195
196 GURL url("http://dummy.com"); // ReplaceComponents needs a valid URL.
197 return url.ReplaceComponents(url_components);
198 }
199
200 // Converts a Keychain time string to a Time object, returning true if
201 // time_string_bytes was parsable. If the return value is false, the value of
202 // |time| is unchanged.
TimeFromKeychainTimeString(const char * time_string_bytes,unsigned int byte_length,base::Time * time)203 bool TimeFromKeychainTimeString(const char* time_string_bytes,
204 unsigned int byte_length,
205 base::Time* time) {
206 DCHECK(time);
207
208 char* time_string = static_cast<char*>(malloc(byte_length + 1));
209 memcpy(time_string, time_string_bytes, byte_length);
210 time_string[byte_length] = '\0';
211 base::Time::Exploded exploded_time;
212 bzero(&exploded_time, sizeof(exploded_time));
213 // The time string is of the form "yyyyMMddHHmmss'Z", in UTC time.
214 int assignments = sscanf(time_string, "%4d%2d%2d%2d%2d%2dZ",
215 &exploded_time.year, &exploded_time.month,
216 &exploded_time.day_of_month, &exploded_time.hour,
217 &exploded_time.minute, &exploded_time.second);
218 free(time_string);
219
220 if (assignments == 6) {
221 *time = base::Time::FromUTCExploded(exploded_time);
222 return true;
223 }
224 return false;
225 }
226
227 // Returns the PasswordForm Scheme corresponding to |auth_type|.
SchemeForAuthType(SecAuthenticationType auth_type)228 PasswordForm::Scheme SchemeForAuthType(SecAuthenticationType auth_type) {
229 switch (auth_type) {
230 case kSecAuthenticationTypeHTMLForm: return PasswordForm::SCHEME_HTML;
231 case kSecAuthenticationTypeHTTPBasic: return PasswordForm::SCHEME_BASIC;
232 case kSecAuthenticationTypeHTTPDigest: return PasswordForm::SCHEME_DIGEST;
233 default: return PasswordForm::SCHEME_OTHER;
234 }
235 }
236
FillPasswordFormFromKeychainItem(const AppleKeychain & keychain,const SecKeychainItemRef & keychain_item,PasswordForm * form,bool extract_password_data)237 bool FillPasswordFormFromKeychainItem(const AppleKeychain& keychain,
238 const SecKeychainItemRef& keychain_item,
239 PasswordForm* form,
240 bool extract_password_data) {
241 DCHECK(form);
242
243 SecKeychainAttributeInfo attrInfo;
244 UInt32 tags[] = { kSecAccountItemAttr,
245 kSecServerItemAttr,
246 kSecPortItemAttr,
247 kSecPathItemAttr,
248 kSecProtocolItemAttr,
249 kSecAuthenticationTypeItemAttr,
250 kSecSecurityDomainItemAttr,
251 kSecCreationDateItemAttr,
252 kSecNegativeItemAttr };
253 attrInfo.count = arraysize(tags);
254 attrInfo.tag = tags;
255 attrInfo.format = NULL;
256
257 SecKeychainAttributeList *attrList;
258 UInt32 password_length;
259
260 // If |extract_password_data| is false, do not pass in a reference to
261 // |password_data|. ItemCopyAttributesAndData will then extract only the
262 // attributes of |keychain_item| (doesn't require OS authorization), and not
263 // attempt to extract its password data (requires OS authorization).
264 void* password_data = NULL;
265 void** password_data_ref = extract_password_data ? &password_data : NULL;
266
267 OSStatus result = keychain.ItemCopyAttributesAndData(keychain_item, &attrInfo,
268 NULL, &attrList,
269 &password_length,
270 password_data_ref);
271
272 if (result != noErr) {
273 // We don't log errSecAuthFailed because that just means that the user
274 // chose not to allow us access to the item.
275 if (result != errSecAuthFailed) {
276 OSSTATUS_LOG(ERROR, result) << "Keychain data load failed";
277 }
278 return false;
279 }
280
281 if (extract_password_data) {
282 base::UTF8ToUTF16(static_cast<const char *>(password_data), password_length,
283 &(form->password_value));
284 }
285
286 int port = kAnyPort;
287 std::string server;
288 std::string security_domain;
289 std::string path;
290 for (unsigned int i = 0; i < attrList->count; i++) {
291 SecKeychainAttribute attr = attrList->attr[i];
292 if (!attr.data) {
293 continue;
294 }
295 switch (attr.tag) {
296 case kSecAccountItemAttr:
297 base::UTF8ToUTF16(static_cast<const char *>(attr.data), attr.length,
298 &(form->username_value));
299 break;
300 case kSecServerItemAttr:
301 server.assign(static_cast<const char *>(attr.data), attr.length);
302 break;
303 case kSecPortItemAttr:
304 port = *(static_cast<UInt32*>(attr.data));
305 break;
306 case kSecPathItemAttr:
307 path.assign(static_cast<const char *>(attr.data), attr.length);
308 break;
309 case kSecProtocolItemAttr:
310 {
311 SecProtocolType protocol = *(static_cast<SecProtocolType*>(attr.data));
312 // TODO(stuartmorgan): Handle proxy types
313 form->ssl_valid = (protocol == kSecProtocolTypeHTTPS);
314 break;
315 }
316 case kSecAuthenticationTypeItemAttr:
317 {
318 SecAuthenticationType auth_type =
319 *(static_cast<SecAuthenticationType*>(attr.data));
320 form->scheme = SchemeForAuthType(auth_type);
321 break;
322 }
323 case kSecSecurityDomainItemAttr:
324 security_domain.assign(static_cast<const char *>(attr.data),
325 attr.length);
326 break;
327 case kSecCreationDateItemAttr:
328 // The only way to get a date out of Keychain is as a string. Really.
329 // (The docs claim it's an int, but the header is correct.)
330 TimeFromKeychainTimeString(static_cast<char*>(attr.data), attr.length,
331 &form->date_created);
332 break;
333 case kSecNegativeItemAttr:
334 Boolean negative_item = *(static_cast<Boolean*>(attr.data));
335 if (negative_item) {
336 form->blacklisted_by_user = true;
337 }
338 break;
339 }
340 }
341 keychain.ItemFreeAttributesAndData(attrList, password_data);
342
343 // kSecNegativeItemAttr doesn't seem to actually be in widespread use. In
344 // practice, other browsers seem to use a "" or " " password (and a special
345 // user name) to indicated blacklist entries.
346 if (extract_password_data && (form->password_value.empty() ||
347 EqualsASCII(form->password_value, " "))) {
348 form->blacklisted_by_user = true;
349 }
350
351 form->origin = URLFromComponents(form->ssl_valid, server, port, path);
352 // TODO(stuartmorgan): Handle proxies, which need a different signon_realm
353 // format.
354 form->signon_realm = form->origin.GetOrigin().spec();
355 if (form->scheme != PasswordForm::SCHEME_HTML) {
356 form->signon_realm.append(security_domain);
357 }
358 return true;
359 }
360
FormsMatchForMerge(const PasswordForm & form_a,const PasswordForm & form_b,FormMatchStrictness strictness)361 bool FormsMatchForMerge(const PasswordForm& form_a,
362 const PasswordForm& form_b,
363 FormMatchStrictness strictness) {
364 // We never merge blacklist entries between our store and the keychain.
365 if (form_a.blacklisted_by_user || form_b.blacklisted_by_user) {
366 return false;
367 }
368 bool equal_realm = form_a.signon_realm == form_b.signon_realm;
369 if (strictness == FUZZY_FORM_MATCH) {
370 equal_realm |= (!form_a.original_signon_realm.empty()) &&
371 form_a.original_signon_realm == form_b.signon_realm;
372 }
373 return form_a.scheme == form_b.scheme && equal_realm &&
374 form_a.username_value == form_b.username_value;
375 }
376
377 // Returns an the best match for |base_form| from |keychain_forms|, or NULL if
378 // there is no suitable match.
BestKeychainFormForForm(const PasswordForm & base_form,const std::vector<PasswordForm * > * keychain_forms)379 PasswordForm* BestKeychainFormForForm(
380 const PasswordForm& base_form,
381 const std::vector<PasswordForm*>* keychain_forms) {
382 PasswordForm* partial_match = NULL;
383 for (std::vector<PasswordForm*>::const_iterator i = keychain_forms->begin();
384 i != keychain_forms->end(); ++i) {
385 // TODO(stuartmorgan): We should really be scoring path matches and picking
386 // the best, rather than just checking exact-or-not (although in practice
387 // keychain items with paths probably came from us).
388 if (FormsMatchForMerge(base_form, *(*i), FUZZY_FORM_MATCH)) {
389 if (base_form.origin == (*i)->origin) {
390 return *i;
391 } else if (!partial_match) {
392 partial_match = *i;
393 }
394 }
395 }
396 return partial_match;
397 }
398
399 // Returns entries from |forms| that are blacklist entries, after removing
400 // them from |forms|.
ExtractBlacklistForms(std::vector<PasswordForm * > * forms)401 std::vector<PasswordForm*> ExtractBlacklistForms(
402 std::vector<PasswordForm*>* forms) {
403 std::vector<PasswordForm*> blacklist_forms;
404 for (std::vector<PasswordForm*>::iterator i = forms->begin();
405 i != forms->end();) {
406 PasswordForm* form = *i;
407 if (form->blacklisted_by_user) {
408 blacklist_forms.push_back(form);
409 i = forms->erase(i);
410 } else {
411 ++i;
412 }
413 }
414 return blacklist_forms;
415 }
416
417 // Deletes and removes from v any element that exists in s.
418 template <class T>
DeleteVectorElementsInSet(std::vector<T * > * v,const std::set<T * > & s)419 void DeleteVectorElementsInSet(std::vector<T*>* v, const std::set<T*>& s) {
420 for (typename std::vector<T*>::iterator i = v->begin(); i != v->end();) {
421 T* element = *i;
422 if (s.find(element) != s.end()) {
423 delete element;
424 i = v->erase(i);
425 } else {
426 ++i;
427 }
428 }
429 }
430
MergePasswordForms(std::vector<PasswordForm * > * keychain_forms,std::vector<PasswordForm * > * database_forms,std::vector<PasswordForm * > * merged_forms)431 void MergePasswordForms(std::vector<PasswordForm*>* keychain_forms,
432 std::vector<PasswordForm*>* database_forms,
433 std::vector<PasswordForm*>* merged_forms) {
434 // Pull out the database blacklist items, since they are used as-is rather
435 // than being merged with keychain forms.
436 std::vector<PasswordForm*> database_blacklist_forms =
437 ExtractBlacklistForms(database_forms);
438
439 // Merge the normal entries.
440 std::set<PasswordForm*> used_keychain_forms;
441 for (std::vector<PasswordForm*>::iterator i = database_forms->begin();
442 i != database_forms->end();) {
443 PasswordForm* db_form = *i;
444 PasswordForm* best_match = BestKeychainFormForForm(*db_form,
445 keychain_forms);
446 if (best_match) {
447 used_keychain_forms.insert(best_match);
448 db_form->password_value = best_match->password_value;
449 merged_forms->push_back(db_form);
450 i = database_forms->erase(i);
451 } else {
452 ++i;
453 }
454 }
455
456 // Add in the blacklist entries from the database.
457 merged_forms->insert(merged_forms->end(),
458 database_blacklist_forms.begin(),
459 database_blacklist_forms.end());
460
461 // Clear out all the Keychain entries we used.
462 DeleteVectorElementsInSet(keychain_forms, used_keychain_forms);
463 }
464
ExtractAllKeychainItemAttributesIntoPasswordForms(std::vector<SecKeychainItemRef> * keychain_items,const AppleKeychain & keychain)465 std::vector<ItemFormPair> ExtractAllKeychainItemAttributesIntoPasswordForms(
466 std::vector<SecKeychainItemRef>* keychain_items,
467 const AppleKeychain& keychain) {
468 DCHECK(keychain_items);
469 MacKeychainPasswordFormAdapter keychain_adapter(&keychain);
470 *keychain_items = keychain_adapter.GetAllPasswordFormKeychainItems();
471 std::vector<ItemFormPair> item_form_pairs;
472 for (std::vector<SecKeychainItemRef>::iterator i = keychain_items->begin();
473 i != keychain_items->end(); ++i) {
474 PasswordForm* form_without_password = new PasswordForm();
475 internal_keychain_helpers::FillPasswordFormFromKeychainItem(
476 keychain,
477 *i,
478 form_without_password,
479 false); // Load password attributes, but not password data.
480 item_form_pairs.push_back(std::make_pair(&(*i), form_without_password));
481 }
482 return item_form_pairs;
483 }
484
GetPasswordsForForms(const AppleKeychain & keychain,std::vector<PasswordForm * > * database_forms)485 std::vector<PasswordForm*> GetPasswordsForForms(
486 const AppleKeychain& keychain,
487 std::vector<PasswordForm*>* database_forms) {
488 // First load the attributes of all items in the keychain without loading
489 // their password data, and then match items in |database_forms| against them.
490 // This avoids individually searching through the keychain for passwords
491 // matching each form in |database_forms|, and results in a significant
492 // performance gain, replacing O(N) keychain search operations with a single
493 // operation that loads all keychain items, and then selective reads of only
494 // the relevant passwords. See crbug.com/263685.
495 std::vector<SecKeychainItemRef> keychain_items;
496 std::vector<ItemFormPair> item_form_pairs =
497 ExtractAllKeychainItemAttributesIntoPasswordForms(&keychain_items,
498 keychain);
499
500 // Next, compare the attributes of the PasswordForms in |database_forms|
501 // against those in |item_form_pairs|, and extract password data for each
502 // matching PasswordForm using its corresponding SecKeychainItemRef.
503 std::vector<PasswordForm*> merged_forms;
504 for (std::vector<PasswordForm*>::iterator i = database_forms->begin();
505 i != database_forms->end();) {
506 std::vector<PasswordForm*> db_form_container(1, *i);
507 std::vector<PasswordForm*> keychain_matches =
508 ExtractPasswordsMergeableWithForm(keychain, item_form_pairs, **i);
509 MergePasswordForms(&keychain_matches, &db_form_container, &merged_forms);
510 if (db_form_container.empty()) {
511 i = database_forms->erase(i);
512 } else {
513 ++i;
514 }
515 STLDeleteElements(&keychain_matches);
516 }
517
518 // Clean up temporary PasswordForms and SecKeychainItemRefs.
519 STLDeleteContainerPairSecondPointers(item_form_pairs.begin(),
520 item_form_pairs.end());
521 for (std::vector<SecKeychainItemRef>::iterator i = keychain_items.begin();
522 i != keychain_items.end(); ++i) {
523 keychain.Free(*i);
524 }
525 return merged_forms;
526 }
527
528 // TODO(stuartmorgan): signon_realm for proxies is not yet supported.
ExtractSignonRealmComponents(const std::string & signon_realm,std::string * server,int * port,bool * is_secure,std::string * security_domain)529 bool ExtractSignonRealmComponents(
530 const std::string& signon_realm, std::string* server, int* port,
531 bool* is_secure, std::string* security_domain) {
532 // The signon_realm will be the Origin portion of a URL for an HTML form,
533 // and the same but with the security domain as a path for HTTP auth.
534 GURL realm_as_url(signon_realm);
535 if (!realm_as_url.is_valid()) {
536 return false;
537 }
538
539 if (server)
540 *server = realm_as_url.host();
541 if (is_secure)
542 *is_secure = realm_as_url.SchemeIsSecure();
543 if (port)
544 *port = realm_as_url.has_port() ? atoi(realm_as_url.port().c_str()) : 0;
545 if (security_domain) {
546 // Strip the leading '/' off of the path to get the security domain.
547 if (realm_as_url.path().length() > 0)
548 *security_domain = realm_as_url.path().substr(1);
549 else
550 security_domain->clear();
551 }
552 return true;
553 }
554
FormIsValidAndMatchesOtherForm(const PasswordForm & query_form,const PasswordForm & other_form)555 bool FormIsValidAndMatchesOtherForm(const PasswordForm& query_form,
556 const PasswordForm& other_form) {
557 std::string server;
558 std::string security_domain;
559 int port;
560 bool is_secure;
561 if (!ExtractSignonRealmComponents(query_form.signon_realm, &server, &port,
562 &is_secure, &security_domain)) {
563 return false;
564 }
565 return internal_keychain_helpers::FormsMatchForMerge(
566 query_form, other_form, STRICT_FORM_MATCH);
567 }
568
ExtractPasswordsMergeableWithForm(const AppleKeychain & keychain,const std::vector<ItemFormPair> & item_form_pairs,const PasswordForm & query_form)569 std::vector<PasswordForm*> ExtractPasswordsMergeableWithForm(
570 const AppleKeychain& keychain,
571 const std::vector<ItemFormPair>& item_form_pairs,
572 const PasswordForm& query_form) {
573 std::vector<PasswordForm*> matches;
574 for (std::vector<ItemFormPair>::const_iterator i = item_form_pairs.begin();
575 i != item_form_pairs.end(); ++i) {
576 if (FormIsValidAndMatchesOtherForm(query_form, *(i->second))) {
577 // Create a new object, since the caller is responsible for deleting the
578 // returned forms.
579 scoped_ptr<PasswordForm> form_with_password(new PasswordForm());
580 internal_keychain_helpers::FillPasswordFormFromKeychainItem(
581 keychain,
582 *(i->first),
583 form_with_password.get(),
584 true); // Load password attributes and data.
585 // Do not include blacklisted items found in the keychain.
586 if (!form_with_password->blacklisted_by_user)
587 matches.push_back(form_with_password.release());
588 }
589 }
590 return matches;
591 }
592
593 } // namespace internal_keychain_helpers
594
595 #pragma mark -
596
MacKeychainPasswordFormAdapter(const AppleKeychain * keychain)597 MacKeychainPasswordFormAdapter::MacKeychainPasswordFormAdapter(
598 const AppleKeychain* keychain)
599 : keychain_(keychain), finds_only_owned_(false) {
600 }
601
PasswordsFillingForm(const std::string & signon_realm,PasswordForm::Scheme scheme)602 std::vector<PasswordForm*> MacKeychainPasswordFormAdapter::PasswordsFillingForm(
603 const std::string& signon_realm,
604 PasswordForm::Scheme scheme) {
605 std::vector<SecKeychainItemRef> keychain_items =
606 MatchingKeychainItems(signon_realm, scheme, NULL, NULL);
607
608 return ConvertKeychainItemsToForms(&keychain_items);
609 }
610
PasswordExactlyMatchingForm(const PasswordForm & query_form)611 PasswordForm* MacKeychainPasswordFormAdapter::PasswordExactlyMatchingForm(
612 const PasswordForm& query_form) {
613 SecKeychainItemRef keychain_item = KeychainItemForForm(query_form);
614 if (keychain_item) {
615 PasswordForm* form = new PasswordForm();
616 internal_keychain_helpers::FillPasswordFormFromKeychainItem(*keychain_,
617 keychain_item,
618 form,
619 true);
620 keychain_->Free(keychain_item);
621 return form;
622 }
623 return NULL;
624 }
625
HasPasswordsMergeableWithForm(const PasswordForm & query_form)626 bool MacKeychainPasswordFormAdapter::HasPasswordsMergeableWithForm(
627 const PasswordForm& query_form) {
628 std::string username = base::UTF16ToUTF8(query_form.username_value);
629 std::vector<SecKeychainItemRef> matches =
630 MatchingKeychainItems(query_form.signon_realm, query_form.scheme,
631 NULL, username.c_str());
632 for (std::vector<SecKeychainItemRef>::iterator i = matches.begin();
633 i != matches.end(); ++i) {
634 keychain_->Free(*i);
635 }
636
637 return !matches.empty();
638 }
639
640 std::vector<SecKeychainItemRef>
GetAllPasswordFormKeychainItems()641 MacKeychainPasswordFormAdapter::GetAllPasswordFormKeychainItems() {
642 SecAuthenticationType supported_auth_types[] = {
643 kSecAuthenticationTypeHTMLForm,
644 kSecAuthenticationTypeHTTPBasic,
645 kSecAuthenticationTypeHTTPDigest,
646 };
647
648 std::vector<SecKeychainItemRef> matches;
649 for (unsigned int i = 0; i < arraysize(supported_auth_types); ++i) {
650 KeychainSearch keychain_search(*keychain_);
651 keychain_search.Init(NULL, 0, kSecProtocolTypeAny, supported_auth_types[i],
652 NULL, NULL, NULL, CreatorCodeForSearch());
653 keychain_search.FindMatchingItems(&matches);
654 }
655 return matches;
656 }
657
658 std::vector<PasswordForm*>
GetAllPasswordFormPasswords()659 MacKeychainPasswordFormAdapter::GetAllPasswordFormPasswords() {
660 std::vector<SecKeychainItemRef> items = GetAllPasswordFormKeychainItems();
661 return ConvertKeychainItemsToForms(&items);
662 }
663
AddPassword(const PasswordForm & form)664 bool MacKeychainPasswordFormAdapter::AddPassword(const PasswordForm& form) {
665 // We should never be trying to store a blacklist in the keychain.
666 DCHECK(!form.blacklisted_by_user);
667
668 std::string server;
669 std::string security_domain;
670 int port;
671 bool is_secure;
672 if (!internal_keychain_helpers::ExtractSignonRealmComponents(
673 form.signon_realm, &server, &port, &is_secure, &security_domain)) {
674 return false;
675 }
676 std::string username = base::UTF16ToUTF8(form.username_value);
677 std::string password = base::UTF16ToUTF8(form.password_value);
678 std::string path = form.origin.path();
679 SecProtocolType protocol = is_secure ? kSecProtocolTypeHTTPS
680 : kSecProtocolTypeHTTP;
681 SecKeychainItemRef new_item = NULL;
682 OSStatus result = keychain_->AddInternetPassword(
683 NULL, server.size(), server.c_str(),
684 security_domain.size(), security_domain.c_str(),
685 username.size(), username.c_str(),
686 path.size(), path.c_str(),
687 port, protocol, AuthTypeForScheme(form.scheme),
688 password.size(), password.c_str(), &new_item);
689
690 if (result == noErr) {
691 SetKeychainItemCreatorCode(new_item,
692 base::mac::CreatorCodeForApplication());
693 keychain_->Free(new_item);
694 } else if (result == errSecDuplicateItem) {
695 // If we collide with an existing item, find and update it instead.
696 SecKeychainItemRef existing_item = KeychainItemForForm(form);
697 if (!existing_item) {
698 return false;
699 }
700 bool changed = SetKeychainItemPassword(existing_item, password);
701 keychain_->Free(existing_item);
702 return changed;
703 }
704
705 return result == noErr;
706 }
707
RemovePassword(const PasswordForm & form)708 bool MacKeychainPasswordFormAdapter::RemovePassword(const PasswordForm& form) {
709 SecKeychainItemRef keychain_item = KeychainItemForForm(form);
710 if (keychain_item == NULL)
711 return false;
712 OSStatus result = keychain_->ItemDelete(keychain_item);
713 keychain_->Free(keychain_item);
714 return result == noErr;
715 }
716
SetFindsOnlyOwnedItems(bool finds_only_owned)717 void MacKeychainPasswordFormAdapter::SetFindsOnlyOwnedItems(
718 bool finds_only_owned) {
719 finds_only_owned_ = finds_only_owned;
720 }
721
722 std::vector<PasswordForm*>
ConvertKeychainItemsToForms(std::vector<SecKeychainItemRef> * items)723 MacKeychainPasswordFormAdapter::ConvertKeychainItemsToForms(
724 std::vector<SecKeychainItemRef>* items) {
725 std::vector<PasswordForm*> keychain_forms;
726 for (std::vector<SecKeychainItemRef>::const_iterator i = items->begin();
727 i != items->end(); ++i) {
728 PasswordForm* form = new PasswordForm();
729 if (internal_keychain_helpers::FillPasswordFormFromKeychainItem(
730 *keychain_, *i, form, true)) {
731 keychain_forms.push_back(form);
732 }
733 keychain_->Free(*i);
734 }
735 items->clear();
736 return keychain_forms;
737 }
738
KeychainItemForForm(const PasswordForm & form)739 SecKeychainItemRef MacKeychainPasswordFormAdapter::KeychainItemForForm(
740 const PasswordForm& form) {
741 // We don't store blacklist entries in the keychain, so the answer to "what
742 // Keychain item goes with this form" is always "nothing" for blacklists.
743 if (form.blacklisted_by_user) {
744 return NULL;
745 }
746
747 std::string path = form.origin.path();
748 std::string username = base::UTF16ToUTF8(form.username_value);
749 std::vector<SecKeychainItemRef> matches = MatchingKeychainItems(
750 form.signon_realm, form.scheme, path.c_str(), username.c_str());
751
752 if (matches.empty()) {
753 return NULL;
754 }
755 // Free all items after the first, since we won't be returning them.
756 for (std::vector<SecKeychainItemRef>::iterator i = matches.begin() + 1;
757 i != matches.end(); ++i) {
758 keychain_->Free(*i);
759 }
760 return matches[0];
761 }
762
763 std::vector<SecKeychainItemRef>
MatchingKeychainItems(const std::string & signon_realm,autofill::PasswordForm::Scheme scheme,const char * path,const char * username)764 MacKeychainPasswordFormAdapter::MatchingKeychainItems(
765 const std::string& signon_realm,
766 autofill::PasswordForm::Scheme scheme,
767 const char* path, const char* username) {
768 std::vector<SecKeychainItemRef> matches;
769
770 std::string server;
771 std::string security_domain;
772 int port;
773 bool is_secure;
774 if (!internal_keychain_helpers::ExtractSignonRealmComponents(
775 signon_realm, &server, &port, &is_secure, &security_domain)) {
776 // TODO(stuartmorgan): Proxies will currently fail here, since their
777 // signon_realm is not a URL. We need to detect the proxy case and handle
778 // it specially.
779 return matches;
780 }
781 SecProtocolType protocol = is_secure ? kSecProtocolTypeHTTPS
782 : kSecProtocolTypeHTTP;
783 SecAuthenticationType auth_type = AuthTypeForScheme(scheme);
784 const char* auth_domain = (scheme == PasswordForm::SCHEME_HTML) ?
785 NULL : security_domain.c_str();
786 KeychainSearch keychain_search(*keychain_);
787 keychain_search.Init(server.c_str(), port, protocol, auth_type,
788 auth_domain, path, username, CreatorCodeForSearch());
789 keychain_search.FindMatchingItems(&matches);
790 return matches;
791 }
792
793 // Returns the Keychain SecAuthenticationType type corresponding to |scheme|.
AuthTypeForScheme(PasswordForm::Scheme scheme)794 SecAuthenticationType MacKeychainPasswordFormAdapter::AuthTypeForScheme(
795 PasswordForm::Scheme scheme) {
796 switch (scheme) {
797 case PasswordForm::SCHEME_HTML: return kSecAuthenticationTypeHTMLForm;
798 case PasswordForm::SCHEME_BASIC: return kSecAuthenticationTypeHTTPBasic;
799 case PasswordForm::SCHEME_DIGEST: return kSecAuthenticationTypeHTTPDigest;
800 case PasswordForm::SCHEME_OTHER: return kSecAuthenticationTypeDefault;
801 }
802 NOTREACHED();
803 return kSecAuthenticationTypeDefault;
804 }
805
SetKeychainItemPassword(const SecKeychainItemRef & keychain_item,const std::string & password)806 bool MacKeychainPasswordFormAdapter::SetKeychainItemPassword(
807 const SecKeychainItemRef& keychain_item, const std::string& password) {
808 OSStatus result = keychain_->ItemModifyAttributesAndData(keychain_item, NULL,
809 password.size(),
810 password.c_str());
811 return result == noErr;
812 }
813
SetKeychainItemCreatorCode(const SecKeychainItemRef & keychain_item,OSType creator_code)814 bool MacKeychainPasswordFormAdapter::SetKeychainItemCreatorCode(
815 const SecKeychainItemRef& keychain_item, OSType creator_code) {
816 SecKeychainAttribute attr = { kSecCreatorItemAttr, sizeof(creator_code),
817 &creator_code };
818 SecKeychainAttributeList attrList = { 1, &attr };
819 OSStatus result = keychain_->ItemModifyAttributesAndData(keychain_item,
820 &attrList, 0, NULL);
821 return result == noErr;
822 }
823
CreatorCodeForSearch()824 OSType MacKeychainPasswordFormAdapter::CreatorCodeForSearch() {
825 return finds_only_owned_ ? base::mac::CreatorCodeForApplication() : 0;
826 }
827
828 #pragma mark -
829
PasswordStoreMac(scoped_refptr<base::SingleThreadTaskRunner> main_thread_runner,scoped_refptr<base::SingleThreadTaskRunner> db_thread_runner,AppleKeychain * keychain,password_manager::LoginDatabase * login_db)830 PasswordStoreMac::PasswordStoreMac(
831 scoped_refptr<base::SingleThreadTaskRunner> main_thread_runner,
832 scoped_refptr<base::SingleThreadTaskRunner> db_thread_runner,
833 AppleKeychain* keychain,
834 password_manager::LoginDatabase* login_db)
835 : password_manager::PasswordStore(main_thread_runner, db_thread_runner),
836 keychain_(keychain),
837 login_metadata_db_(login_db) {
838 DCHECK(keychain_.get());
839 DCHECK(login_metadata_db_.get());
840 }
841
~PasswordStoreMac()842 PasswordStoreMac::~PasswordStoreMac() {}
843
Init(const syncer::SyncableService::StartSyncFlare & flare)844 bool PasswordStoreMac::Init(
845 const syncer::SyncableService::StartSyncFlare& flare) {
846 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
847 thread_.reset(new base::Thread("Chrome_PasswordStore_Thread"));
848
849 if (!thread_->Start()) {
850 thread_.reset(NULL);
851 return false;
852 }
853 return password_manager::PasswordStore::Init(flare);
854 }
855
Shutdown()856 void PasswordStoreMac::Shutdown() {
857 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
858 password_manager::PasswordStore::Shutdown();
859 thread_->Stop();
860 }
861
862 // Mac stores passwords in the system keychain, which can block for an
863 // arbitrarily long time (most notably, it can block on user confirmation
864 // from a dialog). Run tasks on a dedicated thread to avoid blocking the DB
865 // thread.
866 scoped_refptr<base::SingleThreadTaskRunner>
GetBackgroundTaskRunner()867 PasswordStoreMac::GetBackgroundTaskRunner() {
868 return (thread_.get()) ? thread_->message_loop_proxy() : NULL;
869 }
870
ReportMetricsImpl()871 void PasswordStoreMac::ReportMetricsImpl() {
872 login_metadata_db_->ReportMetrics();
873 }
874
AddLoginImpl(const PasswordForm & form)875 PasswordStoreChangeList PasswordStoreMac::AddLoginImpl(
876 const PasswordForm& form) {
877 DCHECK(thread_->message_loop() == base::MessageLoop::current());
878 PasswordStoreChangeList changes;
879 if (AddToKeychainIfNecessary(form)) {
880 changes = login_metadata_db_->AddLogin(form);
881 }
882 return changes;
883 }
884
UpdateLoginImpl(const PasswordForm & form)885 PasswordStoreChangeList PasswordStoreMac::UpdateLoginImpl(
886 const PasswordForm& form) {
887 DCHECK(thread_->message_loop() == base::MessageLoop::current());
888 PasswordStoreChangeList changes = login_metadata_db_->UpdateLogin(form);
889
890 MacKeychainPasswordFormAdapter keychain_adapter(keychain_.get());
891 if (changes.empty() &&
892 !keychain_adapter.HasPasswordsMergeableWithForm(form)) {
893 // If the password isn't in either the DB or the keychain, then it must have
894 // been deleted after autofill happened, and should not be re-added.
895 return changes;
896 }
897
898 // The keychain add will update if there is a collision and add if there
899 // isn't, which is the behavior we want, so there's no separate update call.
900 if (AddToKeychainIfNecessary(form) && changes.empty()) {
901 changes = login_metadata_db_->AddLogin(form);
902 }
903 return changes;
904 }
905
RemoveLoginImpl(const PasswordForm & form)906 PasswordStoreChangeList PasswordStoreMac::RemoveLoginImpl(
907 const PasswordForm& form) {
908 DCHECK(thread_->message_loop() == base::MessageLoop::current());
909 PasswordStoreChangeList changes;
910 if (login_metadata_db_->RemoveLogin(form)) {
911 // See if we own a Keychain item associated with this item. We can do an
912 // exact search rather than messing around with trying to do fuzzy matching
913 // because passwords that we created will always have an exact-match
914 // database entry.
915 // (If a user does lose their profile but not their keychain we'll treat the
916 // entries we find like other imported entries anyway, so it's reasonable to
917 // handle deletes on them the way we would for an imported item.)
918 MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_.get());
919 owned_keychain_adapter.SetFindsOnlyOwnedItems(true);
920 PasswordForm* owned_password_form =
921 owned_keychain_adapter.PasswordExactlyMatchingForm(form);
922 if (owned_password_form) {
923 // If we don't have other forms using it (i.e., a form differing only by
924 // the names of the form elements), delete the keychain entry.
925 if (!DatabaseHasFormMatchingKeychainForm(form)) {
926 owned_keychain_adapter.RemovePassword(form);
927 }
928 }
929
930 changes.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE, form));
931 }
932 return changes;
933 }
934
RemoveLoginsCreatedBetweenImpl(base::Time delete_begin,base::Time delete_end)935 PasswordStoreChangeList PasswordStoreMac::RemoveLoginsCreatedBetweenImpl(
936 base::Time delete_begin,
937 base::Time delete_end) {
938 PasswordStoreChangeList changes;
939 std::vector<PasswordForm*> forms;
940 if (login_metadata_db_->GetLoginsCreatedBetween(delete_begin, delete_end,
941 &forms)) {
942 if (login_metadata_db_->RemoveLoginsCreatedBetween(delete_begin,
943 delete_end)) {
944 // We can't delete from the Keychain by date because we may be sharing
945 // items with database entries that weren't in the delete range. Instead,
946 // we find all the Keychain items we own but aren't using any more and
947 // delete those.
948 std::vector<PasswordForm*> orphan_keychain_forms =
949 GetUnusedKeychainForms();
950 // This is inefficient, since we have to re-look-up each keychain item
951 // one at a time to delete it even though the search step already had a
952 // list of Keychain item references. If this turns out to be noticeably
953 // slow we'll need to rearchitect to allow the search and deletion steps
954 // to share.
955 RemoveKeychainForms(orphan_keychain_forms);
956 STLDeleteElements(&orphan_keychain_forms);
957
958 for (std::vector<PasswordForm*>::const_iterator it = forms.begin();
959 it != forms.end(); ++it) {
960 changes.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE,
961 **it));
962 }
963 LogStatsForBulkDeletion(changes.size());
964 }
965 }
966 return changes;
967 }
968
RemoveLoginsSyncedBetweenImpl(base::Time delete_begin,base::Time delete_end)969 PasswordStoreChangeList PasswordStoreMac::RemoveLoginsSyncedBetweenImpl(
970 base::Time delete_begin,
971 base::Time delete_end) {
972 PasswordStoreChangeList changes;
973 std::vector<PasswordForm*> forms;
974 if (login_metadata_db_->GetLoginsSyncedBetween(
975 delete_begin, delete_end, &forms)) {
976 if (login_metadata_db_->RemoveLoginsSyncedBetween(delete_begin,
977 delete_end)) {
978 // We can't delete from the Keychain by date because we may be sharing
979 // items with database entries that weren't in the delete range. Instead,
980 // we find all the Keychain items we own but aren't using any more and
981 // delete those.
982 std::vector<PasswordForm*> orphan_keychain_forms =
983 GetUnusedKeychainForms();
984 // This is inefficient, since we have to re-look-up each keychain item
985 // one at a time to delete it even though the search step already had a
986 // list of Keychain item references. If this turns out to be noticeably
987 // slow we'll need to rearchitect to allow the search and deletion steps
988 // to share.
989 RemoveKeychainForms(orphan_keychain_forms);
990 STLDeleteElements(&orphan_keychain_forms);
991
992 for (std::vector<PasswordForm*>::const_iterator it = forms.begin();
993 it != forms.end();
994 ++it) {
995 changes.push_back(
996 PasswordStoreChange(PasswordStoreChange::REMOVE, **it));
997 }
998 }
999 }
1000 return changes;
1001 }
1002
GetLoginsImpl(const autofill::PasswordForm & form,AuthorizationPromptPolicy prompt_policy,const ConsumerCallbackRunner & callback_runner)1003 void PasswordStoreMac::GetLoginsImpl(
1004 const autofill::PasswordForm& form,
1005 AuthorizationPromptPolicy prompt_policy,
1006 const ConsumerCallbackRunner& callback_runner) {
1007 chrome::ScopedSecKeychainSetUserInteractionAllowed user_interaction_allowed(
1008 prompt_policy == ALLOW_PROMPT);
1009
1010 std::vector<PasswordForm*> database_forms;
1011 login_metadata_db_->GetLogins(form, &database_forms);
1012
1013 // Let's gather all signon realms we want to match with keychain entries.
1014 std::set<std::string> realm_set;
1015 realm_set.insert(form.signon_realm);
1016 for (std::vector<PasswordForm*>::const_iterator db_form =
1017 database_forms.begin();
1018 db_form != database_forms.end();
1019 ++db_form) {
1020 // TODO(vabr): We should not be getting different schemes here.
1021 // http://crbug.com/340112
1022 if (form.scheme != (*db_form)->scheme)
1023 continue; // Forms with different schemes never match.
1024 const std::string& original_singon_realm((*db_form)->original_signon_realm);
1025 if (!original_singon_realm.empty())
1026 realm_set.insert(original_singon_realm);
1027 }
1028 std::vector<PasswordForm*> keychain_forms;
1029 for (std::set<std::string>::const_iterator realm = realm_set.begin();
1030 realm != realm_set.end();
1031 ++realm) {
1032 MacKeychainPasswordFormAdapter keychain_adapter(keychain_.get());
1033 std::vector<PasswordForm*> temp_keychain_forms =
1034 keychain_adapter.PasswordsFillingForm(*realm, form.scheme);
1035 keychain_forms.insert(keychain_forms.end(),
1036 temp_keychain_forms.begin(),
1037 temp_keychain_forms.end());
1038 }
1039
1040 std::vector<PasswordForm*> matched_forms;
1041 internal_keychain_helpers::MergePasswordForms(&keychain_forms,
1042 &database_forms,
1043 &matched_forms);
1044
1045 // Strip any blacklist entries out of the unused Keychain array, then take
1046 // all the entries that are left (which we can use as imported passwords).
1047 std::vector<PasswordForm*> keychain_blacklist_forms =
1048 internal_keychain_helpers::ExtractBlacklistForms(&keychain_forms);
1049 matched_forms.insert(matched_forms.end(),
1050 keychain_forms.begin(),
1051 keychain_forms.end());
1052 keychain_forms.clear();
1053 STLDeleteElements(&keychain_blacklist_forms);
1054
1055 // Clean up any orphaned database entries.
1056 RemoveDatabaseForms(database_forms);
1057 STLDeleteElements(&database_forms);
1058
1059 callback_runner.Run(matched_forms);
1060 }
1061
GetBlacklistLoginsImpl(GetLoginsRequest * request)1062 void PasswordStoreMac::GetBlacklistLoginsImpl(GetLoginsRequest* request) {
1063 FillBlacklistLogins(request->result());
1064 ForwardLoginsResult(request);
1065 }
1066
GetAutofillableLoginsImpl(GetLoginsRequest * request)1067 void PasswordStoreMac::GetAutofillableLoginsImpl(GetLoginsRequest* request) {
1068 FillAutofillableLogins(request->result());
1069 ForwardLoginsResult(request);
1070 }
1071
FillAutofillableLogins(std::vector<PasswordForm * > * forms)1072 bool PasswordStoreMac::FillAutofillableLogins(
1073 std::vector<PasswordForm*>* forms) {
1074 DCHECK(thread_->message_loop() == base::MessageLoop::current());
1075
1076 std::vector<PasswordForm*> database_forms;
1077 login_metadata_db_->GetAutofillableLogins(&database_forms);
1078
1079 std::vector<PasswordForm*> merged_forms =
1080 internal_keychain_helpers::GetPasswordsForForms(*keychain_,
1081 &database_forms);
1082
1083 // Clean up any orphaned database entries.
1084 RemoveDatabaseForms(database_forms);
1085 STLDeleteElements(&database_forms);
1086
1087 forms->insert(forms->end(), merged_forms.begin(), merged_forms.end());
1088 return true;
1089 }
1090
FillBlacklistLogins(std::vector<PasswordForm * > * forms)1091 bool PasswordStoreMac::FillBlacklistLogins(
1092 std::vector<PasswordForm*>* forms) {
1093 DCHECK(thread_->message_loop() == base::MessageLoop::current());
1094 return login_metadata_db_->GetBlacklistLogins(forms);
1095 }
1096
AddToKeychainIfNecessary(const PasswordForm & form)1097 bool PasswordStoreMac::AddToKeychainIfNecessary(const PasswordForm& form) {
1098 if (form.blacklisted_by_user) {
1099 return true;
1100 }
1101 MacKeychainPasswordFormAdapter keychainAdapter(keychain_.get());
1102 return keychainAdapter.AddPassword(form);
1103 }
1104
DatabaseHasFormMatchingKeychainForm(const autofill::PasswordForm & form)1105 bool PasswordStoreMac::DatabaseHasFormMatchingKeychainForm(
1106 const autofill::PasswordForm& form) {
1107 bool has_match = false;
1108 std::vector<PasswordForm*> database_forms;
1109 login_metadata_db_->GetLogins(form, &database_forms);
1110 for (std::vector<PasswordForm*>::iterator i = database_forms.begin();
1111 i != database_forms.end(); ++i) {
1112 // Below we filter out forms with non-empty original_signon_realm, because
1113 // those signal fuzzy matches, and we are only interested in exact ones.
1114 if ((*i)->original_signon_realm.empty() &&
1115 internal_keychain_helpers::FormsMatchForMerge(
1116 form, **i, internal_keychain_helpers::STRICT_FORM_MATCH) &&
1117 (*i)->origin == form.origin) {
1118 has_match = true;
1119 break;
1120 }
1121 }
1122 STLDeleteElements(&database_forms);
1123 return has_match;
1124 }
1125
GetUnusedKeychainForms()1126 std::vector<PasswordForm*> PasswordStoreMac::GetUnusedKeychainForms() {
1127 std::vector<PasswordForm*> database_forms;
1128 login_metadata_db_->GetAutofillableLogins(&database_forms);
1129
1130 MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_.get());
1131 owned_keychain_adapter.SetFindsOnlyOwnedItems(true);
1132 std::vector<PasswordForm*> owned_keychain_forms =
1133 owned_keychain_adapter.GetAllPasswordFormPasswords();
1134
1135 // Run a merge; anything left in owned_keychain_forms when we are done no
1136 // longer has a matching database entry.
1137 std::vector<PasswordForm*> merged_forms;
1138 internal_keychain_helpers::MergePasswordForms(&owned_keychain_forms,
1139 &database_forms,
1140 &merged_forms);
1141 STLDeleteElements(&merged_forms);
1142 STLDeleteElements(&database_forms);
1143
1144 return owned_keychain_forms;
1145 }
1146
RemoveDatabaseForms(const std::vector<PasswordForm * > & forms)1147 void PasswordStoreMac::RemoveDatabaseForms(
1148 const std::vector<PasswordForm*>& forms) {
1149 for (std::vector<PasswordForm*>::const_iterator i = forms.begin();
1150 i != forms.end(); ++i) {
1151 login_metadata_db_->RemoveLogin(**i);
1152 }
1153 }
1154
RemoveKeychainForms(const std::vector<PasswordForm * > & forms)1155 void PasswordStoreMac::RemoveKeychainForms(
1156 const std::vector<PasswordForm*>& forms) {
1157 MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_.get());
1158 owned_keychain_adapter.SetFindsOnlyOwnedItems(true);
1159 for (std::vector<PasswordForm*>::const_iterator i = forms.begin();
1160 i != forms.end(); ++i) {
1161 owned_keychain_adapter.RemovePassword(**i);
1162 }
1163 }
1164