Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(58)

Side by Side Diff: chrome/browser/password_manager/password_store_mac.cc

Issue 2909283002: Delete PasswordStoreMac and SimplePasswordStoreMac. (Closed)
Patch Set: test Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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
7 #include <CoreServices/CoreServices.h>
8 #include <stddef.h>
9 #include <algorithm>
10 #include <iterator>
11 #include <set>
12 #include <string>
13 #include <utility>
14 #include <vector>
15
16 #include "base/callback.h"
17 #include "base/logging.h"
18 #include "base/mac/foundation_util.h"
19 #include "base/mac/mac_logging.h"
20 #include "base/macros.h"
21 #include "base/memory/ptr_util.h"
22 #include "base/message_loop/message_loop.h"
23 #include "base/metrics/histogram_macros.h"
24 #include "base/strings/string_util.h"
25 #include "base/strings/utf_string_conversions.h"
26 #include "chrome/browser/mac/security_wrappers.h"
27 #include "chrome/browser/password_manager/password_store_mac_internal.h"
28 #include "components/os_crypt/os_crypt.h"
29 #include "components/password_manager/core/browser/affiliation_utils.h"
30 #include "components/password_manager/core/browser/login_database.h"
31 #include "components/password_manager/core/browser/password_manager_util.h"
32 #include "components/password_manager/core/browser/password_store_change.h"
33 #include "content/public/browser/browser_thread.h"
34 #include "crypto/apple_keychain.h"
35 #include "url/origin.h"
36
37 using autofill::PasswordForm;
38 using crypto::AppleKeychain;
39 using password_manager::PasswordStoreChange;
40 using password_manager::PasswordStoreChangeList;
41
42 namespace {
43
44 // Utility class to handle the details of constructing and running a keychain
45 // search from a set of attributes.
46 class KeychainSearch {
47 public:
48 explicit KeychainSearch(const AppleKeychain& keychain);
49 ~KeychainSearch();
50
51 // Sets up a keychain search based on an non "null" (NULL for char*,
52 // The appropriate "Any" entry for other types) arguments.
53 //
54 // IMPORTANT: Any parameters passed in *must* remain valid for as long as the
55 // KeychainSearch object, since the search uses them by reference.
56 void Init(const char* server,
57 const UInt32* port,
58 const SecProtocolType* protocol,
59 const SecAuthenticationType* auth_type,
60 const char* security_domain,
61 const char* path,
62 const char* username,
63 const OSType* creator);
64
65 // Fills |items| with all Keychain items that match the Init'd search.
66 // If the search fails for any reason, |items| will be unchanged.
67 void FindMatchingItems(std::vector<SecKeychainItemRef>* matches);
68
69 private:
70 const AppleKeychain* keychain_;
71 SecKeychainAttributeList search_attributes_;
72 SecKeychainSearchRef search_ref_;
73 };
74
75 KeychainSearch::KeychainSearch(const AppleKeychain& keychain)
76 : keychain_(&keychain), search_ref_(NULL) {
77 search_attributes_.count = 0;
78 search_attributes_.attr = NULL;
79 }
80
81 KeychainSearch::~KeychainSearch() {
82 if (search_attributes_.attr) {
83 free(search_attributes_.attr);
84 }
85 }
86
87 void KeychainSearch::Init(const char* server,
88 const UInt32* port,
89 const SecProtocolType* protocol,
90 const SecAuthenticationType* auth_type,
91 const char* security_domain,
92 const char* path,
93 const char* username,
94 const OSType* creator) {
95 // Allocate enough to hold everything we might use.
96 const unsigned int kMaxEntryCount = 8;
97 search_attributes_.attr =
98 static_cast<SecKeychainAttribute*>(calloc(kMaxEntryCount,
99 sizeof(SecKeychainAttribute)));
100 unsigned int entries = 0;
101 // We only use search_attributes_ with SearchCreateFromAttributes, which takes
102 // a "const SecKeychainAttributeList *", so we trust that they won't try
103 // to modify the list, and that casting away const-ness is thus safe.
104 if (server != NULL) {
105 DCHECK_LT(entries, kMaxEntryCount);
106 search_attributes_.attr[entries].tag = kSecServerItemAttr;
107 search_attributes_.attr[entries].length = strlen(server);
108 search_attributes_.attr[entries].data =
109 const_cast<void*>(static_cast<const void*>(server));
110 ++entries;
111 }
112 if (port != NULL && *port != kAnyPort) {
113 DCHECK_LE(entries, kMaxEntryCount);
114 search_attributes_.attr[entries].tag = kSecPortItemAttr;
115 search_attributes_.attr[entries].length = sizeof(*port);
116 search_attributes_.attr[entries].data =
117 const_cast<void*>(static_cast<const void*>(port));
118 ++entries;
119 }
120 if (protocol != NULL && *protocol != kSecProtocolTypeAny) {
121 DCHECK_LE(entries, kMaxEntryCount);
122 search_attributes_.attr[entries].tag = kSecProtocolItemAttr;
123 search_attributes_.attr[entries].length = sizeof(*protocol);
124 search_attributes_.attr[entries].data =
125 const_cast<void*>(static_cast<const void*>(protocol));
126 ++entries;
127 }
128 if (auth_type != NULL && *auth_type != kSecAuthenticationTypeAny) {
129 DCHECK_LE(entries, kMaxEntryCount);
130 search_attributes_.attr[entries].tag = kSecAuthenticationTypeItemAttr;
131 search_attributes_.attr[entries].length = sizeof(*auth_type);
132 search_attributes_.attr[entries].data =
133 const_cast<void*>(static_cast<const void*>(auth_type));
134 ++entries;
135 }
136 if (security_domain != NULL && strlen(security_domain) > 0) {
137 DCHECK_LE(entries, kMaxEntryCount);
138 search_attributes_.attr[entries].tag = kSecSecurityDomainItemAttr;
139 search_attributes_.attr[entries].length = strlen(security_domain);
140 search_attributes_.attr[entries].data =
141 const_cast<void*>(static_cast<const void*>(security_domain));
142 ++entries;
143 }
144 if (path != NULL && strlen(path) > 0 && strcmp(path, "/") != 0) {
145 DCHECK_LE(entries, kMaxEntryCount);
146 search_attributes_.attr[entries].tag = kSecPathItemAttr;
147 search_attributes_.attr[entries].length = strlen(path);
148 search_attributes_.attr[entries].data =
149 const_cast<void*>(static_cast<const void*>(path));
150 ++entries;
151 }
152 if (username != NULL) {
153 DCHECK_LE(entries, kMaxEntryCount);
154 search_attributes_.attr[entries].tag = kSecAccountItemAttr;
155 search_attributes_.attr[entries].length = strlen(username);
156 search_attributes_.attr[entries].data =
157 const_cast<void*>(static_cast<const void*>(username));
158 ++entries;
159 }
160 if (creator != NULL) {
161 DCHECK_LE(entries, kMaxEntryCount);
162 search_attributes_.attr[entries].tag = kSecCreatorItemAttr;
163 search_attributes_.attr[entries].length = sizeof(*creator);
164 search_attributes_.attr[entries].data =
165 const_cast<void*>(static_cast<const void*>(creator));
166 ++entries;
167 }
168 search_attributes_.count = entries;
169 }
170
171 void KeychainSearch::FindMatchingItems(std::vector<SecKeychainItemRef>* items) {
172 OSStatus result = keychain_->SearchCreateFromAttributes(
173 NULL, kSecInternetPasswordItemClass, &search_attributes_, &search_ref_);
174
175 if (result != noErr) {
176 OSSTATUS_LOG(ERROR, result) << "Keychain lookup failed";
177 return;
178 }
179
180 SecKeychainItemRef keychain_item;
181 while (keychain_->SearchCopyNext(search_ref_, &keychain_item) == noErr) {
182 // Consumer is responsible for freeing the items.
183 items->push_back(keychain_item);
184 }
185
186 keychain_->Free(search_ref_);
187 search_ref_ = NULL;
188 }
189
190 PasswordStoreChangeList FormsToRemoveChangeList(
191 const std::vector<std::unique_ptr<PasswordForm>>& forms) {
192 PasswordStoreChangeList changes;
193 for (const auto& form : forms) {
194 changes.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE, *form));
195 }
196 return changes;
197 }
198
199 // Moves the content of |second| to the end of |first|.
200 void AppendSecondToFirst(std::vector<std::unique_ptr<PasswordForm>>* first,
201 std::vector<std::unique_ptr<PasswordForm>>* second) {
202 first->reserve(first->size() + second->size());
203 std::move(second->begin(), second->end(), std::back_inserter(*first));
204 second->clear();
205 }
206
207 // Returns the best match for |base_form| from |keychain_forms|, or nullptr if
208 // there is no suitable match.
209 const PasswordForm* BestKeychainFormForForm(
210 const PasswordForm& base_form,
211 const std::vector<std::unique_ptr<PasswordForm>>& keychain_forms) {
212 const PasswordForm* partial_match = nullptr;
213 for (const auto& keychain_form : keychain_forms) {
214 // TODO(stuartmorgan): We should really be scoring path matches and picking
215 // the best, rather than just checking exact-or-not (although in practice
216 // keychain items with paths probably came from us).
217 if (internal_keychain_helpers::FormsMatchForMerge(
218 base_form, *keychain_form,
219 internal_keychain_helpers::FUZZY_FORM_MATCH)) {
220 if (base_form.origin == keychain_form->origin) {
221 return keychain_form.get();
222 } else if (!partial_match) {
223 partial_match = keychain_form.get();
224 }
225 }
226 }
227 return partial_match;
228 }
229
230 // True if the form has no password to be stored in Keychain.
231 bool IsLoginDatabaseOnlyForm(const PasswordForm& form) {
232 return form.blacklisted_by_user || !form.federation_origin.unique() ||
233 form.scheme == PasswordForm::SCHEME_USERNAME_ONLY;
234 }
235
236 } // namespace
237
238 #pragma mark -
239
240 // TODO(stuartmorgan): Convert most of this to private helpers in
241 // MacKeychainPasswordFormAdapter once it has sufficient higher-level public
242 // methods to provide test coverage.
243 namespace internal_keychain_helpers {
244
245 // Returns a URL built from the given components. To create a URL without a
246 // port, pass kAnyPort for the |port| parameter.
247 GURL URLFromComponents(bool is_secure, const std::string& host, int port,
248 const std::string& path) {
249 GURL::Replacements url_components;
250 std::string scheme(is_secure ? "https" : "http");
251 url_components.SetSchemeStr(scheme);
252 url_components.SetHostStr(host);
253 std::string port_string; // Must remain in scope until after we do replacing.
254 if (port != kAnyPort) {
255 std::ostringstream port_stringstream;
256 port_stringstream << port;
257 port_string = port_stringstream.str();
258 url_components.SetPortStr(port_string);
259 }
260 url_components.SetPathStr(path);
261
262 GURL url("http://dummy.com"); // ReplaceComponents needs a valid URL.
263 return url.ReplaceComponents(url_components);
264 }
265
266 // Converts a Keychain time string to a Time object, returning true if
267 // time_string_bytes was parsable.
268 bool TimeFromKeychainTimeString(const char* time_string_bytes,
269 unsigned int byte_length,
270 base::Time* time) {
271 DCHECK(time);
272
273 char* time_string = static_cast<char*>(malloc(byte_length + 1));
274 memcpy(time_string, time_string_bytes, byte_length);
275 time_string[byte_length] = '\0';
276 base::Time::Exploded exploded_time;
277 bzero(&exploded_time, sizeof(exploded_time));
278 // The time string is of the form "yyyyMMddHHmmss'Z", in UTC time.
279 int assignments = sscanf(time_string, "%4d%2d%2d%2d%2d%2dZ",
280 &exploded_time.year, &exploded_time.month,
281 &exploded_time.day_of_month, &exploded_time.hour,
282 &exploded_time.minute, &exploded_time.second);
283 free(time_string);
284
285 return assignments == 6 && base::Time::FromUTCExploded(exploded_time, time);
286 }
287
288 // Returns the PasswordForm Scheme corresponding to |auth_type|.
289 PasswordForm::Scheme SchemeForAuthType(SecAuthenticationType auth_type) {
290 switch (auth_type) {
291 case kSecAuthenticationTypeHTMLForm: return PasswordForm::SCHEME_HTML;
292 case kSecAuthenticationTypeHTTPBasic: return PasswordForm::SCHEME_BASIC;
293 case kSecAuthenticationTypeHTTPDigest: return PasswordForm::SCHEME_DIGEST;
294 default: return PasswordForm::SCHEME_OTHER;
295 }
296 }
297
298 bool FillPasswordFormFromKeychainItem(const AppleKeychain& keychain,
299 SecKeychainItemRef keychain_item,
300 PasswordForm* form,
301 bool extract_password_data) {
302 DCHECK(form);
303
304 SecKeychainAttributeInfo attrInfo;
305 UInt32 tags[] = { kSecAccountItemAttr,
306 kSecServerItemAttr,
307 kSecPortItemAttr,
308 kSecPathItemAttr,
309 kSecProtocolItemAttr,
310 kSecAuthenticationTypeItemAttr,
311 kSecSecurityDomainItemAttr,
312 kSecCreationDateItemAttr,
313 kSecNegativeItemAttr };
314 attrInfo.count = arraysize(tags);
315 attrInfo.tag = tags;
316 attrInfo.format = NULL;
317
318 SecKeychainAttributeList* attrList;
319 UInt32 password_length;
320
321 // If |extract_password_data| is false, do not pass in a reference to
322 // |password_data|. ItemCopyAttributesAndData will then extract only the
323 // attributes of |keychain_item| (doesn't require OS authorization), and not
324 // attempt to extract its password data (requires OS authorization).
325 void* password_data = NULL;
326 void** password_data_ref = extract_password_data ? &password_data : NULL;
327
328 OSStatus result = keychain.ItemCopyAttributesAndData(keychain_item, &attrInfo,
329 NULL, &attrList,
330 &password_length,
331 password_data_ref);
332
333 if (result != noErr) {
334 // We don't log errSecAuthFailed because that just means that the user
335 // chose not to allow us access to the item.
336 if (result != errSecAuthFailed) {
337 OSSTATUS_LOG(ERROR, result) << "Keychain data load failed";
338 }
339 return false;
340 }
341
342 if (extract_password_data) {
343 base::UTF8ToUTF16(static_cast<const char *>(password_data), password_length,
344 &(form->password_value));
345 }
346
347 int port = kAnyPort;
348 std::string server;
349 std::string security_domain;
350 std::string path;
351 bool is_secure = false;
352 for (unsigned int i = 0; i < attrList->count; i++) {
353 SecKeychainAttribute attr = attrList->attr[i];
354 if (!attr.data) {
355 continue;
356 }
357 switch (attr.tag) {
358 case kSecAccountItemAttr:
359 base::UTF8ToUTF16(static_cast<const char *>(attr.data), attr.length,
360 &(form->username_value));
361 break;
362 case kSecServerItemAttr:
363 server.assign(static_cast<const char *>(attr.data), attr.length);
364 break;
365 case kSecPortItemAttr:
366 port = *(static_cast<UInt32*>(attr.data));
367 break;
368 case kSecPathItemAttr:
369 path.assign(static_cast<const char *>(attr.data), attr.length);
370 break;
371 case kSecProtocolItemAttr:
372 {
373 SecProtocolType protocol = *(static_cast<SecProtocolType*>(attr.data));
374 // TODO(stuartmorgan): Handle proxy types
375 is_secure = (protocol == kSecProtocolTypeHTTPS);
376 break;
377 }
378 case kSecAuthenticationTypeItemAttr:
379 {
380 SecAuthenticationType auth_type =
381 *(static_cast<SecAuthenticationType*>(attr.data));
382 form->scheme = SchemeForAuthType(auth_type);
383 break;
384 }
385 case kSecSecurityDomainItemAttr:
386 security_domain.assign(static_cast<const char *>(attr.data),
387 attr.length);
388 break;
389 case kSecCreationDateItemAttr:
390 // The only way to get a date out of Keychain is as a string. Really.
391 // (The docs claim it's an int, but the header is correct.)
392 TimeFromKeychainTimeString(static_cast<char*>(attr.data), attr.length,
393 &form->date_created);
394 break;
395 case kSecNegativeItemAttr:
396 Boolean negative_item = *(static_cast<Boolean*>(attr.data));
397 if (negative_item) {
398 form->blacklisted_by_user = true;
399 }
400 break;
401 }
402 }
403 keychain.ItemFreeAttributesAndData(attrList, password_data);
404
405 // kSecNegativeItemAttr doesn't seem to actually be in widespread use. In
406 // practice, other browsers seem to use a "" or " " password (and a special
407 // user name) to indicated blacklist entries.
408 if (extract_password_data && (form->password_value.empty() ||
409 base::EqualsASCII(form->password_value, " "))) {
410 form->blacklisted_by_user = true;
411 }
412
413 // Android facet URLs aren't parsed correctly by GURL and need to be handled
414 // separately.
415 if (password_manager::IsValidAndroidFacetURI(server)) {
416 form->signon_realm = server;
417 form->origin = GURL();
418 } else {
419 form->origin = URLFromComponents(is_secure, server, port, path);
420 // TODO(stuartmorgan): Handle proxies, which need a different signon_realm
421 // format.
422 form->signon_realm = form->origin.GetOrigin().spec();
423 if (form->scheme != PasswordForm::SCHEME_HTML) {
424 form->signon_realm.append(security_domain);
425 }
426 }
427 return true;
428 }
429
430 bool HasChromeCreatorCode(const AppleKeychain& keychain,
431 SecKeychainItemRef keychain_item) {
432 SecKeychainAttributeInfo attr_info;
433 UInt32 tags[] = {kSecCreatorItemAttr};
434 attr_info.count = arraysize(tags);
435 attr_info.tag = tags;
436 attr_info.format = nullptr;
437
438 SecKeychainAttributeList* attr_list;
439 UInt32 password_length;
440 OSStatus result = keychain.ItemCopyAttributesAndData(
441 keychain_item, &attr_info, nullptr, &attr_list,
442 &password_length, nullptr);
443 if (result != noErr)
444 return false;
445 OSType creator_code = 0;
446 for (unsigned int i = 0; i < attr_list->count; i++) {
447 SecKeychainAttribute attr = attr_list->attr[i];
448 if (!attr.data) {
449 continue;
450 }
451 if (attr.tag == kSecCreatorItemAttr) {
452 creator_code = *(static_cast<FourCharCode*>(attr.data));
453 break;
454 }
455 }
456 keychain.ItemFreeAttributesAndData(attr_list, nullptr);
457 return creator_code && creator_code == base::mac::CreatorCodeForApplication();
458 }
459
460 bool FormsMatchForMerge(const PasswordForm& form_a,
461 const PasswordForm& form_b,
462 FormMatchStrictness strictness) {
463 if (IsLoginDatabaseOnlyForm(form_a) || IsLoginDatabaseOnlyForm(form_b))
464 return false;
465
466 bool equal_realm = form_a.signon_realm == form_b.signon_realm;
467 if (strictness == FUZZY_FORM_MATCH) {
468 equal_realm |= form_a.is_public_suffix_match;
469 }
470 return form_a.scheme == form_b.scheme && equal_realm &&
471 form_a.username_value == form_b.username_value;
472 }
473
474 // Moves entries from |forms| that represent either blacklisted or federated
475 // logins into |extracted|. These two types are stored only in the LoginDatabase
476 // and do not have corresponding Keychain entries.
477 void ExtractNonKeychainForms(
478 std::vector<std::unique_ptr<PasswordForm>>* forms,
479 std::vector<std::unique_ptr<PasswordForm>>* extracted) {
480 extracted->reserve(extracted->size() + forms->size());
481
482 std::vector<std::unique_ptr<PasswordForm>> remaining;
483 remaining.reserve(forms->size());
484
485 for (std::unique_ptr<PasswordForm>& form : *forms) {
486 if (IsLoginDatabaseOnlyForm(*form))
487 extracted->push_back(std::move(form));
488 else
489 remaining.push_back(std::move(form));
490 }
491 forms->swap(remaining);
492 }
493
494 // Takes |keychain_forms| and |database_forms| and moves the following 2 types
495 // of forms to |merged_forms|:
496 // (1) |database_forms| that by principle never have a corresponding Keychain
497 // entry (viz., blacklisted and federated logins),
498 // (2) |database_forms| which should have and do have a corresponding entry in
499 // |keychain_forms|.
500 // The database forms of type (2) have their password value updated from the
501 // corresponding keychain form, and all the keychain forms corresponding to some
502 // database form are removed from |keychain_forms| and deleted.
503 void MergePasswordForms(
504 std::vector<std::unique_ptr<PasswordForm>>* keychain_forms,
505 std::vector<std::unique_ptr<PasswordForm>>* database_forms,
506 std::vector<std::unique_ptr<PasswordForm>>* merged_forms) {
507 // Pull out the database blacklist items and federated logins, since they are
508 // used as-is rather than being merged with keychain forms.
509 ExtractNonKeychainForms(database_forms, merged_forms);
510
511 // Merge the normal entries.
512 std::vector<std::unique_ptr<PasswordForm>> unused_database_forms;
513 unused_database_forms.reserve(database_forms->size());
514 std::set<const PasswordForm*> used_keychain_forms;
515 // Move all database forms to either |merged_forms| or
516 // |unused_database_forms|, based on whether they have a match in the keychain
517 // forms or not. If there is a match, add its password to the DB form and
518 // mark the keychain form as used.
519 for (std::unique_ptr<PasswordForm>& db_form : *database_forms) {
520 const PasswordForm* best_match =
521 BestKeychainFormForForm(*db_form, *keychain_forms);
522 if (best_match) {
523 used_keychain_forms.insert(best_match);
524 db_form->password_value = best_match->password_value;
525 merged_forms->push_back(std::move(db_form));
526 } else {
527 unused_database_forms.push_back(std::move(db_form));
528 }
529 }
530 database_forms->swap(unused_database_forms);
531
532 // Clear out all the Keychain entries we used.
533 std::vector<std::unique_ptr<PasswordForm>> unused_keychain_forms;
534 unused_keychain_forms.reserve(keychain_forms->size());
535 for (std::unique_ptr<PasswordForm>& keychain_form : *keychain_forms) {
536 if (!base::ContainsKey(used_keychain_forms, keychain_form.get())) {
537 unused_keychain_forms.push_back(std::move(keychain_form));
538 }
539 }
540 keychain_forms->swap(unused_keychain_forms);
541 }
542
543 std::vector<ItemFormPair> ExtractAllKeychainItemAttributesIntoPasswordForms(
544 std::vector<SecKeychainItemRef>* keychain_items,
545 const AppleKeychain& keychain) {
546 DCHECK(keychain_items);
547 MacKeychainPasswordFormAdapter keychain_adapter(&keychain);
548 *keychain_items = keychain_adapter.GetAllPasswordFormKeychainItems();
549 std::vector<ItemFormPair> item_form_pairs;
550 for (auto* keychain_item : *keychain_items) {
551 std::unique_ptr<PasswordForm> form_without_password =
552 base::MakeUnique<PasswordForm>();
553 internal_keychain_helpers::FillPasswordFormFromKeychainItem(
554 keychain, keychain_item, form_without_password.get(),
555 false); // Load password attributes, but not password data.
556 item_form_pairs.push_back(
557 std::make_pair(keychain_item, std::move(form_without_password)));
558 }
559 return item_form_pairs;
560 }
561
562 void GetPasswordsForForms(
563 const AppleKeychain& keychain,
564 std::vector<std::unique_ptr<PasswordForm>>* database_forms,
565 std::vector<std::unique_ptr<PasswordForm>>* passwords) {
566 // First load the attributes of all items in the keychain without loading
567 // their password data, and then match items in |database_forms| against them.
568 // This avoids individually searching through the keychain for passwords
569 // matching each form in |database_forms|, and results in a significant
570 // performance gain, replacing O(N) keychain search operations with a single
571 // operation that loads all keychain items, and then selective reads of only
572 // the relevant passwords. See crbug.com/263685.
573 std::vector<SecKeychainItemRef> keychain_items;
574 std::vector<ItemFormPair> item_form_pairs =
575 ExtractAllKeychainItemAttributesIntoPasswordForms(&keychain_items,
576 keychain);
577
578 // Next, compare the attributes of the PasswordForms in |database_forms|
579 // against those in |item_form_pairs|, and extract password data for each
580 // matching PasswordForm using its corresponding SecKeychainItemRef.
581 std::vector<std::unique_ptr<PasswordForm>> unused_db_forms;
582 unused_db_forms.reserve(database_forms->size());
583 // Move database forms with a password stored in |keychain| to |passwords|,
584 // including the password. The rest is moved to |unused_db_forms|.
585 for (std::unique_ptr<PasswordForm>& db_form : *database_forms) {
586 std::vector<std::unique_ptr<PasswordForm>> keychain_matches =
587 ExtractPasswordsMergeableWithForm(keychain, item_form_pairs, *db_form);
588
589 std::vector<std::unique_ptr<PasswordForm>> db_form_container;
590 db_form_container.push_back(std::move(db_form));
591 MergePasswordForms(&keychain_matches, &db_form_container, passwords);
592 AppendSecondToFirst(&unused_db_forms, &db_form_container);
593 }
594 database_forms->swap(unused_db_forms);
595
596 for (SecKeychainItemRef item : keychain_items) {
597 keychain.Free(item);
598 }
599 }
600
601 // TODO(stuartmorgan): signon_realm for proxies is not yet supported.
602 bool ExtractSignonRealmComponents(const std::string& signon_realm,
603 std::string* server,
604 UInt32* port,
605 bool* is_secure,
606 std::string* security_domain) {
607 // GURL does not parse Android facet URIs correctly.
608 if (password_manager::IsValidAndroidFacetURI(signon_realm)) {
609 if (server)
610 *server = signon_realm;
611 if (is_secure)
612 *is_secure = true;
613 if (port)
614 *port = 0;
615 if (security_domain)
616 security_domain->clear();
617 return true;
618 }
619
620 // The signon_realm will be the Origin portion of a URL for an HTML form,
621 // and the same but with the security domain as a path for HTTP auth.
622 GURL realm_as_url(signon_realm);
623 if (!realm_as_url.is_valid()) {
624 return false;
625 }
626
627 if (server)
628 *server = realm_as_url.host();
629 if (is_secure)
630 *is_secure = realm_as_url.SchemeIsCryptographic();
631 if (port)
632 *port = realm_as_url.has_port() ? atoi(realm_as_url.port().c_str()) : 0;
633 if (security_domain) {
634 // Strip the leading '/' off of the path to get the security domain.
635 if (realm_as_url.path().length() > 0)
636 *security_domain = realm_as_url.path().substr(1);
637 else
638 security_domain->clear();
639 }
640 return true;
641 }
642
643 bool FormIsValidAndMatchesOtherForm(const PasswordForm& query_form,
644 const PasswordForm& other_form) {
645 std::string server;
646 std::string security_domain;
647 UInt32 port;
648 bool is_secure;
649 if (!ExtractSignonRealmComponents(query_form.signon_realm, &server, &port,
650 &is_secure, &security_domain)) {
651 return false;
652 }
653 return FormsMatchForMerge(query_form, other_form, STRICT_FORM_MATCH);
654 }
655
656 std::vector<std::unique_ptr<PasswordForm>> ExtractPasswordsMergeableWithForm(
657 const AppleKeychain& keychain,
658 const std::vector<ItemFormPair>& item_form_pairs,
659 const PasswordForm& query_form) {
660 std::vector<std::unique_ptr<PasswordForm>> matches;
661 for (std::vector<ItemFormPair>::const_iterator i = item_form_pairs.begin();
662 i != item_form_pairs.end(); ++i) {
663 if (FormIsValidAndMatchesOtherForm(query_form, *(i->second))) {
664 // Create a new object, since the caller is responsible for deleting the
665 // returned forms.
666 auto form_with_password = base::MakeUnique<PasswordForm>();
667 FillPasswordFormFromKeychainItem(
668 keychain, i->first, form_with_password.get(),
669 true); // Load password attributes and data.
670 // Do not include blacklisted items found in the keychain.
671 if (!form_with_password->blacklisted_by_user)
672 matches.push_back(std::move(form_with_password));
673 }
674 }
675 return matches;
676 }
677
678 } // namespace internal_keychain_helpers
679
680 #pragma mark -
681
682 MacKeychainPasswordFormAdapter::MacKeychainPasswordFormAdapter(
683 const AppleKeychain* keychain)
684 : keychain_(keychain), finds_only_owned_(false) {
685 }
686
687 std::vector<std::unique_ptr<PasswordForm>>
688 MacKeychainPasswordFormAdapter::PasswordsFillingForm(
689 const std::string& signon_realm,
690 PasswordForm::Scheme scheme) {
691 std::vector<SecKeychainItemRef> keychain_items =
692 MatchingKeychainItems(signon_realm, scheme, NULL, NULL);
693 return ConvertKeychainItemsToForms(&keychain_items);
694 }
695
696 bool MacKeychainPasswordFormAdapter::HasPasswordExactlyMatchingForm(
697 const PasswordForm& query_form) {
698 SecKeychainItemRef keychain_item = KeychainItemForForm(query_form);
699 if (keychain_item) {
700 keychain_->Free(keychain_item);
701 return true;
702 }
703 return false;
704 }
705
706 bool MacKeychainPasswordFormAdapter::HasPasswordsMergeableWithForm(
707 const PasswordForm& query_form) {
708 if (IsLoginDatabaseOnlyForm(query_form))
709 return false;
710 std::string username = base::UTF16ToUTF8(query_form.username_value);
711 std::vector<SecKeychainItemRef> matches =
712 MatchingKeychainItems(query_form.signon_realm, query_form.scheme,
713 NULL, username.c_str());
714 for (SecKeychainItemRef item : matches)
715 keychain_->Free(item);
716
717 return !matches.empty();
718 }
719
720 std::vector<SecKeychainItemRef>
721 MacKeychainPasswordFormAdapter::GetAllPasswordFormKeychainItems() {
722 SecAuthenticationType supported_auth_types[] = {
723 kSecAuthenticationTypeHTMLForm,
724 kSecAuthenticationTypeHTTPBasic,
725 kSecAuthenticationTypeHTTPDigest,
726 };
727
728 std::vector<SecKeychainItemRef> matches;
729 for (unsigned int i = 0; i < arraysize(supported_auth_types); ++i) {
730 KeychainSearch keychain_search(*keychain_);
731 OSType creator = CreatorCodeForSearch();
732 keychain_search.Init(NULL,
733 NULL,
734 NULL,
735 &supported_auth_types[i],
736 NULL,
737 NULL,
738 NULL,
739 creator ? &creator : NULL);
740 keychain_search.FindMatchingItems(&matches);
741 }
742 return matches;
743 }
744
745 std::vector<std::unique_ptr<PasswordForm>>
746 MacKeychainPasswordFormAdapter::GetAllPasswordFormPasswords() {
747 std::vector<SecKeychainItemRef> items = GetAllPasswordFormKeychainItems();
748 return ConvertKeychainItemsToForms(&items);
749 }
750
751 bool MacKeychainPasswordFormAdapter::AddPassword(const PasswordForm& form) {
752 // We should never be trying to store a blacklist in the keychain.
753 DCHECK(!IsLoginDatabaseOnlyForm(form));
754
755 std::string server;
756 std::string security_domain;
757 UInt32 port;
758 bool is_secure;
759 if (!internal_keychain_helpers::ExtractSignonRealmComponents(
760 form.signon_realm, &server, &port, &is_secure, &security_domain)) {
761 return false;
762 }
763 std::string path;
764 // Path doesn't make sense for Android app credentials.
765 if (!password_manager::IsValidAndroidFacetURI(form.signon_realm))
766 path = form.origin.path();
767 std::string username = base::UTF16ToUTF8(form.username_value);
768 std::string password = base::UTF16ToUTF8(form.password_value);
769 SecProtocolType protocol = is_secure ? kSecProtocolTypeHTTPS
770 : kSecProtocolTypeHTTP;
771 SecKeychainItemRef new_item = NULL;
772 OSStatus result = keychain_->AddInternetPassword(
773 NULL, server.size(), server.c_str(),
774 security_domain.size(), security_domain.c_str(),
775 username.size(), username.c_str(),
776 path.size(), path.c_str(),
777 port, protocol, AuthTypeForScheme(form.scheme),
778 password.size(), password.c_str(), &new_item);
779
780 if (result == noErr) {
781 SetKeychainItemCreatorCode(new_item,
782 base::mac::CreatorCodeForApplication());
783 keychain_->Free(new_item);
784 } else if (result == errSecDuplicateItem) {
785 // If we collide with an existing item, find and update it instead.
786 SecKeychainItemRef existing_item = KeychainItemForForm(form);
787 if (!existing_item) {
788 return false;
789 }
790 bool changed = SetKeychainItemPassword(existing_item, password);
791 keychain_->Free(existing_item);
792 return changed;
793 }
794
795 return result == noErr;
796 }
797
798 bool MacKeychainPasswordFormAdapter::RemovePassword(const PasswordForm& form) {
799 SecKeychainItemRef keychain_item = KeychainItemForForm(form);
800 if (keychain_item == NULL)
801 return false;
802 OSStatus result = keychain_->ItemDelete(keychain_item);
803 keychain_->Free(keychain_item);
804 return result == noErr;
805 }
806
807 void MacKeychainPasswordFormAdapter::SetFindsOnlyOwnedItems(
808 bool finds_only_owned) {
809 finds_only_owned_ = finds_only_owned;
810 }
811
812 std::vector<std::unique_ptr<PasswordForm>>
813 MacKeychainPasswordFormAdapter::ConvertKeychainItemsToForms(
814 std::vector<SecKeychainItemRef>* items) {
815 std::vector<std::unique_ptr<PasswordForm>> forms;
816 for (SecKeychainItemRef item : *items) {
817 auto form = base::MakeUnique<PasswordForm>();
818 if (internal_keychain_helpers::FillPasswordFormFromKeychainItem(
819 *keychain_, item, form.get(), true)) {
820 forms.push_back(std::move(form));
821 }
822 keychain_->Free(item);
823 }
824 items->clear();
825 return forms;
826 }
827
828 SecKeychainItemRef MacKeychainPasswordFormAdapter::KeychainItemForForm(
829 const PasswordForm& form) {
830 // We don't store blacklist entries in the keychain, so the answer to "what
831 // Keychain item goes with this form" is always "nothing" for blacklists.
832 // Same goes for federated logins.
833 if (IsLoginDatabaseOnlyForm(form))
834 return NULL;
835
836 std::string path;
837 // Path doesn't make sense for Android app credentials.
838 if (!password_manager::IsValidAndroidFacetURI(form.signon_realm))
839 path = form.origin.path();
840 std::string username = base::UTF16ToUTF8(form.username_value);
841 std::vector<SecKeychainItemRef> matches = MatchingKeychainItems(
842 form.signon_realm, form.scheme, path.c_str(), username.c_str());
843
844 if (matches.empty()) {
845 return NULL;
846 }
847
848 // Free all items after the first, since we won't be returning them.
849 for (auto i = matches.begin() + 1; i != matches.end(); ++i)
850 keychain_->Free(*i);
851
852 return matches[0];
853 }
854
855 std::vector<SecKeychainItemRef>
856 MacKeychainPasswordFormAdapter::MatchingKeychainItems(
857 const std::string& signon_realm,
858 PasswordForm::Scheme scheme,
859 const char* path,
860 const char* username) {
861 std::vector<SecKeychainItemRef> matches;
862
863 std::string server;
864 std::string security_domain;
865 UInt32 port;
866 bool is_secure;
867 if (!internal_keychain_helpers::ExtractSignonRealmComponents(
868 signon_realm, &server, &port, &is_secure, &security_domain)) {
869 // TODO(stuartmorgan): Proxies will currently fail here, since their
870 // signon_realm is not a URL. We need to detect the proxy case and handle
871 // it specially.
872 return matches;
873 }
874 SecProtocolType protocol = is_secure ? kSecProtocolTypeHTTPS
875 : kSecProtocolTypeHTTP;
876 SecAuthenticationType auth_type = AuthTypeForScheme(scheme);
877 const char* auth_domain = (scheme == PasswordForm::SCHEME_HTML) ?
878 NULL : security_domain.c_str();
879 OSType creator = CreatorCodeForSearch();
880 KeychainSearch keychain_search(*keychain_);
881 keychain_search.Init(server.c_str(),
882 &port,
883 &protocol,
884 &auth_type,
885 auth_domain,
886 path,
887 username,
888 creator ? &creator : NULL);
889 keychain_search.FindMatchingItems(&matches);
890 return matches;
891 }
892
893 // Returns the Keychain SecAuthenticationType type corresponding to |scheme|.
894 SecAuthenticationType MacKeychainPasswordFormAdapter::AuthTypeForScheme(
895 PasswordForm::Scheme scheme) {
896 switch (scheme) {
897 case PasswordForm::SCHEME_HTML: return kSecAuthenticationTypeHTMLForm;
898 case PasswordForm::SCHEME_BASIC: return kSecAuthenticationTypeHTTPBasic;
899 case PasswordForm::SCHEME_DIGEST: return kSecAuthenticationTypeHTTPDigest;
900 case PasswordForm::SCHEME_OTHER: return kSecAuthenticationTypeDefault;
901 case PasswordForm::SCHEME_USERNAME_ONLY:
902 NOTREACHED();
903 break;
904 }
905 NOTREACHED();
906 return kSecAuthenticationTypeDefault;
907 }
908
909 bool MacKeychainPasswordFormAdapter::SetKeychainItemPassword(
910 SecKeychainItemRef keychain_item,
911 const std::string& password) {
912 OSStatus result = keychain_->ItemModifyAttributesAndData(keychain_item, NULL,
913 password.size(),
914 password.c_str());
915 return result == noErr;
916 }
917
918 bool MacKeychainPasswordFormAdapter::SetKeychainItemCreatorCode(
919 SecKeychainItemRef keychain_item,
920 OSType creator_code) {
921 SecKeychainAttribute attr = { kSecCreatorItemAttr, sizeof(creator_code),
922 &creator_code };
923 SecKeychainAttributeList attrList = { 1, &attr };
924 OSStatus result = keychain_->ItemModifyAttributesAndData(keychain_item,
925 &attrList, 0, NULL);
926 return result == noErr;
927 }
928
929 OSType MacKeychainPasswordFormAdapter::CreatorCodeForSearch() {
930 return finds_only_owned_ ? base::mac::CreatorCodeForApplication() : 0;
931 }
932
933 #pragma mark -
934
935 PasswordStoreMac::PasswordStoreMac(
936 scoped_refptr<base::SingleThreadTaskRunner> main_thread_runner,
937 scoped_refptr<base::SingleThreadTaskRunner> db_thread_runner,
938 std::unique_ptr<AppleKeychain> keychain)
939 : password_manager::PasswordStore(main_thread_runner, db_thread_runner),
940 keychain_(std::move(keychain)),
941 login_metadata_db_(nullptr) {
942 DCHECK(keychain_);
943 }
944
945 PasswordStoreMac::~PasswordStoreMac() {}
946
947 void PasswordStoreMac::InitWithTaskRunner(
948 scoped_refptr<base::SingleThreadTaskRunner> background_task_runner) {
949 db_thread_runner_ = background_task_runner;
950 DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread());
951 }
952
953 // static
954 PasswordStoreMac::MigrationResult PasswordStoreMac::ImportFromKeychain(
955 password_manager::LoginDatabase* login_db,
956 crypto::AppleKeychain* keychain) {
957 std::vector<std::unique_ptr<PasswordForm>> database_forms;
958 if (!login_db->GetAutofillableLogins(&database_forms))
959 return LOGIN_DB_FAILURE;
960
961 std::vector<std::unique_ptr<PasswordForm>> uninteresting_forms;
962 internal_keychain_helpers::ExtractNonKeychainForms(&database_forms,
963 &uninteresting_forms);
964 // If there are no passwords to lookup in the Keychain then we're done.
965 if (database_forms.empty())
966 return MIGRATION_OK;
967
968 // Make sure that the encryption key is retrieved from the Keychain so the
969 // encryption can be done.
970 std::string ciphertext;
971 if (!OSCrypt::EncryptString("test", &ciphertext))
972 return ENCRYPTOR_FAILURE;
973
974 // Mute the Keychain.
975 chrome::ScopedSecKeychainSetUserInteractionAllowed user_interaction_allowed(
976 false);
977
978 // Retrieve the passwords.
979 // Get all the password attributes first.
980 std::vector<SecKeychainItemRef> keychain_items;
981 std::vector<internal_keychain_helpers::ItemFormPair> item_form_pairs =
982 internal_keychain_helpers::
983 ExtractAllKeychainItemAttributesIntoPasswordForms(&keychain_items,
984 *keychain);
985
986 // Next, compare the attributes of the PasswordForms in |database_forms|
987 // against those in |item_form_pairs|, and extract password data for each
988 // matching PasswordForm using its corresponding SecKeychainItemRef.
989 size_t unmerged_forms_count = 0;
990 login_db->set_clear_password_values(false);
991 for (const auto& form : database_forms) {
992 std::vector<std::unique_ptr<PasswordForm>> keychain_matches =
993 internal_keychain_helpers::ExtractPasswordsMergeableWithForm(
994 *keychain, item_form_pairs, *form);
995
996 const PasswordForm* best_match =
997 BestKeychainFormForForm(*form, keychain_matches);
998 if (best_match) {
999 form->password_value = best_match->password_value;
1000 PasswordStoreChangeList change = login_db->UpdateLogin(*form);
1001 DCHECK_EQ(1u, change.size());
1002 } else {
1003 unmerged_forms_count++;
1004 bool removed = login_db->RemoveLogin(*form);
1005 DCHECK(removed);
1006 }
1007 }
1008 for (SecKeychainItemRef item : keychain_items)
1009 keychain->Free(item);
1010
1011 if (unmerged_forms_count) {
1012 UMA_HISTOGRAM_COUNTS(
1013 "PasswordManager.KeychainMigration.NumPasswordsOnFailure",
1014 database_forms.size());
1015 UMA_HISTOGRAM_COUNTS("PasswordManager.KeychainMigration.NumFailedPasswords",
1016 unmerged_forms_count);
1017 return MIGRATION_PARTIAL;
1018 }
1019 return MIGRATION_OK;
1020 }
1021
1022 // static
1023 void PasswordStoreMac::CleanUpKeychain(
1024 crypto::AppleKeychain* keychain,
1025 const std::vector<std::unique_ptr<PasswordForm>>& forms) {
1026 MacKeychainPasswordFormAdapter keychain_adapter(keychain);
1027 keychain_adapter.SetFindsOnlyOwnedItems(true);
1028 for (const auto& form : forms)
1029 keychain_adapter.RemovePassword(*form);
1030 }
1031
1032 void PasswordStoreMac::set_login_metadata_db(
1033 password_manager::LoginDatabase* login_db) {
1034 login_metadata_db_ = login_db;
1035 if (login_metadata_db_)
1036 login_metadata_db_->set_clear_password_values(true);
1037 }
1038
1039 bool PasswordStoreMac::Init(
1040 const syncer::SyncableService::StartSyncFlare& flare,
1041 PrefService* prefs) {
1042 // The class should be used inside PasswordStoreProxyMac only.
1043 NOTREACHED();
1044 return true;
1045 }
1046
1047 void PasswordStoreMac::ReportMetricsImpl(const std::string& sync_username,
1048 bool custom_passphrase_sync_enabled) {
1049 if (!login_metadata_db_)
1050 return;
1051 login_metadata_db_->ReportMetrics(sync_username,
1052 custom_passphrase_sync_enabled);
1053 }
1054
1055 PasswordStoreChangeList PasswordStoreMac::AddLoginImpl(
1056 const PasswordForm& form) {
1057 DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread());
1058 if (login_metadata_db_ && AddToKeychainIfNecessary(form))
1059 return login_metadata_db_->AddLogin(form);
1060 return PasswordStoreChangeList();
1061 }
1062
1063 PasswordStoreChangeList PasswordStoreMac::UpdateLoginImpl(
1064 const PasswordForm& form) {
1065 DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread());
1066 if (!login_metadata_db_)
1067 return PasswordStoreChangeList();
1068
1069 PasswordStoreChangeList changes = login_metadata_db_->UpdateLogin(form);
1070
1071 MacKeychainPasswordFormAdapter keychain_adapter(keychain_.get());
1072 if (changes.empty() &&
1073 !keychain_adapter.HasPasswordsMergeableWithForm(form)) {
1074 // If the password isn't in either the DB or the keychain, then it must have
1075 // been deleted after autofill happened, and should not be re-added.
1076 return changes;
1077 }
1078
1079 // The keychain add will update if there is a collision and add if there
1080 // isn't, which is the behavior we want, so there's no separate update call.
1081 if (AddToKeychainIfNecessary(form) && changes.empty()) {
1082 changes = login_metadata_db_->AddLogin(form);
1083 }
1084 return changes;
1085 }
1086
1087 PasswordStoreChangeList PasswordStoreMac::RemoveLoginImpl(
1088 const PasswordForm& form) {
1089 DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread());
1090 PasswordStoreChangeList changes;
1091 if (login_metadata_db_ && login_metadata_db_->RemoveLogin(form)) {
1092 // See if we own a Keychain item associated with this item. We can do an
1093 // exact search rather than messing around with trying to do fuzzy matching
1094 // because passwords that we created will always have an exact-match
1095 // database entry.
1096 // (If a user does lose their profile but not their keychain we'll treat the
1097 // entries we find like other imported entries anyway, so it's reasonable to
1098 // handle deletes on them the way we would for an imported item.)
1099 MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_.get());
1100 owned_keychain_adapter.SetFindsOnlyOwnedItems(true);
1101 if (owned_keychain_adapter.HasPasswordExactlyMatchingForm(form)) {
1102 // If we don't have other forms using it (i.e., a form differing only by
1103 // the names of the form elements), delete the keychain entry.
1104 if (!DatabaseHasFormMatchingKeychainForm(form)) {
1105 owned_keychain_adapter.RemovePassword(form);
1106 }
1107 }
1108
1109 changes.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE, form));
1110 }
1111 return changes;
1112 }
1113
1114 PasswordStoreChangeList PasswordStoreMac::RemoveLoginsByURLAndTimeImpl(
1115 const base::Callback<bool(const GURL&)>& url_filter,
1116 base::Time delete_begin,
1117 base::Time delete_end) {
1118 PasswordStoreChangeList changes;
1119 std::vector<std::unique_ptr<PasswordForm>> forms_to_consider;
1120 std::vector<std::unique_ptr<PasswordForm>> forms_to_remove;
1121 if (login_metadata_db_ &&
1122 login_metadata_db_->GetLoginsCreatedBetween(delete_begin, delete_end,
1123 &forms_to_consider)) {
1124 for (std::unique_ptr<PasswordForm>& form_to_consider : forms_to_consider) {
1125 if (url_filter.Run(form_to_consider->origin) &&
1126 login_metadata_db_->RemoveLogin(*form_to_consider))
1127 forms_to_remove.push_back(std::move(form_to_consider));
1128 }
1129 if (!forms_to_remove.empty()) {
1130 RemoveKeychainForms(forms_to_remove);
1131 CleanOrphanedForms(&forms_to_remove); // Add the orphaned forms.
1132 changes = FormsToRemoveChangeList(forms_to_remove);
1133 LogStatsForBulkDeletion(changes.size());
1134 }
1135 }
1136 return changes;
1137 }
1138
1139 PasswordStoreChangeList PasswordStoreMac::RemoveLoginsCreatedBetweenImpl(
1140 base::Time delete_begin,
1141 base::Time delete_end) {
1142 PasswordStoreChangeList changes;
1143 std::vector<std::unique_ptr<PasswordForm>> forms_to_remove;
1144 if (login_metadata_db_ &&
1145 login_metadata_db_->GetLoginsCreatedBetween(delete_begin, delete_end,
1146 &forms_to_remove) &&
1147 login_metadata_db_->RemoveLoginsCreatedBetween(delete_begin,
1148 delete_end)) {
1149 RemoveKeychainForms(forms_to_remove);
1150 CleanOrphanedForms(&forms_to_remove); // Add the orphaned forms.
1151 changes = FormsToRemoveChangeList(forms_to_remove);
1152 LogStatsForBulkDeletion(changes.size());
1153 }
1154 return changes;
1155 }
1156
1157 PasswordStoreChangeList PasswordStoreMac::RemoveLoginsSyncedBetweenImpl(
1158 base::Time delete_begin,
1159 base::Time delete_end) {
1160 PasswordStoreChangeList changes;
1161 std::vector<std::unique_ptr<PasswordForm>> forms_to_remove;
1162 if (login_metadata_db_ &&
1163 login_metadata_db_->GetLoginsSyncedBetween(delete_begin, delete_end,
1164 &forms_to_remove) &&
1165 login_metadata_db_->RemoveLoginsSyncedBetween(delete_begin, delete_end)) {
1166 RemoveKeychainForms(forms_to_remove);
1167 CleanOrphanedForms(&forms_to_remove); // Add the orphaned forms_to_remove.
1168 changes = FormsToRemoveChangeList(forms_to_remove);
1169 LogStatsForBulkDeletionDuringRollback(changes.size());
1170 }
1171 return changes;
1172 }
1173
1174 PasswordStoreChangeList PasswordStoreMac::DisableAutoSignInForOriginsImpl(
1175 const base::Callback<bool(const GURL&)>& origin_filter) {
1176 std::vector<std::unique_ptr<PasswordForm>> forms;
1177 PasswordStoreChangeList changes;
1178 if (!login_metadata_db_ ||
1179 !login_metadata_db_->GetAutoSignInLogins(&forms)) {
1180 return changes;
1181 }
1182
1183 std::set<GURL> origins_to_update;
1184 for (const auto& form : forms) {
1185 if (origin_filter.Run(form->origin))
1186 origins_to_update.insert(form->origin);
1187 }
1188
1189 std::set<GURL> origins_updated;
1190 for (const GURL& origin : origins_to_update) {
1191 if (login_metadata_db_->DisableAutoSignInForOrigin(origin))
1192 origins_updated.insert(origin);
1193 }
1194
1195 for (const auto& form : forms) {
1196 if (origins_updated.count(form->origin)) {
1197 changes.push_back(
1198 PasswordStoreChange(PasswordStoreChange::UPDATE, *form));
1199 }
1200 }
1201
1202 return changes;
1203 }
1204
1205 bool PasswordStoreMac::RemoveStatisticsByOriginAndTimeImpl(
1206 const base::Callback<bool(const GURL&)>& origin_filter,
1207 base::Time delete_begin,
1208 base::Time delete_end) {
1209 return login_metadata_db_ &&
1210 login_metadata_db_->stats_table().RemoveStatsByOriginAndTime(
1211 origin_filter, delete_begin, delete_end);
1212 }
1213
1214 std::vector<std::unique_ptr<PasswordForm>> PasswordStoreMac::FillMatchingLogins(
1215 const FormDigest& form) {
1216 chrome::ScopedSecKeychainSetUserInteractionAllowed user_interaction_allowed(
1217 false);
1218
1219 std::vector<std::unique_ptr<PasswordForm>> database_forms;
1220 if (!login_metadata_db_ ||
1221 !login_metadata_db_->GetLogins(form, &database_forms)) {
1222 return std::vector<std::unique_ptr<PasswordForm>>();
1223 }
1224
1225 // Let's gather all signon realms we want to match with keychain entries.
1226 std::set<std::string> realm_set;
1227 realm_set.insert(form.signon_realm);
1228 for (const std::unique_ptr<PasswordForm>& db_form : database_forms) {
1229 // TODO(vabr): We should not be getting different schemes here.
1230 // http://crbug.com/340112
1231 if (form.scheme != db_form->scheme)
1232 continue; // Forms with different schemes never match.
1233 if (db_form->is_public_suffix_match || db_form->is_affiliation_based_match)
1234 realm_set.insert(db_form->signon_realm);
1235 }
1236 std::vector<std::unique_ptr<PasswordForm>> keychain_forms;
1237 for (std::set<std::string>::const_iterator realm = realm_set.begin();
1238 realm != realm_set.end(); ++realm) {
1239 MacKeychainPasswordFormAdapter keychain_adapter(keychain_.get());
1240 std::vector<std::unique_ptr<PasswordForm>> temp_keychain_forms =
1241 keychain_adapter.PasswordsFillingForm(*realm, form.scheme);
1242 AppendSecondToFirst(&keychain_forms, &temp_keychain_forms);
1243 }
1244
1245 std::vector<std::unique_ptr<PasswordForm>> matched_forms;
1246 internal_keychain_helpers::MergePasswordForms(
1247 &keychain_forms, &database_forms, &matched_forms);
1248
1249 // Strip any blacklist entries out of the unused Keychain array, then take
1250 // all the entries that are left (which we can use as imported passwords).
1251 std::vector<std::unique_ptr<PasswordForm>> keychain_blacklist_forms;
1252 internal_keychain_helpers::ExtractNonKeychainForms(&keychain_forms,
1253 &keychain_blacklist_forms);
1254 AppendSecondToFirst(&matched_forms, &keychain_forms);
1255
1256 if (!database_forms.empty()) {
1257 RemoveDatabaseForms(&database_forms);
1258 NotifyLoginsChanged(FormsToRemoveChangeList(database_forms));
1259 }
1260
1261 return matched_forms;
1262 }
1263
1264 std::vector<std::unique_ptr<autofill::PasswordForm>>
1265 PasswordStoreMac::FillLoginsForSameOrganizationName(
1266 const std::string& signon_realm) {
1267 // Not implemented.
1268 return std::vector<std::unique_ptr<autofill::PasswordForm>>();
1269 }
1270
1271 bool PasswordStoreMac::FillAutofillableLogins(
1272 std::vector<std::unique_ptr<PasswordForm>>* forms) {
1273 DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread());
1274 forms->clear();
1275
1276 std::vector<std::unique_ptr<PasswordForm>> database_forms;
1277 if (!login_metadata_db_ ||
1278 !login_metadata_db_->GetAutofillableLogins(&database_forms))
1279 return false;
1280
1281 internal_keychain_helpers::GetPasswordsForForms(*keychain_, &database_forms,
1282 forms);
1283
1284 if (!database_forms.empty()) {
1285 RemoveDatabaseForms(&database_forms);
1286 NotifyLoginsChanged(FormsToRemoveChangeList(database_forms));
1287 }
1288
1289 return true;
1290 }
1291
1292 bool PasswordStoreMac::FillBlacklistLogins(
1293 std::vector<std::unique_ptr<PasswordForm>>* forms) {
1294 DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread());
1295 return login_metadata_db_ && login_metadata_db_->GetBlacklistLogins(forms);
1296 }
1297
1298 void PasswordStoreMac::AddSiteStatsImpl(
1299 const password_manager::InteractionsStats& stats) {
1300 DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread());
1301 if (login_metadata_db_)
1302 login_metadata_db_->stats_table().AddRow(stats);
1303 }
1304
1305 void PasswordStoreMac::RemoveSiteStatsImpl(const GURL& origin_domain) {
1306 DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread());
1307 if (login_metadata_db_)
1308 login_metadata_db_->stats_table().RemoveRow(origin_domain);
1309 }
1310
1311 std::vector<password_manager::InteractionsStats>
1312 PasswordStoreMac::GetAllSiteStatsImpl() {
1313 DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread());
1314 return login_metadata_db_
1315 ? login_metadata_db_->stats_table().GetAllRows()
1316 : std::vector<password_manager::InteractionsStats>();
1317 }
1318
1319 std::vector<password_manager::InteractionsStats>
1320 PasswordStoreMac::GetSiteStatsImpl(const GURL& origin_domain) {
1321 DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread());
1322 return login_metadata_db_
1323 ? login_metadata_db_->stats_table().GetRows(origin_domain)
1324 : std::vector<password_manager::InteractionsStats>();
1325 }
1326
1327 bool PasswordStoreMac::AddToKeychainIfNecessary(const PasswordForm& form) {
1328 if (IsLoginDatabaseOnlyForm(form))
1329 return true;
1330 MacKeychainPasswordFormAdapter keychainAdapter(keychain_.get());
1331 return keychainAdapter.AddPassword(form);
1332 }
1333
1334 bool PasswordStoreMac::DatabaseHasFormMatchingKeychainForm(
1335 const PasswordForm& form) {
1336 DCHECK(login_metadata_db_);
1337 bool has_match = false;
1338 std::vector<std::unique_ptr<PasswordForm>> database_forms;
1339 if (!login_metadata_db_->GetLogins(
1340 password_manager::PasswordStore::FormDigest(form), &database_forms))
1341 return false;
1342 for (const auto& db_form : database_forms) {
1343 // Below we filter out fuzzy matched forms because we are only interested
1344 // in exact ones.
1345 if (!db_form->is_public_suffix_match &&
1346 internal_keychain_helpers::FormsMatchForMerge(
1347 form, *db_form, internal_keychain_helpers::STRICT_FORM_MATCH) &&
1348 db_form->origin == form.origin) {
1349 has_match = true;
1350 break;
1351 }
1352 }
1353 return has_match;
1354 }
1355
1356 void PasswordStoreMac::RemoveDatabaseForms(
1357 std::vector<std::unique_ptr<PasswordForm>>* forms) {
1358 DCHECK(login_metadata_db_);
1359 std::vector<std::unique_ptr<PasswordForm>> removed_forms;
1360 for (std::unique_ptr<PasswordForm>& form : *forms) {
1361 if (login_metadata_db_->RemoveLogin(*form))
1362 removed_forms.push_back(std::move(form));
1363 }
1364 removed_forms.swap(*forms);
1365 }
1366
1367 void PasswordStoreMac::RemoveKeychainForms(
1368 const std::vector<std::unique_ptr<PasswordForm>>& forms) {
1369 MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_.get());
1370 owned_keychain_adapter.SetFindsOnlyOwnedItems(true);
1371 for (const auto& form : forms) {
1372 owned_keychain_adapter.RemovePassword(*form);
1373 }
1374 }
1375
1376 void PasswordStoreMac::CleanOrphanedForms(
1377 std::vector<std::unique_ptr<PasswordForm>>* orphaned_forms) {
1378 DCHECK(orphaned_forms);
1379 DCHECK(login_metadata_db_);
1380
1381 std::vector<std::unique_ptr<PasswordForm>> database_forms;
1382 if (!login_metadata_db_->GetAutofillableLogins(&database_forms))
1383 return;
1384
1385 // Filter forms with corresponding Keychain entry out of |database_forms|.
1386 std::vector<std::unique_ptr<PasswordForm>> forms_with_keychain_entry;
1387 internal_keychain_helpers::GetPasswordsForForms(*keychain_, &database_forms,
1388 &forms_with_keychain_entry);
1389
1390 // Clean up any orphaned database entries.
1391 RemoveDatabaseForms(&database_forms);
1392
1393 // Move the orphaned DB forms to the output parameter.
1394 AppendSecondToFirst(orphaned_forms, &database_forms);
1395 }
OLDNEW
« no previous file with comments | « chrome/browser/password_manager/password_store_mac.h ('k') | chrome/browser/password_manager/password_store_mac_internal.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698