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 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 24 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 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 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 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 118 void MockAppleKeychain::SetTestDataString(MockKeychainItemType item, 119 UInt32 tag, 120 const char* value) { 121 SetTestDataBytes(item, tag, value, value ? strlen(value) : 0); 122 } 123 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 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 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 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 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 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 179 void MockAppleKeychain::SetTestDataPasswordString(MockKeychainItemType item, 180 const char* value) { 181 SetTestDataPasswordBytes(item, value, value ? strlen(value) : 0); 182 } 183 184 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 210 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 243 OSStatus MockAppleKeychain::ItemFreeAttributesAndData( 244 SecKeychainAttributeList* attrList, 245 void* data) const { 246 --attribute_data_copy_count_; 247 return noErr; 248 } 249 250 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 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(); 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 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, 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 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, 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 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_; 452 return noErr; 453 } 454 455 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 466 int MockAppleKeychain::UnfreedSearchCount() const { 467 return search_copy_count_; 468 } 469 470 int MockAppleKeychain::UnfreedKeychainItemCount() const { 471 return keychain_item_copy_count_; 472 } 473 474 int MockAppleKeychain::UnfreedAttributeDataCount() const { 475 return attribute_data_copy_count_; 476 } 477 478 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 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