• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "base/logging.h"
6 #include "base/time/time.h"
7 #include "crypto/mock_apple_keychain.h"
8 
9 namespace crypto {
10 
11 // static
12 const SecKeychainSearchRef MockAppleKeychain::kDummySearchRef =
13     reinterpret_cast<SecKeychainSearchRef>(1000);
14 
MockAppleKeychain()15 MockAppleKeychain::MockAppleKeychain()
16     : next_item_key_(0),
17       search_copy_count_(0),
18       keychain_item_copy_count_(0),
19       attribute_data_copy_count_(0),
20       find_generic_result_(noErr),
21       called_add_generic_(false),
22       password_data_count_(0) {}
23 
InitializeKeychainData(MockKeychainItemType key) const24 void MockAppleKeychain::InitializeKeychainData(MockKeychainItemType key) const {
25   UInt32 tags[] = { kSecAccountItemAttr,
26                     kSecServerItemAttr,
27                     kSecPortItemAttr,
28                     kSecPathItemAttr,
29                     kSecProtocolItemAttr,
30                     kSecAuthenticationTypeItemAttr,
31                     kSecSecurityDomainItemAttr,
32                     kSecCreationDateItemAttr,
33                     kSecNegativeItemAttr,
34                     kSecCreatorItemAttr };
35   keychain_attr_list_[key] = SecKeychainAttributeList();
36   keychain_data_[key] = KeychainPasswordData();
37   keychain_attr_list_[key].count = arraysize(tags);
38   keychain_attr_list_[key].attr = static_cast<SecKeychainAttribute*>(
39       calloc(keychain_attr_list_[key].count, sizeof(SecKeychainAttribute)));
40   for (unsigned int i = 0; i < keychain_attr_list_[key].count; ++i) {
41     keychain_attr_list_[key].attr[i].tag = tags[i];
42     size_t data_size = 0;
43     switch (tags[i]) {
44       case kSecPortItemAttr:
45         data_size = sizeof(UInt32);
46         break;
47       case kSecProtocolItemAttr:
48         data_size = sizeof(SecProtocolType);
49         break;
50       case kSecAuthenticationTypeItemAttr:
51         data_size = sizeof(SecAuthenticationType);
52         break;
53       case kSecNegativeItemAttr:
54         data_size = sizeof(Boolean);
55         break;
56       case kSecCreatorItemAttr:
57         data_size = sizeof(OSType);
58         break;
59     }
60     if (data_size > 0) {
61       keychain_attr_list_[key].attr[i].length = data_size;
62       keychain_attr_list_[key].attr[i].data = calloc(1, data_size);
63     }
64   }
65 }
66 
~MockAppleKeychain()67 MockAppleKeychain::~MockAppleKeychain() {
68   for (MockKeychainAttributesMap::iterator it = keychain_attr_list_.begin();
69        it != keychain_attr_list_.end();
70        ++it) {
71     for (unsigned int i = 0; i < it->second.count; ++i) {
72       if (it->second.attr[i].data)
73         free(it->second.attr[i].data);
74     }
75     free(it->second.attr);
76     if (keychain_data_[it->first].data)
77       free(keychain_data_[it->first].data);
78   }
79   keychain_attr_list_.clear();
80   keychain_data_.clear();
81 }
82 
AttributeWithTag(const SecKeychainAttributeList & attribute_list,UInt32 tag)83 SecKeychainAttribute* MockAppleKeychain::AttributeWithTag(
84     const SecKeychainAttributeList& attribute_list,
85     UInt32 tag) {
86   int attribute_index = -1;
87   for (unsigned int i = 0; i < attribute_list.count; ++i) {
88     if (attribute_list.attr[i].tag == tag) {
89       attribute_index = i;
90       break;
91     }
92   }
93   if (attribute_index == -1) {
94     NOTREACHED() << "Unsupported attribute: " << tag;
95     return NULL;
96   }
97   return &(attribute_list.attr[attribute_index]);
98 }
99 
SetTestDataBytes(MockKeychainItemType item,UInt32 tag,const void * data,size_t length)100 void MockAppleKeychain::SetTestDataBytes(MockKeychainItemType item,
101                                          UInt32 tag,
102                                          const void* data,
103                                          size_t length) {
104   SecKeychainAttribute* attribute = AttributeWithTag(keychain_attr_list_[item],
105                                                      tag);
106   attribute->length = length;
107   if (length > 0) {
108     if (attribute->data)
109       free(attribute->data);
110     attribute->data = malloc(length);
111     CHECK(attribute->data);
112     memcpy(attribute->data, data, length);
113   } else {
114     attribute->data = NULL;
115   }
116 }
117 
SetTestDataString(MockKeychainItemType item,UInt32 tag,const char * value)118 void MockAppleKeychain::SetTestDataString(MockKeychainItemType item,
119                                           UInt32 tag,
120                                           const char* value) {
121   SetTestDataBytes(item, tag, value, value ? strlen(value) : 0);
122 }
123 
SetTestDataPort(MockKeychainItemType item,UInt32 value)124 void MockAppleKeychain::SetTestDataPort(MockKeychainItemType item,
125                                         UInt32 value) {
126   SecKeychainAttribute* attribute = AttributeWithTag(keychain_attr_list_[item],
127                                                      kSecPortItemAttr);
128   UInt32* data = static_cast<UInt32*>(attribute->data);
129   *data = value;
130 }
131 
SetTestDataProtocol(MockKeychainItemType item,SecProtocolType value)132 void MockAppleKeychain::SetTestDataProtocol(MockKeychainItemType item,
133                                             SecProtocolType value) {
134   SecKeychainAttribute* attribute = AttributeWithTag(keychain_attr_list_[item],
135                                                      kSecProtocolItemAttr);
136   SecProtocolType* data = static_cast<SecProtocolType*>(attribute->data);
137   *data = value;
138 }
139 
SetTestDataAuthType(MockKeychainItemType item,SecAuthenticationType value)140 void MockAppleKeychain::SetTestDataAuthType(MockKeychainItemType item,
141                                             SecAuthenticationType value) {
142   SecKeychainAttribute* attribute = AttributeWithTag(
143       keychain_attr_list_[item], kSecAuthenticationTypeItemAttr);
144   SecAuthenticationType* data = static_cast<SecAuthenticationType*>(
145       attribute->data);
146   *data = value;
147 }
148 
SetTestDataNegativeItem(MockKeychainItemType item,Boolean value)149 void MockAppleKeychain::SetTestDataNegativeItem(MockKeychainItemType item,
150                                                 Boolean value) {
151   SecKeychainAttribute* attribute = AttributeWithTag(keychain_attr_list_[item],
152                                                      kSecNegativeItemAttr);
153   Boolean* data = static_cast<Boolean*>(attribute->data);
154   *data = value;
155 }
156 
SetTestDataCreator(MockKeychainItemType item,OSType value)157 void MockAppleKeychain::SetTestDataCreator(MockKeychainItemType item,
158                                            OSType value) {
159   SecKeychainAttribute* attribute = AttributeWithTag(keychain_attr_list_[item],
160                                                      kSecCreatorItemAttr);
161   OSType* data = static_cast<OSType*>(attribute->data);
162   *data = value;
163 }
164 
SetTestDataPasswordBytes(MockKeychainItemType item,const void * data,size_t length)165 void MockAppleKeychain::SetTestDataPasswordBytes(MockKeychainItemType item,
166                                                  const void* data,
167                                                  size_t length) {
168   keychain_data_[item].length = length;
169   if (length > 0) {
170     if (keychain_data_[item].data)
171       free(keychain_data_[item].data);
172     keychain_data_[item].data = malloc(length);
173     memcpy(keychain_data_[item].data, data, length);
174   } else {
175     keychain_data_[item].data = NULL;
176   }
177 }
178 
SetTestDataPasswordString(MockKeychainItemType item,const char * value)179 void MockAppleKeychain::SetTestDataPasswordString(MockKeychainItemType item,
180                                                   const char* value) {
181   SetTestDataPasswordBytes(item, value, value ? strlen(value) : 0);
182 }
183 
ItemCopyAttributesAndData(SecKeychainItemRef itemRef,SecKeychainAttributeInfo * info,SecItemClass * itemClass,SecKeychainAttributeList ** attrList,UInt32 * length,void ** outData) const184 OSStatus MockAppleKeychain::ItemCopyAttributesAndData(
185     SecKeychainItemRef itemRef,
186     SecKeychainAttributeInfo* info,
187     SecItemClass* itemClass,
188     SecKeychainAttributeList** attrList,
189     UInt32* length,
190     void** outData) const {
191   DCHECK(itemRef);
192   MockKeychainItemType key =
193       reinterpret_cast<MockKeychainItemType>(itemRef) - 1;
194   if (keychain_attr_list_.find(key) == keychain_attr_list_.end())
195     return errSecInvalidItemRef;
196 
197   DCHECK(!itemClass);  // itemClass not implemented in the Mock.
198   if (attrList)
199     *attrList  = &(keychain_attr_list_[key]);
200   if (outData) {
201     *outData = keychain_data_[key].data;
202     DCHECK(length);
203     *length = keychain_data_[key].length;
204   }
205 
206   ++attribute_data_copy_count_;
207   return noErr;
208 }
209 
ItemModifyAttributesAndData(SecKeychainItemRef itemRef,const SecKeychainAttributeList * attrList,UInt32 length,const void * data) const210 OSStatus MockAppleKeychain::ItemModifyAttributesAndData(
211     SecKeychainItemRef itemRef,
212     const SecKeychainAttributeList* attrList,
213     UInt32 length,
214     const void* data) const {
215   DCHECK(itemRef);
216   const char* fail_trigger = "fail_me";
217   if (length == strlen(fail_trigger) &&
218       memcmp(data, fail_trigger, length) == 0) {
219     return errSecAuthFailed;
220   }
221 
222   MockKeychainItemType key =
223       reinterpret_cast<MockKeychainItemType>(itemRef) - 1;
224   if (keychain_attr_list_.find(key) == keychain_attr_list_.end())
225     return errSecInvalidItemRef;
226 
227   MockAppleKeychain* mutable_this = const_cast<MockAppleKeychain*>(this);
228   if (attrList) {
229     for (UInt32 change_attr = 0; change_attr < attrList->count; ++change_attr) {
230       if (attrList->attr[change_attr].tag == kSecCreatorItemAttr) {
231         void* data = attrList->attr[change_attr].data;
232         mutable_this->SetTestDataCreator(key, *(static_cast<OSType*>(data)));
233       } else {
234         NOTIMPLEMENTED();
235       }
236     }
237   }
238   if (data)
239     mutable_this->SetTestDataPasswordBytes(key, data, length);
240   return noErr;
241 }
242 
ItemFreeAttributesAndData(SecKeychainAttributeList * attrList,void * data) const243 OSStatus MockAppleKeychain::ItemFreeAttributesAndData(
244     SecKeychainAttributeList* attrList,
245     void* data) const {
246   --attribute_data_copy_count_;
247   return noErr;
248 }
249 
ItemDelete(SecKeychainItemRef itemRef) const250 OSStatus MockAppleKeychain::ItemDelete(SecKeychainItemRef itemRef) const {
251   MockKeychainItemType key =
252       reinterpret_cast<MockKeychainItemType>(itemRef) - 1;
253 
254   for (unsigned int i = 0; i < keychain_attr_list_[key].count; ++i) {
255     if (keychain_attr_list_[key].attr[i].data)
256       free(keychain_attr_list_[key].attr[i].data);
257   }
258   free(keychain_attr_list_[key].attr);
259   if (keychain_data_[key].data)
260     free(keychain_data_[key].data);
261 
262   keychain_attr_list_.erase(key);
263   keychain_data_.erase(key);
264   added_via_api_.erase(key);
265   return noErr;
266 }
267 
SearchCreateFromAttributes(CFTypeRef keychainOrArray,SecItemClass itemClass,const SecKeychainAttributeList * attrList,SecKeychainSearchRef * searchRef) const268 OSStatus MockAppleKeychain::SearchCreateFromAttributes(
269     CFTypeRef keychainOrArray,
270     SecItemClass itemClass,
271     const SecKeychainAttributeList* attrList,
272     SecKeychainSearchRef* searchRef) const {
273   // Figure out which of our mock items matches, and set up the array we'll use
274   // to generate results out of SearchCopyNext.
275   remaining_search_results_.clear();
276   for (MockKeychainAttributesMap::const_iterator it =
277            keychain_attr_list_.begin();
278        it != keychain_attr_list_.end();
279        ++it) {
280     bool mock_item_matches = true;
281     for (UInt32 search_attr = 0; search_attr < attrList->count; ++search_attr) {
282       SecKeychainAttribute* mock_attribute =
283           AttributeWithTag(it->second, attrList->attr[search_attr].tag);
284       if (mock_attribute->length != attrList->attr[search_attr].length ||
285           memcmp(mock_attribute->data, attrList->attr[search_attr].data,
286                  attrList->attr[search_attr].length) != 0) {
287         mock_item_matches = false;
288         break;
289       }
290     }
291     if (mock_item_matches)
292       remaining_search_results_.push_back(it->first);
293   }
294 
295   DCHECK(searchRef);
296   *searchRef = kDummySearchRef;
297   ++search_copy_count_;
298   return noErr;
299 }
300 
AlreadyContainsInternetPassword(UInt32 serverNameLength,const char * serverName,UInt32 securityDomainLength,const char * securityDomain,UInt32 accountNameLength,const char * accountName,UInt32 pathLength,const char * path,UInt16 port,SecProtocolType protocol,SecAuthenticationType authenticationType) const301 bool MockAppleKeychain::AlreadyContainsInternetPassword(
302     UInt32 serverNameLength,
303     const char* serverName,
304     UInt32 securityDomainLength,
305     const char* securityDomain,
306     UInt32 accountNameLength,
307     const char* accountName,
308     UInt32 pathLength,
309     const char* path,
310     UInt16 port,
311     SecProtocolType protocol,
312     SecAuthenticationType authenticationType) const {
313   for (MockKeychainAttributesMap::const_iterator it =
314            keychain_attr_list_.begin();
315        it != keychain_attr_list_.end();
316        ++it) {
317     SecKeychainAttribute* attribute;
318     attribute = AttributeWithTag(it->second, kSecServerItemAttr);
319     if ((attribute->length != serverNameLength) ||
320         (attribute->data == NULL && *serverName != '\0') ||
321         (attribute->data != NULL && *serverName == '\0') ||
322         strncmp(serverName,
323                 (const char*) attribute->data,
324                 serverNameLength) != 0) {
325       continue;
326     }
327     attribute = AttributeWithTag(it->second, kSecSecurityDomainItemAttr);
328     if ((attribute->length != securityDomainLength) ||
329         (attribute->data == NULL && *securityDomain != '\0') ||
330         (attribute->data != NULL && *securityDomain == '\0') ||
331         strncmp(securityDomain,
332                 (const char*) attribute->data,
333                 securityDomainLength) != 0) {
334       continue;
335     }
336     attribute = AttributeWithTag(it->second, kSecAccountItemAttr);
337     if ((attribute->length != accountNameLength) ||
338         (attribute->data == NULL && *accountName != '\0') ||
339         (attribute->data != NULL && *accountName == '\0') ||
340         strncmp(accountName,
341                 (const char*) attribute->data,
342                 accountNameLength) != 0) {
343       continue;
344     }
345     attribute = AttributeWithTag(it->second, kSecPathItemAttr);
346     if ((attribute->length != pathLength) ||
347         (attribute->data == NULL && *path != '\0') ||
348         (attribute->data != NULL && *path == '\0') ||
349         strncmp(path,
350                 (const char*) attribute->data,
351                 pathLength) != 0) {
352       continue;
353     }
354     attribute = AttributeWithTag(it->second, kSecPortItemAttr);
355     if ((attribute->data == NULL) ||
356         (port != *(static_cast<UInt32*>(attribute->data)))) {
357       continue;
358     }
359     attribute = AttributeWithTag(it->second, kSecProtocolItemAttr);
360     if ((attribute->data == NULL) ||
361         (protocol != *(static_cast<SecProtocolType*>(attribute->data)))) {
362       continue;
363     }
364     attribute = AttributeWithTag(it->second, kSecAuthenticationTypeItemAttr);
365     if ((attribute->data == NULL) ||
366         (authenticationType !=
367             *(static_cast<SecAuthenticationType*>(attribute->data)))) {
368       continue;
369     }
370     // The keychain already has this item, since all fields other than the
371     // password match.
372     return true;
373   }
374   return false;
375 }
376 
AddInternetPassword(SecKeychainRef keychain,UInt32 serverNameLength,const char * serverName,UInt32 securityDomainLength,const char * securityDomain,UInt32 accountNameLength,const char * accountName,UInt32 pathLength,const char * path,UInt16 port,SecProtocolType protocol,SecAuthenticationType authenticationType,UInt32 passwordLength,const void * passwordData,SecKeychainItemRef * itemRef) const377 OSStatus MockAppleKeychain::AddInternetPassword(
378     SecKeychainRef keychain,
379     UInt32 serverNameLength,
380     const char* serverName,
381     UInt32 securityDomainLength,
382     const char* securityDomain,
383     UInt32 accountNameLength,
384     const char* accountName,
385     UInt32 pathLength,
386     const char* path,
387     UInt16 port,
388     SecProtocolType protocol,
389     SecAuthenticationType authenticationType,
390     UInt32 passwordLength,
391     const void* passwordData,
392     SecKeychainItemRef* itemRef) const {
393 
394   // Check for the magic duplicate item trigger.
395   if (strcmp(serverName, "some.domain.com") == 0)
396     return errSecDuplicateItem;
397 
398   // If the account already exists in the keychain, we don't add it.
399   if (AlreadyContainsInternetPassword(serverNameLength, serverName,
400                                       securityDomainLength, securityDomain,
401                                       accountNameLength, accountName,
402                                       pathLength, path,
403                                       port, protocol,
404                                       authenticationType)) {
405     return errSecDuplicateItem;
406   }
407 
408   // Pick the next unused slot.
409   MockKeychainItemType key = next_item_key_++;
410 
411   // Initialize keychain data storage at the target location.
412   InitializeKeychainData(key);
413 
414   MockAppleKeychain* mutable_this = const_cast<MockAppleKeychain*>(this);
415   mutable_this->SetTestDataBytes(key, kSecServerItemAttr, serverName,
416                                  serverNameLength);
417   mutable_this->SetTestDataBytes(key, kSecSecurityDomainItemAttr,
418                                  securityDomain, securityDomainLength);
419   mutable_this->SetTestDataBytes(key, kSecAccountItemAttr, accountName,
420                                  accountNameLength);
421   mutable_this->SetTestDataBytes(key, kSecPathItemAttr, path, pathLength);
422   mutable_this->SetTestDataPort(key, port);
423   mutable_this->SetTestDataProtocol(key, protocol);
424   mutable_this->SetTestDataAuthType(key, authenticationType);
425   mutable_this->SetTestDataPasswordBytes(key, passwordData,
426                                          passwordLength);
427   base::Time::Exploded exploded_time;
428   base::Time::Now().UTCExplode(&exploded_time);
429   char time_string[128];
430   snprintf(time_string, sizeof(time_string), "%04d%02d%02d%02d%02d%02dZ",
431            exploded_time.year, exploded_time.month, exploded_time.day_of_month,
432            exploded_time.hour, exploded_time.minute, exploded_time.second);
433   mutable_this->SetTestDataString(key, kSecCreationDateItemAttr, time_string);
434 
435   added_via_api_.insert(key);
436 
437   if (itemRef) {
438     *itemRef = reinterpret_cast<SecKeychainItemRef>(key + 1);
439     ++keychain_item_copy_count_;
440   }
441   return noErr;
442 }
443 
SearchCopyNext(SecKeychainSearchRef searchRef,SecKeychainItemRef * itemRef) const444 OSStatus MockAppleKeychain::SearchCopyNext(SecKeychainSearchRef searchRef,
445                                            SecKeychainItemRef* itemRef) const {
446   if (remaining_search_results_.empty())
447     return errSecItemNotFound;
448   MockKeychainItemType key = remaining_search_results_.front();
449   remaining_search_results_.erase(remaining_search_results_.begin());
450   *itemRef = reinterpret_cast<SecKeychainItemRef>(key + 1);
451   ++keychain_item_copy_count_;
452   return noErr;
453 }
454 
Free(CFTypeRef ref) const455 void MockAppleKeychain::Free(CFTypeRef ref) const {
456   if (!ref)
457     return;
458 
459   if (ref == kDummySearchRef) {
460     --search_copy_count_;
461   } else {
462     --keychain_item_copy_count_;
463   }
464 }
465 
UnfreedSearchCount() const466 int MockAppleKeychain::UnfreedSearchCount() const {
467   return search_copy_count_;
468 }
469 
UnfreedKeychainItemCount() const470 int MockAppleKeychain::UnfreedKeychainItemCount() const {
471   return keychain_item_copy_count_;
472 }
473 
UnfreedAttributeDataCount() const474 int MockAppleKeychain::UnfreedAttributeDataCount() const {
475   return attribute_data_copy_count_;
476 }
477 
CreatorCodesSetForAddedItems() const478 bool MockAppleKeychain::CreatorCodesSetForAddedItems() const {
479   for (std::set<MockKeychainItemType>::const_iterator
480            i = added_via_api_.begin();
481        i != added_via_api_.end();
482        ++i) {
483     SecKeychainAttribute* attribute = AttributeWithTag(keychain_attr_list_[*i],
484                                                        kSecCreatorItemAttr);
485     OSType* data = static_cast<OSType*>(attribute->data);
486     if (*data == 0)
487       return false;
488   }
489   return true;
490 }
491 
AddTestItem(const KeychainTestData & item_data)492 void MockAppleKeychain::AddTestItem(const KeychainTestData& item_data) {
493   MockKeychainItemType key = next_item_key_++;
494 
495   InitializeKeychainData(key);
496   SetTestDataAuthType(key, item_data.auth_type);
497   SetTestDataString(key, kSecServerItemAttr, item_data.server);
498   SetTestDataProtocol(key, item_data.protocol);
499   SetTestDataString(key, kSecPathItemAttr, item_data.path);
500   SetTestDataPort(key, item_data.port);
501   SetTestDataString(key, kSecSecurityDomainItemAttr,
502                     item_data.security_domain);
503   SetTestDataString(key, kSecCreationDateItemAttr, item_data.creation_date);
504   SetTestDataString(key, kSecAccountItemAttr, item_data.username);
505   SetTestDataPasswordString(key, item_data.password);
506   SetTestDataNegativeItem(key, item_data.negative_item);
507 }
508 
509 }  // namespace crypto
510