• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2009 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.h"
7 #include "chrome/browser/keychain_mock_mac.h"
8 
MockKeychain(unsigned int item_capacity)9 MockKeychain::MockKeychain(unsigned int item_capacity)
10     : item_capacity_(item_capacity), item_count_(0), search_copy_count_(0),
11       keychain_item_copy_count_(0), attribute_data_copy_count_(0),
12       find_generic_result_(noErr), called_add_generic_(false),
13       password_data_count_(0) {
14   UInt32 tags[] = { kSecAccountItemAttr,
15                     kSecServerItemAttr,
16                     kSecPortItemAttr,
17                     kSecPathItemAttr,
18                     kSecProtocolItemAttr,
19                     kSecAuthenticationTypeItemAttr,
20                     kSecSecurityDomainItemAttr,
21                     kSecCreationDateItemAttr,
22                     kSecNegativeItemAttr,
23                     kSecCreatorItemAttr };
24 
25   // Create the test keychain data storage.
26   keychain_attr_list_ = static_cast<SecKeychainAttributeList*>(
27       calloc(item_capacity_, sizeof(SecKeychainAttributeList)));
28   keychain_data_ = static_cast<KeychainPasswordData*>(
29       calloc(item_capacity_, sizeof(KeychainPasswordData)));
30   for (unsigned int i = 0; i < item_capacity_; ++i) {
31     keychain_attr_list_[i].count = arraysize(tags);
32     keychain_attr_list_[i].attr = static_cast<SecKeychainAttribute*>(
33         calloc(keychain_attr_list_[i].count, sizeof(SecKeychainAttribute)));
34     for (unsigned int j = 0; j < keychain_attr_list_[i].count; ++j) {
35       keychain_attr_list_[i].attr[j].tag = tags[j];
36       size_t data_size = 0;
37       switch (tags[j]) {
38         case kSecPortItemAttr:
39           data_size = sizeof(UInt32);
40           break;
41         case kSecProtocolItemAttr:
42           data_size = sizeof(SecProtocolType);
43           break;
44         case kSecAuthenticationTypeItemAttr:
45           data_size = sizeof(SecAuthenticationType);
46           break;
47         case kSecNegativeItemAttr:
48           data_size = sizeof(Boolean);
49           break;
50         case kSecCreatorItemAttr:
51           data_size = sizeof(OSType);
52           break;
53       }
54       if (data_size > 0) {
55         keychain_attr_list_[i].attr[j].length = data_size;
56         keychain_attr_list_[i].attr[j].data = calloc(1, data_size);
57       }
58     }
59   }
60 }
61 
~MockKeychain()62 MockKeychain::~MockKeychain() {
63   for (unsigned int i = 0; i < item_capacity_; ++i) {
64     for (unsigned int j = 0; j < keychain_attr_list_[i].count; ++j) {
65       if (keychain_attr_list_[i].attr[j].data) {
66         free(keychain_attr_list_[i].attr[j].data);
67       }
68     }
69     free(keychain_attr_list_[i].attr);
70     if (keychain_data_[i].data) {
71       free(keychain_data_[i].data);
72     }
73   }
74   free(keychain_attr_list_);
75   free(keychain_data_);
76 }
77 
78 
AttributeWithTag(const SecKeychainAttributeList & attribute_list,UInt32 tag)79 SecKeychainAttribute* MockKeychain::AttributeWithTag(
80     const SecKeychainAttributeList& attribute_list, UInt32 tag) {
81   int attribute_index = -1;
82   for (unsigned int i = 0; i < attribute_list.count; ++i) {
83     if (attribute_list.attr[i].tag == tag) {
84       attribute_index = i;
85       break;
86     }
87   }
88   if (attribute_index == -1) {
89     NOTREACHED() << "Unsupported attribute: " << tag;
90     return NULL;
91   }
92   return &(attribute_list.attr[attribute_index]);
93 }
94 
SetTestDataBytes(int item,UInt32 tag,const void * data,size_t length)95 void MockKeychain::SetTestDataBytes(int item, UInt32 tag, const void* data,
96                                     size_t length) {
97   SecKeychainAttribute* attribute = AttributeWithTag(keychain_attr_list_[item],
98                                                      tag);
99   attribute->length = length;
100   if (length > 0) {
101     if (attribute->data) {
102       free(attribute->data);
103     }
104     attribute->data = malloc(length);
105     CHECK(attribute->data);
106     memcpy(attribute->data, data, length);
107   } else {
108     attribute->data = NULL;
109   }
110 }
111 
SetTestDataString(int item,UInt32 tag,const char * value)112 void MockKeychain::SetTestDataString(int item, UInt32 tag, const char* value) {
113   SetTestDataBytes(item, tag, value, value ? strlen(value) : 0);
114 }
115 
SetTestDataPort(int item,UInt32 value)116 void MockKeychain::SetTestDataPort(int item, UInt32 value) {
117   SecKeychainAttribute* attribute = AttributeWithTag(keychain_attr_list_[item],
118                                                      kSecPortItemAttr);
119   UInt32* data = static_cast<UInt32*>(attribute->data);
120   *data = value;
121 }
122 
SetTestDataProtocol(int item,SecProtocolType value)123 void MockKeychain::SetTestDataProtocol(int item, SecProtocolType value) {
124   SecKeychainAttribute* attribute = AttributeWithTag(keychain_attr_list_[item],
125                                                      kSecProtocolItemAttr);
126   SecProtocolType* data = static_cast<SecProtocolType*>(attribute->data);
127   *data = value;
128 }
129 
SetTestDataAuthType(int item,SecAuthenticationType value)130 void MockKeychain::SetTestDataAuthType(int item, SecAuthenticationType value) {
131   SecKeychainAttribute* attribute = AttributeWithTag(
132       keychain_attr_list_[item], kSecAuthenticationTypeItemAttr);
133   SecAuthenticationType* data = static_cast<SecAuthenticationType*>(
134       attribute->data);
135   *data = value;
136 }
137 
SetTestDataNegativeItem(int item,Boolean value)138 void MockKeychain::SetTestDataNegativeItem(int item, Boolean value) {
139   SecKeychainAttribute* attribute = AttributeWithTag(keychain_attr_list_[item],
140                                                      kSecNegativeItemAttr);
141   Boolean* data = static_cast<Boolean*>(attribute->data);
142   *data = value;
143 }
144 
SetTestDataCreator(int item,OSType value)145 void MockKeychain::SetTestDataCreator(int item, OSType value) {
146   SecKeychainAttribute* attribute = AttributeWithTag(keychain_attr_list_[item],
147                                                      kSecCreatorItemAttr);
148   OSType* data = static_cast<OSType*>(attribute->data);
149   *data = value;
150 }
151 
SetTestDataPasswordBytes(int item,const void * data,size_t length)152 void MockKeychain::SetTestDataPasswordBytes(int item, const void* data,
153                                             size_t length) {
154   keychain_data_[item].length = length;
155   if (length > 0) {
156     if (keychain_data_[item].data) {
157       free(keychain_data_[item].data);
158     }
159     keychain_data_[item].data = malloc(length);
160     memcpy(keychain_data_[item].data, data, length);
161   } else {
162     keychain_data_[item].data = NULL;
163   }
164 }
165 
SetTestDataPasswordString(int item,const char * value)166 void MockKeychain::SetTestDataPasswordString(int item, const char* value) {
167   SetTestDataPasswordBytes(item, value, value ? strlen(value) : 0);
168 }
169 
ItemCopyAttributesAndData(SecKeychainItemRef itemRef,SecKeychainAttributeInfo * info,SecItemClass * itemClass,SecKeychainAttributeList ** attrList,UInt32 * length,void ** outData) const170 OSStatus MockKeychain::ItemCopyAttributesAndData(
171     SecKeychainItemRef itemRef, SecKeychainAttributeInfo *info,
172     SecItemClass *itemClass, SecKeychainAttributeList **attrList,
173     UInt32 *length, void **outData) const {
174   DCHECK(itemRef);
175   unsigned int item_index = reinterpret_cast<unsigned int>(itemRef) - 1;
176   if (item_index >= item_count_) {
177     return errSecInvalidItemRef;
178   }
179 
180   DCHECK(!itemClass);  // itemClass not implemented in the Mock.
181   if (attrList) {
182     *attrList  = &(keychain_attr_list_[item_index]);
183   }
184   if (outData) {
185     *outData = keychain_data_[item_index].data;
186     DCHECK(length);
187     *length = keychain_data_[item_index].length;
188   }
189 
190   ++attribute_data_copy_count_;
191   return noErr;
192 }
193 
ItemModifyAttributesAndData(SecKeychainItemRef itemRef,const SecKeychainAttributeList * attrList,UInt32 length,const void * data) const194 OSStatus MockKeychain::ItemModifyAttributesAndData(
195     SecKeychainItemRef itemRef, const SecKeychainAttributeList *attrList,
196     UInt32 length, const void *data) const {
197   DCHECK(itemRef);
198   const char* fail_trigger = "fail_me";
199   if (length == strlen(fail_trigger) &&
200       memcmp(data, fail_trigger, length) == 0) {
201     return errSecAuthFailed;
202   }
203 
204   unsigned int item_index = reinterpret_cast<unsigned int>(itemRef) - 1;
205   if (item_index >= item_count_) {
206     return errSecInvalidItemRef;
207   }
208 
209   MockKeychain* mutable_this = const_cast<MockKeychain*>(this);
210   if (attrList) {
211     for (UInt32 change_attr = 0; change_attr < attrList->count; ++change_attr) {
212       if (attrList->attr[change_attr].tag == kSecCreatorItemAttr) {
213         void* data = attrList->attr[change_attr].data;
214         mutable_this->SetTestDataCreator(item_index,
215                                          *(static_cast<OSType*>(data)));
216       } else {
217         NOTIMPLEMENTED();
218       }
219     }
220   }
221   if (data) {
222     mutable_this->SetTestDataPasswordBytes(item_index, data, length);
223   }
224   return noErr;
225 }
226 
ItemFreeAttributesAndData(SecKeychainAttributeList * attrList,void * data) const227 OSStatus MockKeychain::ItemFreeAttributesAndData(
228     SecKeychainAttributeList *attrList,
229     void *data) const {
230   --attribute_data_copy_count_;
231   return noErr;
232 }
233 
ItemDelete(SecKeychainItemRef itemRef) const234 OSStatus MockKeychain::ItemDelete(SecKeychainItemRef itemRef) const {
235   unsigned int item_index = reinterpret_cast<unsigned int>(itemRef) - 1;
236   // The mock only supports deleting the last item.
237   if (item_index != item_count_ - 1) {
238     NOTIMPLEMENTED();
239   }
240   --item_count_;
241   return noErr;
242 }
243 
SearchCreateFromAttributes(CFTypeRef keychainOrArray,SecItemClass itemClass,const SecKeychainAttributeList * attrList,SecKeychainSearchRef * searchRef) const244 OSStatus MockKeychain::SearchCreateFromAttributes(
245     CFTypeRef keychainOrArray, SecItemClass itemClass,
246     const SecKeychainAttributeList *attrList,
247     SecKeychainSearchRef *searchRef) const {
248   // Figure out which of our mock items matches, and set up the array we'll use
249   // to generate results out of SearchCopyNext.
250   remaining_search_results_.clear();
251   for (unsigned int mock_item = 0; mock_item < item_count_; ++mock_item) {
252     bool mock_item_matches = true;
253     for (UInt32 search_attr = 0; search_attr < attrList->count; ++search_attr) {
254       SecKeychainAttribute* mock_attribute =
255           AttributeWithTag(keychain_attr_list_[mock_item],
256                            attrList->attr[search_attr].tag);
257       if (mock_attribute->length != attrList->attr[search_attr].length ||
258           memcmp(mock_attribute->data, attrList->attr[search_attr].data,
259                  attrList->attr[search_attr].length) != 0) {
260         mock_item_matches = false;
261         break;
262       }
263     }
264     if (mock_item_matches) {
265       remaining_search_results_.push_back(mock_item);
266     }
267   }
268 
269   DCHECK(searchRef);
270   *searchRef = reinterpret_cast<SecKeychainSearchRef>(kDummySearchRef);
271   ++search_copy_count_;
272   return noErr;
273 }
274 
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) const275 OSStatus MockKeychain::AddInternetPassword(
276     SecKeychainRef keychain,
277     UInt32 serverNameLength, const char *serverName,
278     UInt32 securityDomainLength, const char *securityDomain,
279     UInt32 accountNameLength, const char *accountName,
280     UInt32 pathLength, const char *path,
281     UInt16 port, SecProtocolType protocol,
282     SecAuthenticationType authenticationType,
283     UInt32 passwordLength, const void *passwordData,
284     SecKeychainItemRef *itemRef) const {
285 
286   // Check for the magic duplicate item trigger.
287   if (strcmp(serverName, "some.domain.com") == 0) {
288     return errSecDuplicateItem;
289   }
290 
291   // Use empty slots until they run out, then just keep replacing the last item.
292   int target_item = (item_count_ == item_capacity_) ? item_capacity_ - 1
293                                                     : item_count_++;
294 
295   MockKeychain* mutable_this = const_cast<MockKeychain*>(this);
296   mutable_this->SetTestDataBytes(target_item, kSecServerItemAttr, serverName,
297                                  serverNameLength);
298   mutable_this->SetTestDataBytes(target_item, kSecSecurityDomainItemAttr,
299                                  securityDomain, securityDomainLength);
300   mutable_this->SetTestDataBytes(target_item, kSecAccountItemAttr, accountName,
301                                  accountNameLength);
302   mutable_this->SetTestDataBytes(target_item, kSecPathItemAttr, path,
303                                  pathLength);
304   mutable_this->SetTestDataPort(target_item, port);
305   mutable_this->SetTestDataProtocol(target_item, protocol);
306   mutable_this->SetTestDataAuthType(target_item, authenticationType);
307   mutable_this->SetTestDataPasswordBytes(target_item, passwordData,
308                                          passwordLength);
309   base::Time::Exploded exploded_time;
310   base::Time::Now().UTCExplode(&exploded_time);
311   char time_string[128];
312   snprintf(time_string, sizeof(time_string), "%04d%02d%02d%02d%02d%02dZ",
313            exploded_time.year, exploded_time.month, exploded_time.day_of_month,
314            exploded_time.hour, exploded_time.minute, exploded_time.second);
315   mutable_this->SetTestDataString(target_item, kSecCreationDateItemAttr,
316                                   time_string);
317 
318   added_via_api_.insert(target_item);
319 
320   if (itemRef) {
321     *itemRef = reinterpret_cast<SecKeychainItemRef>(target_item + 1);
322     ++keychain_item_copy_count_;
323   }
324   return noErr;
325 }
326 
SearchCopyNext(SecKeychainSearchRef searchRef,SecKeychainItemRef * itemRef) const327 OSStatus MockKeychain::SearchCopyNext(SecKeychainSearchRef searchRef,
328                                       SecKeychainItemRef *itemRef) const {
329   if (remaining_search_results_.empty()) {
330     return errSecItemNotFound;
331   }
332   unsigned int index = remaining_search_results_.front();
333   remaining_search_results_.erase(remaining_search_results_.begin());
334   *itemRef = reinterpret_cast<SecKeychainItemRef>(index + 1);
335   ++keychain_item_copy_count_;
336   return noErr;
337 }
338 
FindGenericPassword(CFTypeRef keychainOrArray,UInt32 serviceNameLength,const char * serviceName,UInt32 accountNameLength,const char * accountName,UInt32 * passwordLength,void ** passwordData,SecKeychainItemRef * itemRef) const339 OSStatus MockKeychain::FindGenericPassword(CFTypeRef keychainOrArray,
340                                            UInt32 serviceNameLength,
341                                            const char *serviceName,
342                                            UInt32 accountNameLength,
343                                            const char *accountName,
344                                            UInt32 *passwordLength,
345                                            void **passwordData,
346                                            SecKeychainItemRef *itemRef) const {
347   // When simulating |noErr| we return canned |passwordData| and
348   // |passwordLenght|.  Otherwise, just return given code.
349   if (find_generic_result_ == noErr) {
350     static char password[] = "my_password";
351 
352     DCHECK(passwordData);
353     *passwordData = static_cast<void*>(password);
354     DCHECK(passwordLength);
355     *passwordLength = strlen(password);
356     password_data_count_++;
357   }
358 
359   return find_generic_result_;
360 }
361 
ItemFreeContent(SecKeychainAttributeList * attrList,void * data) const362 OSStatus MockKeychain::ItemFreeContent(SecKeychainAttributeList *attrList,
363                                        void *data) const {
364   // No-op.
365   password_data_count_--;
366   return noErr;
367 }
368 
AddGenericPassword(SecKeychainRef keychain,UInt32 serviceNameLength,const char * serviceName,UInt32 accountNameLength,const char * accountName,UInt32 passwordLength,const void * passwordData,SecKeychainItemRef * itemRef) const369 OSStatus MockKeychain::AddGenericPassword(SecKeychainRef keychain,
370                                           UInt32 serviceNameLength,
371                                           const char *serviceName,
372                                           UInt32 accountNameLength,
373                                           const char *accountName,
374                                           UInt32 passwordLength,
375                                           const void *passwordData,
376                                           SecKeychainItemRef *itemRef) const {
377   called_add_generic_ = true;
378 
379   DCHECK(passwordLength > 0);
380   DCHECK(passwordData);
381   add_generic_password_ =
382       std::string(const_cast<char*>(static_cast<const char*>(passwordData)),
383                   passwordLength);
384   return noErr;
385 }
386 
Free(CFTypeRef ref) const387 void MockKeychain::Free(CFTypeRef ref) const {
388   if (!ref) {
389     return;
390   }
391 
392   if (reinterpret_cast<int>(ref) == kDummySearchRef) {
393     --search_copy_count_;
394   } else {
395     --keychain_item_copy_count_;
396   }
397 }
398 
UnfreedSearchCount() const399 int MockKeychain::UnfreedSearchCount() const {
400   return search_copy_count_;
401 }
402 
UnfreedKeychainItemCount() const403 int MockKeychain::UnfreedKeychainItemCount() const {
404   return keychain_item_copy_count_;
405 }
406 
UnfreedAttributeDataCount() const407 int MockKeychain::UnfreedAttributeDataCount() const {
408   return attribute_data_copy_count_;
409 }
410 
CreatorCodesSetForAddedItems() const411 bool MockKeychain::CreatorCodesSetForAddedItems() const {
412   for (std::set<unsigned int>::const_iterator i = added_via_api_.begin();
413        i != added_via_api_.end(); ++i) {
414     SecKeychainAttribute* attribute = AttributeWithTag(keychain_attr_list_[*i],
415                                                        kSecCreatorItemAttr);
416     OSType* data = static_cast<OSType*>(attribute->data);
417     if (*data == 0) {
418       return false;
419     }
420   }
421   return true;
422 }
423 
AddTestItem(const KeychainTestData & item_data)424 void MockKeychain::AddTestItem(const KeychainTestData& item_data) {
425   unsigned int index = item_count_++;
426   CHECK(index < item_capacity_);
427 
428   SetTestDataAuthType(index, item_data.auth_type);
429   SetTestDataString(index, kSecServerItemAttr, item_data.server);
430   SetTestDataProtocol(index, item_data.protocol);
431   SetTestDataString(index, kSecPathItemAttr, item_data.path);
432   SetTestDataPort(index, item_data.port);
433   SetTestDataString(index, kSecSecurityDomainItemAttr,
434                     item_data.security_domain);
435   SetTestDataString(index, kSecCreationDateItemAttr, item_data.creation_date);
436   SetTestDataString(index, kSecAccountItemAttr, item_data.username);
437   SetTestDataPasswordString(index, item_data.password);
438   SetTestDataNegativeItem(index, item_data.negative_item);
439 }
440