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