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"
12 const SecKeychainSearchRef
MockAppleKeychain::kDummySearchRef
=
13 reinterpret_cast<SecKeychainSearchRef
>(1000);
15 MockAppleKeychain::MockAppleKeychain()
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) {}
24 void MockAppleKeychain::InitializeKeychainData(MockKeychainItemType key
) const {
25 UInt32 tags
[] = { kSecAccountItemAttr
,
30 kSecAuthenticationTypeItemAttr
,
31 kSecSecurityDomainItemAttr
,
32 kSecCreationDateItemAttr
,
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
];
44 case kSecPortItemAttr
:
45 data_size
= sizeof(UInt32
);
47 case kSecProtocolItemAttr
:
48 data_size
= sizeof(SecProtocolType
);
50 case kSecAuthenticationTypeItemAttr
:
51 data_size
= sizeof(SecAuthenticationType
);
53 case kSecNegativeItemAttr
:
54 data_size
= sizeof(Boolean
);
56 case kSecCreatorItemAttr
:
57 data_size
= sizeof(OSType
);
61 keychain_attr_list_
[key
].attr
[i
].length
= data_size
;
62 keychain_attr_list_
[key
].attr
[i
].data
= calloc(1, data_size
);
67 MockAppleKeychain::~MockAppleKeychain() {
68 for (MockKeychainAttributesMap::iterator it
= keychain_attr_list_
.begin();
69 it
!= keychain_attr_list_
.end();
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
);
75 free(it
->second
.attr
);
76 if (keychain_data_
[it
->first
].data
)
77 free(keychain_data_
[it
->first
].data
);
79 keychain_attr_list_
.clear();
80 keychain_data_
.clear();
83 SecKeychainAttribute
* MockAppleKeychain::AttributeWithTag(
84 const SecKeychainAttributeList
& attribute_list
,
86 int attribute_index
= -1;
87 for (unsigned int i
= 0; i
< attribute_list
.count
; ++i
) {
88 if (attribute_list
.attr
[i
].tag
== tag
) {
93 if (attribute_index
== -1) {
94 NOTREACHED() << "Unsupported attribute: " << tag
;
97 return &(attribute_list
.attr
[attribute_index
]);
100 void MockAppleKeychain::SetTestDataBytes(MockKeychainItemType item
,
104 SecKeychainAttribute
* attribute
= AttributeWithTag(keychain_attr_list_
[item
],
106 attribute
->length
= length
;
109 free(attribute
->data
);
110 attribute
->data
= malloc(length
);
111 CHECK(attribute
->data
);
112 memcpy(attribute
->data
, data
, length
);
114 attribute
->data
= NULL
;
118 void MockAppleKeychain::SetTestDataString(MockKeychainItemType item
,
121 SetTestDataBytes(item
, tag
, value
, value
? strlen(value
) : 0);
124 void MockAppleKeychain::SetTestDataPort(MockKeychainItemType item
,
126 SecKeychainAttribute
* attribute
= AttributeWithTag(keychain_attr_list_
[item
],
128 UInt32
* data
= static_cast<UInt32
*>(attribute
->data
);
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
);
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
*>(
149 void MockAppleKeychain::SetTestDataNegativeItem(MockKeychainItemType item
,
151 SecKeychainAttribute
* attribute
= AttributeWithTag(keychain_attr_list_
[item
],
152 kSecNegativeItemAttr
);
153 Boolean
* data
= static_cast<Boolean
*>(attribute
->data
);
157 void MockAppleKeychain::SetTestDataCreator(MockKeychainItemType item
,
159 SecKeychainAttribute
* attribute
= AttributeWithTag(keychain_attr_list_
[item
],
160 kSecCreatorItemAttr
);
161 OSType
* data
= static_cast<OSType
*>(attribute
->data
);
165 void MockAppleKeychain::SetTestDataPasswordBytes(MockKeychainItemType item
,
168 keychain_data_
[item
].length
= length
;
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
);
175 keychain_data_
[item
].data
= NULL
;
179 void MockAppleKeychain::SetTestDataPasswordString(MockKeychainItemType item
,
181 SetTestDataPasswordBytes(item
, value
, value
? strlen(value
) : 0);
184 OSStatus
MockAppleKeychain::ItemCopyAttributesAndData(
185 SecKeychainItemRef itemRef
,
186 SecKeychainAttributeInfo
* info
,
187 SecItemClass
* itemClass
,
188 SecKeychainAttributeList
** attrList
,
190 void** outData
) const {
192 MockKeychainItemType key
=
193 reinterpret_cast<MockKeychainItemType
>(itemRef
) - 1;
194 if (keychain_attr_list_
.find(key
) == keychain_attr_list_
.end())
195 return errSecInvalidItemRef
;
197 DCHECK(!itemClass
); // itemClass not implemented in the Mock.
199 *attrList
= &(keychain_attr_list_
[key
]);
201 *outData
= keychain_data_
[key
].data
;
203 *length
= keychain_data_
[key
].length
;
206 ++attribute_data_copy_count_
;
210 OSStatus
MockAppleKeychain::ItemModifyAttributesAndData(
211 SecKeychainItemRef itemRef
,
212 const SecKeychainAttributeList
* attrList
,
214 const void* data
) const {
216 const char* fail_trigger
= "fail_me";
217 if (length
== strlen(fail_trigger
) &&
218 memcmp(data
, fail_trigger
, length
) == 0) {
219 return errSecAuthFailed
;
222 MockKeychainItemType key
=
223 reinterpret_cast<MockKeychainItemType
>(itemRef
) - 1;
224 if (keychain_attr_list_
.find(key
) == keychain_attr_list_
.end())
225 return errSecInvalidItemRef
;
227 MockAppleKeychain
* mutable_this
= const_cast<MockAppleKeychain
*>(this);
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
)));
239 mutable_this
->SetTestDataPasswordBytes(key
, data
, length
);
243 OSStatus
MockAppleKeychain::ItemFreeAttributesAndData(
244 SecKeychainAttributeList
* attrList
,
246 --attribute_data_copy_count_
;
250 OSStatus
MockAppleKeychain::ItemDelete(SecKeychainItemRef itemRef
) const {
251 MockKeychainItemType key
=
252 reinterpret_cast<MockKeychainItemType
>(itemRef
) - 1;
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
);
258 free(keychain_attr_list_
[key
].attr
);
259 if (keychain_data_
[key
].data
)
260 free(keychain_data_
[key
].data
);
262 keychain_attr_list_
.erase(key
);
263 keychain_data_
.erase(key
);
264 added_via_api_
.erase(key
);
268 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();
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;
291 if (mock_item_matches
)
292 remaining_search_results_
.push_back(it
->first
);
296 *searchRef
= kDummySearchRef
;
297 ++search_copy_count_
;
301 bool MockAppleKeychain::AlreadyContainsInternetPassword(
302 UInt32 serverNameLength
,
303 const char* serverName
,
304 UInt32 securityDomainLength
,
305 const char* securityDomain
,
306 UInt32 accountNameLength
,
307 const char* accountName
,
311 SecProtocolType protocol
,
312 SecAuthenticationType authenticationType
) const {
313 for (MockKeychainAttributesMap::const_iterator it
=
314 keychain_attr_list_
.begin();
315 it
!= keychain_attr_list_
.end();
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') ||
323 (const char*) attribute
->data
,
324 serverNameLength
) != 0) {
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) {
336 attribute
= AttributeWithTag(it
->second
, kSecAccountItemAttr
);
337 if ((attribute
->length
!= accountNameLength
) ||
338 (attribute
->data
== NULL
&& *accountName
!= '\0') ||
339 (attribute
->data
!= NULL
&& *accountName
== '\0') ||
341 (const char*) attribute
->data
,
342 accountNameLength
) != 0) {
345 attribute
= AttributeWithTag(it
->second
, kSecPathItemAttr
);
346 if ((attribute
->length
!= pathLength
) ||
347 (attribute
->data
== NULL
&& *path
!= '\0') ||
348 (attribute
->data
!= NULL
&& *path
== '\0') ||
350 (const char*) attribute
->data
,
354 attribute
= AttributeWithTag(it
->second
, kSecPortItemAttr
);
355 if ((attribute
->data
== NULL
) ||
356 (port
!= *(static_cast<UInt32
*>(attribute
->data
)))) {
359 attribute
= AttributeWithTag(it
->second
, kSecProtocolItemAttr
);
360 if ((attribute
->data
== NULL
) ||
361 (protocol
!= *(static_cast<SecProtocolType
*>(attribute
->data
)))) {
364 attribute
= AttributeWithTag(it
->second
, kSecAuthenticationTypeItemAttr
);
365 if ((attribute
->data
== NULL
) ||
366 (authenticationType
!=
367 *(static_cast<SecAuthenticationType
*>(attribute
->data
)))) {
370 // The keychain already has this item, since all fields other than the
377 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
,
388 SecProtocolType protocol
,
389 SecAuthenticationType authenticationType
,
390 UInt32 passwordLength
,
391 const void* passwordData
,
392 SecKeychainItemRef
* itemRef
) const {
394 // Check for the magic duplicate item trigger.
395 if (strcmp(serverName
, "some.domain.com") == 0)
396 return errSecDuplicateItem
;
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
,
404 authenticationType
)) {
405 return errSecDuplicateItem
;
408 // Pick the next unused slot.
409 MockKeychainItemType key
= next_item_key_
++;
411 // Initialize keychain data storage at the target location.
412 InitializeKeychainData(key
);
414 MockAppleKeychain
* mutable_this
= const_cast<MockAppleKeychain
*>(this);
415 mutable_this
->SetTestDataBytes(key
, kSecServerItemAttr
, serverName
,
417 mutable_this
->SetTestDataBytes(key
, kSecSecurityDomainItemAttr
,
418 securityDomain
, securityDomainLength
);
419 mutable_this
->SetTestDataBytes(key
, kSecAccountItemAttr
, accountName
,
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
,
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
);
435 added_via_api_
.insert(key
);
438 *itemRef
= reinterpret_cast<SecKeychainItemRef
>(key
+ 1);
439 ++keychain_item_copy_count_
;
444 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_
;
455 void MockAppleKeychain::Free(CFTypeRef ref
) const {
459 if (ref
== kDummySearchRef
) {
460 --search_copy_count_
;
462 --keychain_item_copy_count_
;
466 int MockAppleKeychain::UnfreedSearchCount() const {
467 return search_copy_count_
;
470 int MockAppleKeychain::UnfreedKeychainItemCount() const {
471 return keychain_item_copy_count_
;
474 int MockAppleKeychain::UnfreedAttributeDataCount() const {
475 return attribute_data_copy_count_
;
478 bool MockAppleKeychain::CreatorCodesSetForAddedItems() const {
479 for (std::set
<MockKeychainItemType
>::const_iterator
480 i
= added_via_api_
.begin();
481 i
!= added_via_api_
.end();
483 SecKeychainAttribute
* attribute
= AttributeWithTag(keychain_attr_list_
[*i
],
484 kSecCreatorItemAttr
);
485 OSType
* data
= static_cast<OSType
*>(attribute
->data
);
492 void MockAppleKeychain::AddTestItem(const KeychainTestData
& item_data
) {
493 MockKeychainItemType key
= next_item_key_
++;
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
);
509 } // namespace crypto