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