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 9MockKeychain::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 62MockKeychain::~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 79SecKeychainAttribute* 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 95void 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 112void MockKeychain::SetTestDataString(int item, UInt32 tag, const char* value) { 113 SetTestDataBytes(item, tag, value, value ? strlen(value) : 0); 114} 115 116void 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 123void 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 130void 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 138void 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 145void 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 152void 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 166void MockKeychain::SetTestDataPasswordString(int item, const char* value) { 167 SetTestDataPasswordBytes(item, value, value ? strlen(value) : 0); 168} 169 170OSStatus 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 194OSStatus 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 227OSStatus MockKeychain::ItemFreeAttributesAndData( 228 SecKeychainAttributeList *attrList, 229 void *data) const { 230 --attribute_data_copy_count_; 231 return noErr; 232} 233 234OSStatus 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 244OSStatus 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 275OSStatus 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 327OSStatus 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 339OSStatus 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 362OSStatus MockKeychain::ItemFreeContent(SecKeychainAttributeList *attrList, 363 void *data) const { 364 // No-op. 365 password_data_count_--; 366 return noErr; 367} 368 369OSStatus 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 387void 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 399int MockKeychain::UnfreedSearchCount() const { 400 return search_copy_count_; 401} 402 403int MockKeychain::UnfreedKeychainItemCount() const { 404 return keychain_item_copy_count_; 405} 406 407int MockKeychain::UnfreedAttributeDataCount() const { 408 return attribute_data_copy_count_; 409} 410 411bool 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 424void 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