1/* 2 * Copyright (c) 2000-2004,2011-2014 Apple Inc. All Rights Reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24 25// 26// Item.cpp 27// 28 29#include "Item.h" 30 31#include "Certificate.h" 32#include "KeyItem.h" 33#include "ExtendedAttribute.h" 34 35#include "Globals.h" 36#include <security_cdsa_utilities/Schema.h> 37#include "KCEventNotifier.h" 38#include "KCExceptions.h" 39#include "cssmdatetime.h" 40#include <security_cdsa_client/keychainacl.h> 41#include <security_utilities/osxcode.h> 42#include <security_utilities/trackingallocator.h> 43#include <Security/SecKeychainItemPriv.h> 44#include <Security/cssmapple.h> 45#include <CommonCrypto/CommonDigest.h> 46 47#define SENDACCESSNOTIFICATIONS 1 48 49//%%% schema indexes should be defined in Schema.h 50#define _kSecAppleSharePasswordItemClass 'ashp' 51#define APPLEDB_CSSM_PRINTNAME_ATTRIBUTE 1 /* schema index for label attribute of keys or certificates */ 52#define APPLEDB_GENERIC_PRINTNAME_ATTRIBUTE 7 /* schema index for label attribute of password items */ 53#define IS_PASSWORD_ITEM_CLASS(X) ( (X) == kSecInternetPasswordItemClass || \ 54 (X) == kSecGenericPasswordItemClass || \ 55 (X) == _kSecAppleSharePasswordItemClass ) ? 1 : 0 56 57using namespace KeychainCore; 58using namespace CSSMDateTimeUtils; 59 60// 61// ItemImpl 62// 63 64// NewItemImpl constructor 65ItemImpl::ItemImpl(SecItemClass itemClass, OSType itemCreator, UInt32 length, const void* data, bool dontDoAttributes) 66 : mDbAttributes(new DbAttributes()), 67 mKeychain(NULL), 68 secd_PersistentRef(NULL), 69 mDoNotEncrypt(false), 70 mInCache(false), 71 mMutex(Mutex::recursive) 72{ 73 if (length && data) 74 mData = new CssmDataContainer(data, length); 75 76 mDbAttributes->recordType(Schema::recordTypeFor(itemClass)); 77 78 if (itemCreator) 79 mDbAttributes->add(Schema::attributeInfo(kSecCreatorItemAttr), itemCreator); 80} 81 82ItemImpl::ItemImpl(SecItemClass itemClass, SecKeychainAttributeList *attrList, UInt32 length, const void* data) 83 : mDbAttributes(new DbAttributes()), 84 mKeychain(NULL), 85 secd_PersistentRef(NULL), 86 mDoNotEncrypt(false), 87 mInCache(false), 88 mMutex(Mutex::recursive) 89{ 90 if (length && data) 91 mData = new CssmDataContainer(data, length); 92 93 94 mDbAttributes->recordType(Schema::recordTypeFor(itemClass)); 95 96 if(attrList) 97 { 98 for(UInt32 i=0; i < attrList->count; i++) 99 { 100 mDbAttributes->add(Schema::attributeInfo(attrList->attr[i].tag), CssmData(attrList->attr[i].data, attrList->attr[i].length)); 101 } 102 } 103} 104 105// DbItemImpl constructor 106ItemImpl::ItemImpl(const Keychain &keychain, const PrimaryKey &primaryKey, const DbUniqueRecord &uniqueId) 107 : mUniqueId(uniqueId), mKeychain(keychain), mPrimaryKey(primaryKey), 108 secd_PersistentRef(NULL), mDoNotEncrypt(false), mInCache(false), 109 mMutex(Mutex::recursive) 110{ 111} 112 113// PrimaryKey ItemImpl constructor 114ItemImpl::ItemImpl(const Keychain &keychain, const PrimaryKey &primaryKey) 115: mKeychain(keychain), mPrimaryKey(primaryKey), secd_PersistentRef(NULL), mDoNotEncrypt(false), 116 mInCache(false), 117 mMutex(Mutex::recursive) 118{ 119} 120 121ItemImpl* ItemImpl::make(const Keychain &keychain, const PrimaryKey &primaryKey, const CssmClient::DbUniqueRecord &uniqueId) 122{ 123 ItemImpl* ii = new ItemImpl(keychain, primaryKey, uniqueId); 124 keychain->addItem(primaryKey, ii); 125 return ii; 126} 127 128 129 130ItemImpl* ItemImpl::make(const Keychain &keychain, const PrimaryKey &primaryKey) 131{ 132 ItemImpl* ii = new ItemImpl(keychain, primaryKey); 133 keychain->addItem(primaryKey, ii); 134 return ii; 135} 136 137 138 139// Constructor used when copying an item to a keychain. 140 141ItemImpl::ItemImpl(ItemImpl &item) : 142 mData(item.modifiedData() ? NULL : new CssmDataContainer()), 143 mDbAttributes(new DbAttributes()), 144 mKeychain(NULL), 145 secd_PersistentRef(NULL), 146 mDoNotEncrypt(false), 147 mInCache(false), 148 mMutex(Mutex::recursive) 149{ 150 mDbAttributes->recordType(item.recordType()); 151 CSSM_DB_RECORD_ATTRIBUTE_INFO *schemaAttributes = NULL; 152 153 if (item.mKeychain) { 154 // get the entire source item from its keychain. This requires figuring 155 // out the schema for the item based on its record type. 156 157 for (uint32 i = 0; i < Schema::DBInfo.NumberOfRecordTypes; i++) 158 if (item.recordType() == Schema::DBInfo.RecordAttributeNames[i].DataRecordType) { 159 schemaAttributes = &Schema::DBInfo.RecordAttributeNames[i]; 160 break; 161 } 162 163 if (schemaAttributes == NULL) 164 // the source item is invalid 165 MacOSError::throwMe(errSecInvalidItemRef); 166 167 for (uint32 i = 0; i < schemaAttributes->NumberOfAttributes; i++) 168 mDbAttributes->add(schemaAttributes->AttributeInfo[i]); 169 170 item.getContent(mDbAttributes.get(), mData.get()); 171 } 172 173 // @@@ We don't deal with modified attributes. 174 175 if (item.modifiedData()) 176 // the copied data comes from the source item 177 mData = new CssmDataContainer(item.modifiedData()->Data, 178 item.modifiedData()->Length); 179} 180 181ItemImpl::~ItemImpl() 182{ 183 if (secd_PersistentRef) { 184 CFRelease(secd_PersistentRef); 185 } 186} 187 188 189 190Mutex* 191ItemImpl::getMutexForObject() 192{ 193 if (mKeychain.get()) 194 { 195 return mKeychain->getKeychainMutex(); 196 } 197 198 return NULL; 199} 200 201 202 203void 204ItemImpl::aboutToDestruct() 205{ 206 if (mKeychain && *mPrimaryKey) 207 { 208 mKeychain->removeItem(mPrimaryKey, this); 209 } 210} 211 212 213 214void 215ItemImpl::didModify() 216{ 217 StLock<Mutex>_(mMutex); 218 mData = NULL; 219 mDbAttributes.reset(NULL); 220} 221 222const CSSM_DATA & 223ItemImpl::defaultAttributeValue(const CSSM_DB_ATTRIBUTE_INFO &info) 224{ 225 static const uint32 zeroInt = 0; 226 static const double zeroDouble = 0.0; 227 static const char timeBytes[] = "20010101000000Z"; 228 229 static const CSSM_DATA defaultFourBytes = { 4, (uint8 *) &zeroInt }; 230 static const CSSM_DATA defaultEightBytes = { 8, (uint8 *) &zeroDouble }; 231 static const CSSM_DATA defaultTime = { 16, (uint8 *) timeBytes }; 232 static const CSSM_DATA defaultZeroBytes = { 0, NULL }; 233 234 switch (info.AttributeFormat) 235 { 236 case CSSM_DB_ATTRIBUTE_FORMAT_SINT32: 237 case CSSM_DB_ATTRIBUTE_FORMAT_UINT32: 238 return defaultFourBytes; 239 240 case CSSM_DB_ATTRIBUTE_FORMAT_REAL: 241 return defaultEightBytes; 242 243 case CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE: 244 return defaultTime; 245 246 default: 247 return defaultZeroBytes; 248 } 249} 250 251 252 253PrimaryKey ItemImpl::addWithCopyInfo (Keychain &keychain, bool isCopy) 254{ 255 StLock<Mutex>_(mMutex); 256 // If we already have a Keychain we can't be added. 257 if (mKeychain) 258 MacOSError::throwMe(errSecDuplicateItem); 259 260 // If we don't have any attributes we can't be added. 261 // (this might occur if attempting to add the item twice, since our attributes 262 // and data are set to NULL at the end of this function.) 263 if (!mDbAttributes.get()) 264 MacOSError::throwMe(errSecDuplicateItem); 265 266 CSSM_DB_RECORDTYPE recordType = mDbAttributes->recordType(); 267 268 // update the creation and update dates on the new item 269 if (!isCopy) 270 { 271 KeychainSchema schema = keychain->keychainSchema(); 272 SInt64 date; 273 GetCurrentMacLongDateTime(date); 274 if (schema->hasAttribute(recordType, kSecCreationDateItemAttr)) 275 { 276 setAttribute(schema->attributeInfoFor(recordType, kSecCreationDateItemAttr), date); 277 } 278 279 if (schema->hasAttribute(recordType, kSecModDateItemAttr)) 280 { 281 setAttribute(schema->attributeInfoFor(recordType, kSecModDateItemAttr), date); 282 } 283 } 284 285 // If the label (PrintName) attribute isn't specified, set a default label. 286 if (!mDoNotEncrypt && !mDbAttributes->find(Schema::attributeInfo(kSecLabelItemAttr))) 287 { 288 // if doNotEncrypt was set all of the attributes are wrapped in the data blob. Don't calculate here. 289 CssmDbAttributeData *label = NULL; 290 switch (recordType) 291 { 292 case CSSM_DL_DB_RECORD_GENERIC_PASSWORD: 293 label = mDbAttributes->find(Schema::attributeInfo(kSecServiceItemAttr)); 294 break; 295 296 case CSSM_DL_DB_RECORD_APPLESHARE_PASSWORD: 297 case CSSM_DL_DB_RECORD_INTERNET_PASSWORD: 298 label = mDbAttributes->find(Schema::attributeInfo(kSecServerItemAttr)); 299 // if AppleShare server name wasn't specified, try the server address 300 if (!label) label = mDbAttributes->find(Schema::attributeInfo(kSecAddressItemAttr)); 301 break; 302 303 default: 304 break; 305 } 306 // if all else fails, use the account name. 307 if (!label) 308 label = mDbAttributes->find(Schema::attributeInfo(kSecAccountItemAttr)); 309 310 if (label && label->size()) 311 setAttribute (Schema::attributeInfo(kSecLabelItemAttr), label->at<CssmData>(0)); 312 } 313 314 // get the attributes that are part of the primary key 315 const CssmAutoDbRecordAttributeInfo &primaryKeyInfos = 316 keychain->primaryKeyInfosFor(recordType); 317 318 // make sure each primary key element has a value in the item, otherwise 319 // the database will complain. we make a set of the provided attribute infos 320 // to avoid O(N^2) behavior. 321 322 DbAttributes *attributes = mDbAttributes.get(); 323 typedef set<CssmDbAttributeInfo> InfoSet; 324 InfoSet infoSet; 325 326 if (!mDoNotEncrypt) 327 { 328 // make a set of all the attributes in the key 329 for (uint32 i = 0; i < attributes->size(); i++) 330 infoSet.insert(attributes->at(i).Info); 331 332 for (uint32 i = 0; i < primaryKeyInfos.size(); i++) { // check to make sure all required attributes are in the key 333 InfoSet::const_iterator it = infoSet.find(primaryKeyInfos.at(i)); 334 335 if (it == infoSet.end()) { // not in the key? add the default 336 // we need to add a default value to the item attributes 337 attributes->add(primaryKeyInfos.at(i), defaultAttributeValue(primaryKeyInfos.at(i))); 338 } 339 } 340 } 341 342 Db db(keychain->database()); 343 if (mDoNotEncrypt) 344 { 345 mUniqueId = db->insertWithoutEncryption (recordType, NULL, mData.get()); 346 } 347 else if (useSecureStorage(db)) 348 { 349 // Add the item to the secure storage db 350 SSDbImpl* impl = dynamic_cast<SSDbImpl *>(&(*db)); 351 if (impl == NULL) 352 { 353 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER); 354 } 355 356 SSDb ssDb(impl); 357 358 TrackingAllocator allocator(Allocator::standard()); 359 360 // hhs replaced with the new aclFactory class 361 AclFactory aclFactory; 362 const AccessCredentials *nullCred = aclFactory.nullCred(); 363 364 SecPointer<Access> access = mAccess; 365 if (!access) { 366 // create default access controls for the new item 367 CssmDbAttributeData *data = mDbAttributes->find(Schema::attributeInfo(kSecLabelItemAttr)); 368 string printName = data ? CssmData::overlay(data->Value[0]).toString() : "keychain item"; 369 access = new Access(printName); 370 371 // special case for "iTools" password - allow anyone to decrypt the item 372 if (recordType == CSSM_DL_DB_RECORD_GENERIC_PASSWORD) 373 { 374 CssmDbAttributeData *data = mDbAttributes->find(Schema::attributeInfo(kSecServiceItemAttr)); 375 if (data && data->Value[0].Length == 6 && !memcmp("iTools", data->Value[0].Data, 6)) 376 { 377 typedef vector<SecPointer<ACL> > AclSet; 378 AclSet acls; 379 access->findAclsForRight(CSSM_ACL_AUTHORIZATION_DECRYPT, acls); 380 for (AclSet::const_iterator it = acls.begin(); it != acls.end(); it++) 381 (*it)->form(ACL::allowAllForm); 382 } 383 } 384 } 385 386 // Get the handle of the DL underlying this CSPDL. 387 CSSM_DL_DB_HANDLE dldbh; 388 db->passThrough(CSSM_APPLECSPDL_DB_GET_HANDLE, NULL, 389 reinterpret_cast<void **>(&dldbh)); 390 391 // Turn off autocommit on the underlying DL and remember the old state. 392 CSSM_BOOL autoCommit = CSSM_TRUE; 393 ObjectImpl::check(CSSM_DL_PassThrough(dldbh, 394 CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT, 395 0, reinterpret_cast<void **>(&autoCommit))); 396 397 try 398 { 399 // Create a new SSGroup with temporary access controls 400 Access::Maker maker; 401 ResourceControlContext prototype; 402 maker.initialOwner(prototype, nullCred); 403 SSGroup ssGroup(ssDb, &prototype); 404 405 try 406 { 407 // Insert the record using the newly created group. 408 mUniqueId = ssDb->insert(recordType, mDbAttributes.get(), 409 mData.get(), ssGroup, maker.cred()); 410 } 411 catch(...) 412 { 413 ssGroup->deleteKey(nullCred); 414 throw; 415 } 416 417 // now finalize the access controls on the group 418 access->setAccess(*ssGroup, maker); 419 mAccess = NULL; // use them and lose them 420 if (autoCommit) 421 { 422 // autoCommit was on so commit now that we are done and turn 423 // it back on. 424 ObjectImpl::check(CSSM_DL_PassThrough(dldbh, 425 CSSM_APPLEFILEDL_COMMIT, NULL, NULL)); 426 CSSM_DL_PassThrough(dldbh, CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT, 427 reinterpret_cast<const void *>(autoCommit), NULL); 428 } 429 } 430 catch (...) 431 { 432 if (autoCommit) 433 { 434 // autoCommit was off so rollback since we failed and turn 435 // autoCommit back on. 436 CSSM_DL_PassThrough(dldbh, CSSM_APPLEFILEDL_ROLLBACK, NULL, NULL); 437 CSSM_DL_PassThrough(dldbh, CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT, 438 reinterpret_cast<const void *>(autoCommit), NULL); 439 } 440 throw; 441 } 442 } 443 else 444 { 445 // add the item to the (regular) db 446 mUniqueId = db->insert(recordType, mDbAttributes.get(), mData.get()); 447 } 448 449 mPrimaryKey = keychain->makePrimaryKey(recordType, mUniqueId); 450 mKeychain = keychain; 451 452 // Forget our data and attributes. 453 mData = NULL; 454 mDbAttributes.reset(NULL); 455 456 return mPrimaryKey; 457} 458 459 460 461PrimaryKey 462ItemImpl::add (Keychain &keychain) 463{ 464 return addWithCopyInfo (keychain, false); 465} 466 467 468 469Item 470ItemImpl::copyTo(const Keychain &keychain, Access *newAccess) 471{ 472 StLock<Mutex>_(mMutex); 473 Item item(*this); 474 if (newAccess) 475 item->setAccess(newAccess); 476 else 477 { 478 /* Attempt to copy the access from the current item to the newly created one. */ 479 SSGroup myGroup = group(); 480 if (myGroup) 481 { 482 SecPointer<Access> access = new Access(*myGroup); 483 item->setAccess(access); 484 } 485 } 486 487 keychain->addCopy(item); 488 return item; 489} 490 491void 492ItemImpl::update() 493{ 494 StLock<Mutex>_(mMutex); 495 if (!mKeychain) 496 MacOSError::throwMe(errSecNoSuchKeychain); 497 498 // Don't update if nothing changed. 499 if (!isModified()) 500 return; 501 502 CSSM_DB_RECORDTYPE aRecordType = recordType(); 503 KeychainSchema schema = mKeychain->keychainSchema(); 504 505 // Update the modification date on the item if there is a mod date attribute. 506 if (schema->hasAttribute(aRecordType, kSecModDateItemAttr)) 507 { 508 SInt64 date; 509 GetCurrentMacLongDateTime(date); 510 setAttribute(schema->attributeInfoFor(aRecordType, kSecModDateItemAttr), date); 511 } 512 513 // Make sure that we have mUniqueId 514 dbUniqueRecord(); 515 Db db(mUniqueId->database()); 516 if (mDoNotEncrypt) 517 { 518 CSSM_DB_RECORD_ATTRIBUTE_DATA attrData; 519 memset (&attrData, 0, sizeof (attrData)); 520 attrData.DataRecordType = aRecordType; 521 522 mUniqueId->modifyWithoutEncryption(aRecordType, 523 &attrData, 524 mData.get(), 525 CSSM_DB_MODIFY_ATTRIBUTE_REPLACE); 526 } 527 else if (useSecureStorage(db)) 528 { 529 // Add the item to the secure storage db 530 SSDbUniqueRecordImpl * impl = dynamic_cast<SSDbUniqueRecordImpl *>(&(*mUniqueId)); 531 if (impl == NULL) 532 { 533 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER); 534 } 535 536 SSDbUniqueRecord ssUniqueId(impl); 537 538 // @@@ Share this instance 539 const AccessCredentials *autoPrompt = globals().itemCredentials(); 540 541 542 // Only call this is user interaction is enabled. 543 ssUniqueId->modify(aRecordType, 544 mDbAttributes.get(), 545 mData.get(), 546 CSSM_DB_MODIFY_ATTRIBUTE_REPLACE, 547 autoPrompt); 548 } 549 else 550 { 551 mUniqueId->modify(aRecordType, 552 mDbAttributes.get(), 553 mData.get(), 554 CSSM_DB_MODIFY_ATTRIBUTE_REPLACE); 555 } 556 557 if (!mDoNotEncrypt) 558 { 559 PrimaryKey oldPK = mPrimaryKey; 560 mPrimaryKey = mKeychain->makePrimaryKey(aRecordType, mUniqueId); 561 562 // Forget our data and attributes. 563 mData = NULL; 564 mDbAttributes.reset(NULL); 565 566 // Let the Keychain update what it needs to. 567 mKeychain->didUpdate(this, oldPK, mPrimaryKey); 568 } 569} 570 571void 572ItemImpl::getClass(SecKeychainAttribute &attr, UInt32 *actualLength) 573{ 574 StLock<Mutex>_(mMutex); 575 if (actualLength) 576 *actualLength = sizeof(SecItemClass); 577 578 if (attr.length < sizeof(SecItemClass)) 579 MacOSError::throwMe(errSecBufferTooSmall); 580 581 SecItemClass aClass = Schema::itemClassFor(recordType()); 582 memcpy(attr.data, &aClass, sizeof(SecItemClass)); 583} 584 585void 586ItemImpl::setAttribute(SecKeychainAttribute& attr) 587{ 588 StLock<Mutex>_(mMutex); 589 setAttribute(Schema::attributeInfo(attr.tag), CssmData(attr.data, attr.length)); 590} 591 592CSSM_DB_RECORDTYPE 593ItemImpl::recordType() 594{ 595 StLock<Mutex>_(mMutex); 596 if (mDbAttributes.get()) 597 return mDbAttributes->recordType(); 598 599 return mPrimaryKey->recordType(); 600} 601 602const DbAttributes * 603ItemImpl::modifiedAttributes() 604{ 605 StLock<Mutex>_(mMutex); 606 return mDbAttributes.get(); 607} 608 609const CssmData * 610ItemImpl::modifiedData() 611{ 612 StLock<Mutex>_(mMutex); 613 return mData.get(); 614} 615 616void 617ItemImpl::setData(UInt32 length,const void *data) 618{ 619 StLock<Mutex>_(mMutex); 620 mData = new CssmDataContainer(data, length); 621} 622 623void 624ItemImpl::setAccess(Access *newAccess) 625{ 626 StLock<Mutex>_(mMutex); 627 mAccess = newAccess; 628} 629 630CssmClient::DbUniqueRecord 631ItemImpl::dbUniqueRecord() 632{ 633 StLock<Mutex>_(mMutex); 634 if (!isPersistent()) // is there no database attached? 635 { 636 MacOSError::throwMe(errSecNotAvailable); 637 } 638 639 if (!mUniqueId) 640 { 641 DbCursor cursor(mPrimaryKey->createCursor(mKeychain)); 642 if (!cursor->next(NULL, NULL, mUniqueId)) 643 MacOSError::throwMe(errSecInvalidItemRef); 644 } 645 646 return mUniqueId; 647} 648 649PrimaryKey 650ItemImpl::primaryKey() 651{ 652 return mPrimaryKey; 653} 654 655bool 656ItemImpl::isPersistent() 657{ 658 return mKeychain; 659} 660 661bool 662ItemImpl::isModified() 663{ 664 StLock<Mutex>_(mMutex); 665 return mData.get() || mDbAttributes.get(); 666} 667 668Keychain 669ItemImpl::keychain() 670{ 671 return mKeychain; 672} 673 674bool 675ItemImpl::operator < (const ItemImpl &other) 676{ 677 if (mData && *mData) 678 { 679 // Pointer compare 680 return this < &other; 681 } 682 683 return mPrimaryKey < other.mPrimaryKey; 684} 685 686void 687ItemImpl::setAttribute(const CssmDbAttributeInfo &info, const CssmPolyData &data) 688{ 689 StLock<Mutex>_(mMutex); 690 if (!mDbAttributes.get()) 691 { 692 mDbAttributes.reset(new DbAttributes()); 693 mDbAttributes->recordType(mPrimaryKey->recordType()); 694 } 695 696 size_t length = data.Length; 697 const void *buf = reinterpret_cast<const void *>(data.Data); 698 uint8 timeString[16]; 699 700 // XXX This code is duplicated in KCCursorImpl::KCCursorImpl() 701 // Convert a 4 or 8 byte TIME_DATE to a CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE 702 // style attribute value. 703 if (info.format() == CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE) 704 { 705 if (length == sizeof(UInt32)) 706 { 707 MacSecondsToTimeString(*reinterpret_cast<const UInt32 *>(buf), 16, &timeString); 708 buf = &timeString; 709 length = 16; 710 } 711 else if (length == sizeof(SInt64)) 712 { 713 MacLongDateTimeToTimeString(*reinterpret_cast<const SInt64 *>(buf), 16, &timeString); 714 buf = &timeString; 715 length = 16; 716 } 717 } 718 719 mDbAttributes->add(info, CssmData(const_cast<void*>(buf), length)); 720} 721 722void 723ItemImpl::modifyContent(const SecKeychainAttributeList *attrList, UInt32 dataLength, const void *inData) 724{ 725 StLock<Mutex>_(mMutex); 726 if (!mDbAttributes.get()) 727 { 728 mDbAttributes.reset(new DbAttributes()); 729 mDbAttributes->recordType(mPrimaryKey->recordType()); 730 } 731 732 if(attrList) // optional 733 { 734 for(UInt32 ix=0; ix < attrList->count; ix++) 735 { 736 SecKeychainAttrType attrTag = attrList->attr[ix].tag; 737 738 if (attrTag == APPLEDB_CSSM_PRINTNAME_ATTRIBUTE) 739 { 740 // must remap a caller-supplied kSecKeyPrintName attribute tag for key items, since it isn't in the schema 741 // (note that this will ultimately match kGenericPrintName in Schema.cpp) 742 attrTag = kSecLabelItemAttr; 743 } 744 745 mDbAttributes->add(Schema::attributeInfo(attrTag), CssmData(attrList->attr[ix].data, attrList->attr[ix].length)); 746 } 747 } 748 749 if(inData) 750 { 751 mData = new CssmDataContainer(inData, dataLength); 752 } 753 754 update(); 755} 756 757void 758ItemImpl::getContent(SecItemClass *itemClass, SecKeychainAttributeList *attrList, UInt32 *length, void **outData) 759{ 760 StLock<Mutex>_(mMutex); 761 // If the data hasn't been set we can't return it. 762 if (!mKeychain && outData) 763 { 764 CssmData *data = mData.get(); 765 if (!data) 766 MacOSError::throwMe(errSecDataNotAvailable); 767 } 768 // TODO: need to check and make sure attrs are valid and handle error condition 769 770 771 if (itemClass) 772 *itemClass = Schema::itemClassFor(recordType()); 773 774 bool getDataFromDatabase = mKeychain && mPrimaryKey; 775 if (getDataFromDatabase) // are we attached to a database? 776 { 777 dbUniqueRecord(); 778 779 // get the number of attributes requested by the caller 780 UInt32 attrCount = attrList ? attrList->count : 0; 781 782 // make a DBAttributes structure and populate it 783 DbAttributes dbAttributes(mUniqueId->database(), attrCount); 784 for (UInt32 ix = 0; ix < attrCount; ++ix) 785 { 786 dbAttributes.add(Schema::attributeInfo(attrList->attr[ix].tag)); 787 } 788 789 // request the data from the database (since we are a reference "item" and the data is really stored there) 790 CssmDataContainer itemData; 791 getContent(&dbAttributes, outData ? &itemData : NULL); 792 793 // retrieve the data from result 794 for (UInt32 ix = 0; ix < attrCount; ++ix) 795 { 796 if (dbAttributes.at(ix).NumberOfValues > 0) 797 { 798 attrList->attr[ix].data = dbAttributes.at(ix).Value[0].Data; 799 attrList->attr[ix].length = (UInt32)dbAttributes.at(ix).Value[0].Length; 800 801 // We don't want the data released, it is up the client 802 dbAttributes.at(ix).Value[0].Data = NULL; 803 dbAttributes.at(ix).Value[0].Length = 0; 804 } 805 else 806 { 807 attrList->attr[ix].data = NULL; 808 attrList->attr[ix].length = 0; 809 } 810 } 811 812 // clean up 813 if (outData) 814 { 815 *outData=itemData.data(); 816 itemData.Data = NULL; 817 818 if (length) 819 *length=(UInt32)itemData.length(); 820 itemData.Length = 0; 821 } 822 } 823 else 824 { 825 getLocalContent(attrList, length, outData); 826 } 827 828 // Inform anyone interested that we are doing this 829#if SENDACCESSNOTIFICATIONS 830 if (outData) 831 { 832 secdebug("kcnotify", "ItemImpl::getContent(%p, %p, %p, %p) retrieved content", 833 itemClass, attrList, length, outData); 834 835 KCEventNotifier::PostKeychainEvent(kSecDataAccessEvent, mKeychain, this); 836 } 837#endif 838} 839 840void 841ItemImpl::freeContent(SecKeychainAttributeList *attrList, void *data) 842{ 843 Allocator &allocator = Allocator::standard(); // @@@ This might not match the one used originally 844 if (data) 845 allocator.free(data); 846 847 UInt32 attrCount = attrList ? attrList->count : 0; 848 for (UInt32 ix = 0; ix < attrCount; ++ix) 849 { 850 allocator.free(attrList->attr[ix].data); 851 attrList->attr[ix].data = NULL; 852 } 853} 854 855void 856ItemImpl::modifyAttributesAndData(const SecKeychainAttributeList *attrList, UInt32 dataLength, const void *inData) 857{ 858 StLock<Mutex>_(mMutex); 859 if (!mKeychain) 860 MacOSError::throwMe(errSecNoSuchKeychain); 861 862 if (!mDoNotEncrypt) 863 { 864 if (!mDbAttributes.get()) 865 { 866 mDbAttributes.reset(new DbAttributes()); 867 mDbAttributes->recordType(mPrimaryKey->recordType()); 868 } 869 870 CSSM_DB_RECORDTYPE recordType = mDbAttributes->recordType(); 871 UInt32 attrCount = attrList ? attrList->count : 0; 872 for (UInt32 ix = 0; ix < attrCount; ix++) 873 { 874 SecKeychainAttrType attrTag = attrList->attr[ix].tag; 875 876 if (attrTag == kSecLabelItemAttr) 877 { 878 // must remap a caller-supplied label attribute tag for password items, since it isn't in the schema 879 // (note that this will ultimately match kGenericPrintName in Schema.cpp) 880 if (IS_PASSWORD_ITEM_CLASS( Schema::itemClassFor(recordType) )) 881 attrTag = APPLEDB_GENERIC_PRINTNAME_ATTRIBUTE; 882 } 883 884 CssmDbAttributeInfo info=mKeychain->attributeInfoFor(recordType, attrTag); 885 886 if (attrList->attr[ix].length || info.AttributeFormat==CSSM_DB_ATTRIBUTE_FORMAT_STRING || info.AttributeFormat==CSSM_DB_ATTRIBUTE_FORMAT_BLOB 887 || info.AttributeFormat==CSSM_DB_ATTRIBUTE_FORMAT_STRING || info.AttributeFormat==CSSM_DB_ATTRIBUTE_FORMAT_BIG_NUM 888 || info.AttributeFormat==CSSM_DB_ATTRIBUTE_FORMAT_MULTI_UINT32) 889 mDbAttributes->add(info, CssmData(attrList->attr[ix].data, attrList->attr[ix].length)); 890 else 891 mDbAttributes->add(info); 892 } 893 } 894 895 if(inData) 896 { 897 mData = new CssmDataContainer(inData, dataLength); 898 } 899 900 update(); 901} 902 903void 904ItemImpl::getAttributesAndData(SecKeychainAttributeInfo *info, SecItemClass *itemClass, 905 SecKeychainAttributeList **attrList, UInt32 *length, void **outData) 906{ 907 StLock<Mutex>_(mMutex); 908 // If the data hasn't been set we can't return it. 909 if (!mKeychain && outData) 910 { 911 CssmData *data = mData.get(); 912 if (!data) 913 MacOSError::throwMe(errSecDataNotAvailable); 914 } 915 // TODO: need to check and make sure attrs are valid and handle error condition 916 917 SecItemClass myItemClass = Schema::itemClassFor(recordType()); 918 if (itemClass) 919 *itemClass = myItemClass; 920 921 // @@@ This call won't work for floating items (like certificates). 922 dbUniqueRecord(); 923 924 UInt32 attrCount = info ? info->count : 0; 925 DbAttributes dbAttributes(mUniqueId->database(), attrCount); 926 for (UInt32 ix = 0; ix < attrCount; ix++) 927 { 928 CssmDbAttributeData &record = dbAttributes.add(); 929 record.Info.AttributeNameFormat=CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER; 930 record.Info.Label.AttributeID=info->tag[ix]; 931 932 if (record.Info.Label.AttributeID == kSecLabelItemAttr) 933 { 934 // must remap a caller-supplied label attribute tag for password items, since it isn't in the schema 935 if (IS_PASSWORD_ITEM_CLASS( myItemClass )) 936 record.Info.Label.AttributeID = APPLEDB_GENERIC_PRINTNAME_ATTRIBUTE; 937 } 938 } 939 940 CssmDataContainer itemData; 941 getContent(&dbAttributes, outData ? &itemData : NULL); 942 943 if (info && attrList) 944 { 945 SecKeychainAttributeList *theList=reinterpret_cast<SecKeychainAttributeList *>(malloc(sizeof(SecKeychainAttributeList))); 946 SecKeychainAttribute *attr=reinterpret_cast<SecKeychainAttribute *>(malloc(sizeof(SecKeychainAttribute)*attrCount)); 947 theList->count=attrCount; 948 theList->attr=attr; 949 950 for (UInt32 ix = 0; ix < attrCount; ++ix) 951 { 952 attr[ix].tag=info->tag[ix]; 953 954 if (dbAttributes.at(ix).NumberOfValues > 0) 955 { 956 attr[ix].data = dbAttributes.at(ix).Value[0].Data; 957 attr[ix].length = (UInt32)dbAttributes.at(ix).Value[0].Length; 958 959 // We don't want the data released, it is up the client 960 dbAttributes.at(ix).Value[0].Data = NULL; 961 dbAttributes.at(ix).Value[0].Length = 0; 962 } 963 else 964 { 965 attr[ix].data = NULL; 966 attr[ix].length = 0; 967 } 968 } 969 *attrList=theList; 970 } 971 972 if (outData) 973 { 974 *outData=itemData.data(); 975 itemData.Data=NULL; 976 977 if (length) *length=(UInt32)itemData.length(); 978 itemData.Length=0; 979 980#if SENDACCESSNOTIFICATIONS 981 secdebug("kcnotify", "ItemImpl::getAttributesAndData(%p, %p, %p, %p, %p) retrieved data", 982 info, itemClass, attrList, length, outData); 983 984 KCEventNotifier::PostKeychainEvent(kSecDataAccessEvent, mKeychain, this); 985#endif 986 } 987 988} 989 990void 991ItemImpl::freeAttributesAndData(SecKeychainAttributeList *attrList, void *data) 992{ 993 Allocator &allocator = Allocator::standard(); // @@@ This might not match the one used originally 994 995 if (data) 996 allocator.free(data); 997 998 if (attrList) 999 { 1000 for (UInt32 ix = 0; ix < attrList->count; ++ix) 1001 { 1002 allocator.free(attrList->attr[ix].data); 1003 } 1004 free(attrList->attr); 1005 free(attrList); 1006 } 1007} 1008 1009void 1010ItemImpl::getAttribute(SecKeychainAttribute& attr, UInt32 *actualLength) 1011{ 1012 StLock<Mutex>_(mMutex); 1013 if (attr.tag == kSecClassItemAttr) 1014 return getClass(attr, actualLength); 1015 1016 if (mDbAttributes.get()) 1017 { 1018 CssmDbAttributeData *data = mDbAttributes->find(Schema::attributeInfo(attr.tag)); 1019 if (data) 1020 { 1021 getAttributeFrom(data, attr, actualLength); 1022 return; 1023 } 1024 } 1025 1026 if (!mKeychain) 1027 MacOSError::throwMe(errSecNoSuchAttr); 1028 1029 dbUniqueRecord(); 1030 DbAttributes dbAttributes(mUniqueId->database(), 1); 1031 dbAttributes.add(Schema::attributeInfo(attr.tag)); 1032 mUniqueId->get(&dbAttributes, NULL); 1033 getAttributeFrom(&dbAttributes.at(0), attr, actualLength); 1034} 1035 1036void 1037ItemImpl::getAttributeFrom(CssmDbAttributeData *data, SecKeychainAttribute &attr, UInt32 *actualLength) 1038{ 1039 StLock<Mutex>_(mMutex); 1040 static const uint32 zero = 0; 1041 UInt32 length; 1042 const void *buf = NULL; 1043 1044 // Temporary storage for buf. 1045 sint64 macLDT; 1046 uint32 macSeconds; 1047 sint16 svalue16; 1048 uint16 uvalue16; 1049 sint8 svalue8; 1050 uint8 uvalue8; 1051 1052 if (!data) 1053 length = 0; 1054 else if (data->size() < 1) // Attribute has no values. 1055 { 1056 if (data->format() == CSSM_DB_ATTRIBUTE_FORMAT_SINT32 1057 || data->format() == CSSM_DB_ATTRIBUTE_FORMAT_UINT32) 1058 { 1059 length = sizeof(zero); 1060 buf = &zero; 1061 } 1062 else if (CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE) 1063 length = 0; // Should we throw here? 1064 else // All other formats 1065 length = 0; 1066 } 1067 else // Get the first value 1068 { 1069 length = (UInt32)data->Value[0].Length; 1070 buf = data->Value[0].Data; 1071 1072 if (data->format() == CSSM_DB_ATTRIBUTE_FORMAT_SINT32) 1073 { 1074 if (attr.length == sizeof(sint8)) 1075 { 1076 length = attr.length; 1077 svalue8 = sint8(*reinterpret_cast<const sint32 *>(buf)); 1078 buf = &svalue8; 1079 } 1080 else if (attr.length == sizeof(sint16)) 1081 { 1082 length = attr.length; 1083 svalue16 = sint16(*reinterpret_cast<const sint32 *>(buf)); 1084 buf = &svalue16; 1085 } 1086 } 1087 else if (data->format() == CSSM_DB_ATTRIBUTE_FORMAT_UINT32) 1088 { 1089 if (attr.length == sizeof(uint8)) 1090 { 1091 length = attr.length; 1092 uvalue8 = uint8(*reinterpret_cast<const uint32 *>(buf)); 1093 buf = &uvalue8; 1094 } 1095 else if (attr.length == sizeof(uint16)) 1096 { 1097 length = attr.length; 1098 uvalue16 = uint16(*reinterpret_cast<const uint32 *>(buf)); 1099 buf = &uvalue16; 1100 } 1101 } 1102 else if (data->format() == CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE) 1103 { 1104 if (attr.length == sizeof(uint32)) 1105 { 1106 TimeStringToMacSeconds(data->Value[0], macSeconds); 1107 buf = &macSeconds; 1108 length = attr.length; 1109 } 1110 else if (attr.length == sizeof(sint64)) 1111 { 1112 TimeStringToMacLongDateTime(data->Value[0], macLDT); 1113 buf = &macLDT; 1114 length = attr.length; 1115 } 1116 } 1117 } 1118 1119 if (actualLength) 1120 *actualLength = length; 1121 1122 if (length) 1123 { 1124 if (attr.length < length) 1125 MacOSError::throwMe(errSecBufferTooSmall); 1126 1127 memcpy(attr.data, buf, length); 1128 } 1129} 1130 1131void 1132ItemImpl::getData(CssmDataContainer& outData) 1133{ 1134 StLock<Mutex>_(mMutex); 1135 if (!mKeychain) 1136 { 1137 CssmData *data = mData.get(); 1138 // If the data hasn't been set we can't return it. 1139 if (!data) 1140 MacOSError::throwMe(errSecDataNotAvailable); 1141 1142 outData = *data; 1143 return; 1144 } 1145 1146 getContent(NULL, &outData); 1147 1148#if SENDACCESSNOTIFICATIONS 1149 secdebug("kcnotify", "ItemImpl::getData retrieved data"); 1150 1151 //%%%<might> be done elsewhere, but here is good for now 1152 KCEventNotifier::PostKeychainEvent(kSecDataAccessEvent, mKeychain, this); 1153#endif 1154} 1155 1156SSGroup 1157ItemImpl::group() 1158{ 1159 StLock<Mutex>_(mMutex); 1160 SSGroup group; 1161 if (!!mUniqueId) 1162 { 1163 Db db(mKeychain->database()); 1164 if (useSecureStorage(db)) 1165 { 1166 group = safer_cast<SSDbUniqueRecordImpl &>(*mUniqueId).group(); 1167 } 1168 } 1169 1170 return group; 1171} 1172 1173void ItemImpl::getLocalContent(SecKeychainAttributeList *attributeList, UInt32 *outLength, void **outData) 1174{ 1175 StLock<Mutex>_(mMutex); 1176 willRead(); 1177 Allocator &allocator = Allocator::standard(); // @@@ This might not match the one used originally 1178 if (outData) 1179 { 1180 CssmData *data = mData.get(); 1181 if (!data) 1182 MacOSError::throwMe(errSecDataNotAvailable); 1183 1184 // Copy the data out of our internal cached copy. 1185 UInt32 length = (UInt32)data->Length; 1186 *outData = allocator.malloc(length); 1187 memcpy(*outData, data->Data, length); 1188 if (outLength) 1189 *outLength = length; 1190 } 1191 1192 if (attributeList) 1193 { 1194 if (!mDbAttributes.get()) 1195 MacOSError::throwMe(errSecDataNotAvailable); 1196 1197 // Pull attributes out of a "floating" item, i.e. one that isn't attached to a database 1198 for (UInt32 ix = 0; ix < attributeList->count; ++ix) 1199 { 1200 SecKeychainAttribute &attribute = attributeList->attr[ix]; 1201 CssmDbAttributeData *data = mDbAttributes->find(Schema::attributeInfo(attribute.tag)); 1202 if (data && data->NumberOfValues > 0) 1203 { 1204 // Copy the data out of our internal cached copy. 1205 UInt32 length = (UInt32)data->Value[0].Length; 1206 attribute.data = allocator.malloc(length); 1207 memcpy(attribute.data, data->Value[0].Data, length); 1208 attribute.length = length; 1209 } 1210 else 1211 { 1212 attribute.length = 0; 1213 attribute.data = NULL; 1214 } 1215 } 1216 } 1217} 1218 1219void 1220ItemImpl::getContent(DbAttributes *dbAttributes, CssmDataContainer *itemData) 1221{ 1222 StLock<Mutex>_(mMutex); 1223 // Make sure mUniqueId is set. 1224 dbUniqueRecord(); 1225 if (itemData) 1226 { 1227 Db db(mUniqueId->database()); 1228 if (mDoNotEncrypt) 1229 { 1230 mUniqueId->getWithoutEncryption (dbAttributes, itemData); 1231 return; 1232 } 1233 if (useSecureStorage(db)) 1234 { 1235 SSDbUniqueRecordImpl* impl = dynamic_cast<SSDbUniqueRecordImpl *>(&(*mUniqueId)); 1236 if (impl == NULL) 1237 { 1238 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER); 1239 } 1240 1241 SSDbUniqueRecord ssUniqueId(impl); 1242 const AccessCredentials *autoPrompt = globals().itemCredentials(); 1243 ssUniqueId->get(dbAttributes, itemData, autoPrompt); 1244 return; 1245 } 1246 } 1247 1248 mUniqueId->get(dbAttributes, itemData); 1249} 1250 1251bool 1252ItemImpl::useSecureStorage(const Db &db) 1253{ 1254 StLock<Mutex>_(mMutex); 1255 switch (recordType()) 1256 { 1257 case CSSM_DL_DB_RECORD_GENERIC_PASSWORD: 1258 case CSSM_DL_DB_RECORD_INTERNET_PASSWORD: 1259 case CSSM_DL_DB_RECORD_APPLESHARE_PASSWORD: 1260 if (db->dl()->subserviceMask() & CSSM_SERVICE_CSP) 1261 return true; 1262 break; 1263 default: 1264 break; 1265 } 1266 return false; 1267} 1268 1269void ItemImpl::willRead() 1270{ 1271} 1272 1273Item ItemImpl::makeFromPersistentReference(const CFDataRef persistentRef, bool *isIdentityRef) 1274{ 1275 CssmData dictData((void*)::CFDataGetBytePtr(persistentRef), ::CFDataGetLength(persistentRef)); 1276 NameValueDictionary dict(dictData); 1277 1278 Keychain keychain; 1279 Item item = (ItemImpl *) NULL; 1280 1281 if (isIdentityRef) { 1282 *isIdentityRef = (dict.FindByName(IDENTITY_KEY) != 0) ? true : false; 1283 } 1284 1285 // make sure we have a database identifier 1286 if (dict.FindByName(SSUID_KEY) != 0) 1287 { 1288 DLDbIdentifier dlDbIdentifier = NameValueDictionary::MakeDLDbIdentifierFromNameValueDictionary(dict); 1289 DLDbIdentifier newDlDbIdentifier(dlDbIdentifier.ssuid(), 1290 DLDbListCFPref::ExpandTildesInPath(dlDbIdentifier.dbName()).c_str(), 1291 dlDbIdentifier.dbLocation()); 1292 1293 keychain = globals().storageManager.keychain(newDlDbIdentifier); 1294 1295 const NameValuePair* aDictItem = dict.FindByName(ITEM_KEY); 1296 if (aDictItem && keychain) 1297 { 1298 PrimaryKey primaryKey(aDictItem->Value()); 1299 item = keychain->item(primaryKey); 1300 } 1301 } 1302 KCThrowIf_( !item, errSecItemNotFound ); 1303 return item; 1304} 1305 1306void ItemImpl::copyPersistentReference(CFDataRef &outDataRef, bool isSecIdentityRef) 1307{ 1308 if (secd_PersistentRef) { 1309 outDataRef = secd_PersistentRef; 1310 return; 1311 } 1312 StLock<Mutex>_(mMutex); 1313 // item must be in a keychain and have a primary key to be persistent 1314 if (!mKeychain || !mPrimaryKey) { 1315 MacOSError::throwMe(errSecItemNotFound); 1316 } 1317 DLDbIdentifier dlDbIdentifier = mKeychain->dlDbIdentifier(); 1318 DLDbIdentifier newDlDbIdentifier(dlDbIdentifier.ssuid(), 1319 DLDbListCFPref::AbbreviatedPath(mKeychain->name()).c_str(), 1320 dlDbIdentifier.dbLocation()); 1321 NameValueDictionary dict; 1322 NameValueDictionary::MakeNameValueDictionaryFromDLDbIdentifier(newDlDbIdentifier, dict); 1323 1324 CssmData* pKey = mPrimaryKey; 1325 dict.Insert (new NameValuePair(ITEM_KEY, *pKey)); 1326 1327 if (isSecIdentityRef) { 1328 uint32_t value = -1; 1329 CssmData valueData((void*)&value, sizeof(value)); 1330 dict.Insert (new NameValuePair(IDENTITY_KEY, valueData)); 1331 } 1332 1333 // flatten the NameValueDictionary 1334 CssmData dictData; 1335 dict.Export(dictData); 1336 outDataRef = ::CFDataCreate(kCFAllocatorDefault, dictData.Data, dictData.Length); 1337 free (dictData.Data); 1338} 1339 1340void ItemImpl::copyRecordIdentifier(CSSM_DATA &data) 1341{ 1342 StLock<Mutex>_(mMutex); 1343 CssmClient::DbUniqueRecord uniqueRecord = dbUniqueRecord (); 1344 uniqueRecord->getRecordIdentifier(data); 1345} 1346 1347/* 1348 * Obtain blob used to bind a keychain item to an Extended Attribute record. 1349 * We just use the PrimaryKey blob as the default. Note that for standard Items, 1350 * this can cause the loss of extended attribute bindings if a Primary Key 1351 * attribute changes. 1352 */ 1353const CssmData &ItemImpl::itemID() 1354{ 1355 StLock<Mutex>_(mMutex); 1356 if(mPrimaryKey->length() == 0) { 1357 /* not in a keychain; we don't have a primary key */ 1358 MacOSError::throwMe(errSecNoSuchAttr); 1359 } 1360 return *mPrimaryKey; 1361} 1362 1363bool ItemImpl::equal(SecCFObject &other) 1364{ 1365 // First check to see if both items have a primary key and 1366 // if the primary key is the same. If so then these 1367 // items must be equal 1368 ItemImpl& other_item = (ItemImpl&)other; 1369 if (mPrimaryKey != NULL && mPrimaryKey == other_item.mPrimaryKey) 1370 { 1371 return true; 1372 } 1373 1374 // The primary keys do not match so do a CFHash of the 1375 // data of the item and compare those for equality 1376 CFHashCode this_hash = hash(); 1377 CFHashCode other_hash = other.hash(); 1378 return (this_hash == other_hash); 1379} 1380 1381CFHashCode ItemImpl::hash() 1382{ 1383 CFHashCode result = SecCFObject::hash(); 1384 1385 StLock<Mutex>_(mMutex); 1386 RefPointer<CssmDataContainer> data_to_hash; 1387 1388 // Use the item data for the hash 1389 if (mData && *mData) 1390 { 1391 data_to_hash = mData; 1392 } 1393 1394 // If there is no primary key AND not data ???? 1395 // just return the 'old' hash value which is the 1396 // object pointer. 1397 if (NULL != data_to_hash.get()) 1398 { 1399 CFDataRef temp_data = NULL; 1400 unsigned char digest[CC_SHA256_DIGEST_LENGTH]; 1401 1402 if (data_to_hash->length() < 80) 1403 { 1404 // If it is less than 80 bytes then CFData can be used 1405 temp_data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, 1406 (const UInt8 *)data_to_hash->data(), data_to_hash->length(), kCFAllocatorNull); 1407 1408 } 1409 // CFData truncates its hash value to 80 bytes. ???? 1410 // In order to do the 'right thing' a SHA 256 hash will be used to 1411 // include all of the data 1412 else 1413 { 1414 memset(digest, 0, CC_SHA256_DIGEST_LENGTH); 1415 1416 CC_SHA256((const void *)data_to_hash->data(), (CC_LONG)data_to_hash->length(), digest); 1417 1418 temp_data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, 1419 (const UInt8 *)digest, CC_SHA256_DIGEST_LENGTH, kCFAllocatorNull); 1420 } 1421 1422 if (NULL != temp_data) 1423 { 1424 result = CFHash(temp_data); 1425 CFRelease(temp_data); 1426 } 1427 1428 } 1429 1430 return result; 1431} 1432 1433 1434void ItemImpl::postItemEvent(SecKeychainEvent theEvent) 1435{ 1436 mKeychain->postEvent(theEvent, this); 1437} 1438 1439 1440 1441// 1442// Item -- This class is here to magically create the right subclass of ItemImpl 1443// when constructing new items. 1444// 1445Item::Item() 1446{ 1447} 1448 1449Item::Item(ItemImpl *impl) : SecPointer<ItemImpl>(impl) 1450{ 1451} 1452 1453Item::Item(SecItemClass itemClass, OSType itemCreator, UInt32 length, const void* data, bool inhibitCheck) 1454{ 1455 if (!inhibitCheck) 1456 { 1457 if (itemClass == CSSM_DL_DB_RECORD_X509_CERTIFICATE 1458 || itemClass == CSSM_DL_DB_RECORD_PUBLIC_KEY 1459 || itemClass == CSSM_DL_DB_RECORD_PRIVATE_KEY 1460 || itemClass == CSSM_DL_DB_RECORD_SYMMETRIC_KEY) 1461 MacOSError::throwMe(errSecNoSuchClass); /* @@@ errSecInvalidClass */ 1462 } 1463 1464 *this = new ItemImpl(itemClass, itemCreator, length, data, inhibitCheck); 1465} 1466 1467Item::Item(SecItemClass itemClass, SecKeychainAttributeList *attrList, UInt32 length, const void* data) 1468{ 1469 *this = new ItemImpl(itemClass, attrList, length, data); 1470} 1471 1472Item::Item(const Keychain &keychain, const PrimaryKey &primaryKey, const CssmClient::DbUniqueRecord &uniqueId) 1473 : SecPointer<ItemImpl>( 1474 primaryKey->recordType() == CSSM_DL_DB_RECORD_X509_CERTIFICATE 1475 ? Certificate::make(keychain, primaryKey, uniqueId) 1476 : (primaryKey->recordType() == CSSM_DL_DB_RECORD_PUBLIC_KEY 1477 || primaryKey->recordType() == CSSM_DL_DB_RECORD_PRIVATE_KEY 1478 || primaryKey->recordType() == CSSM_DL_DB_RECORD_SYMMETRIC_KEY) 1479 ? KeyItem::make(keychain, primaryKey, uniqueId) 1480 : primaryKey->recordType() == CSSM_DL_DB_RECORD_EXTENDED_ATTRIBUTE 1481 ? ExtendedAttribute::make(keychain, primaryKey, uniqueId) 1482 : ItemImpl::make(keychain, primaryKey, uniqueId)) 1483{ 1484} 1485 1486Item::Item(const Keychain &keychain, const PrimaryKey &primaryKey) 1487 : SecPointer<ItemImpl>( 1488 primaryKey->recordType() == CSSM_DL_DB_RECORD_X509_CERTIFICATE 1489 ? Certificate::make(keychain, primaryKey) 1490 : (primaryKey->recordType() == CSSM_DL_DB_RECORD_PUBLIC_KEY 1491 || primaryKey->recordType() == CSSM_DL_DB_RECORD_PRIVATE_KEY 1492 || primaryKey->recordType() == CSSM_DL_DB_RECORD_SYMMETRIC_KEY) 1493 ? KeyItem::make(keychain, primaryKey) 1494 : primaryKey->recordType() == CSSM_DL_DB_RECORD_EXTENDED_ATTRIBUTE 1495 ? ExtendedAttribute::make(keychain, primaryKey) 1496 : ItemImpl::make(keychain, primaryKey)) 1497{ 1498} 1499 1500Item::Item(ItemImpl &item) 1501 : SecPointer<ItemImpl>( 1502 item.recordType() == CSSM_DL_DB_RECORD_X509_CERTIFICATE 1503 ? new Certificate(safer_cast<Certificate &>(item)) 1504 : (item.recordType() == CSSM_DL_DB_RECORD_PUBLIC_KEY 1505 || item.recordType() == CSSM_DL_DB_RECORD_PRIVATE_KEY 1506 || item.recordType() == CSSM_DL_DB_RECORD_SYMMETRIC_KEY) 1507 ? new KeyItem(safer_cast<KeyItem &>(item)) 1508 : item.recordType() == CSSM_DL_DB_RECORD_EXTENDED_ATTRIBUTE 1509 ? new ExtendedAttribute(safer_cast<ExtendedAttribute &>(item)) 1510 : new ItemImpl(item)) 1511{ 1512} 1513 1514CFIndex KeychainCore::GetItemRetainCount(Item& item) 1515{ 1516 return CFGetRetainCount(item->handle(false)); 1517} 1518 1519void ItemImpl::setPersistentRef(CFDataRef ref) 1520{ 1521 if (secd_PersistentRef) { 1522 CFRelease(secd_PersistentRef); 1523 } 1524 secd_PersistentRef = ref; 1525 CFRetain(ref); 1526} 1527 1528CFDataRef ItemImpl::getPersistentRef() 1529{ 1530 return secd_PersistentRef; 1531} 1532 1533 1534 1535bool ItemImpl::mayDelete() 1536{ 1537 ObjectImpl* uniqueIDImpl = mUniqueId.get(); 1538 1539 if (uniqueIDImpl != NULL) 1540 { 1541 bool result = mUniqueId->isIdle(); 1542 return result; 1543 } 1544 else 1545 { 1546 return true; 1547 } 1548} 1549