X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/5dd5f9ec28f304ca377c42fd7f711d6cf12b90e1..5c19dc3ae3bd8e40a9c028b0deddd50ff337692c:/Security/libsecurity_keychain/lib/Item.cpp?ds=inline diff --git a/Security/libsecurity_keychain/lib/Item.cpp b/Security/libsecurity_keychain/lib/Item.cpp deleted file mode 100644 index 057f0618..00000000 --- a/Security/libsecurity_keychain/lib/Item.cpp +++ /dev/null @@ -1,1548 +0,0 @@ -/* - * Copyright (c) 2000-2004,2011-2014 Apple Inc. All Rights Reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - - -// -// Item.cpp -// - -#include "Item.h" - -#include "Certificate.h" -#include "KeyItem.h" -#include "ExtendedAttribute.h" - -#include "Globals.h" -#include -#include "KCEventNotifier.h" -#include "KCExceptions.h" -#include "cssmdatetime.h" -#include -#include -#include -#include -#include -#include - -#define SENDACCESSNOTIFICATIONS 1 - -//%%% schema indexes should be defined in Schema.h -#define _kSecAppleSharePasswordItemClass 'ashp' -#define APPLEDB_CSSM_PRINTNAME_ATTRIBUTE 1 /* schema index for label attribute of keys or certificates */ -#define APPLEDB_GENERIC_PRINTNAME_ATTRIBUTE 7 /* schema index for label attribute of password items */ -#define IS_PASSWORD_ITEM_CLASS(X) ( (X) == kSecInternetPasswordItemClass || \ - (X) == kSecGenericPasswordItemClass || \ - (X) == _kSecAppleSharePasswordItemClass ) ? 1 : 0 - -using namespace KeychainCore; -using namespace CSSMDateTimeUtils; - -// -// ItemImpl -// - -// NewItemImpl constructor -ItemImpl::ItemImpl(SecItemClass itemClass, OSType itemCreator, UInt32 length, const void* data, bool dontDoAttributes) - : mDbAttributes(new DbAttributes()), - mKeychain(NULL), - secd_PersistentRef(NULL), - mDoNotEncrypt(false), - mInCache(false), - mMutex(Mutex::recursive) -{ - if (length && data) - mData = new CssmDataContainer(data, length); - - mDbAttributes->recordType(Schema::recordTypeFor(itemClass)); - - if (itemCreator) - mDbAttributes->add(Schema::attributeInfo(kSecCreatorItemAttr), itemCreator); -} - -ItemImpl::ItemImpl(SecItemClass itemClass, SecKeychainAttributeList *attrList, UInt32 length, const void* data) - : mDbAttributes(new DbAttributes()), - mKeychain(NULL), - secd_PersistentRef(NULL), - mDoNotEncrypt(false), - mInCache(false), - mMutex(Mutex::recursive) -{ - if (length && data) - mData = new CssmDataContainer(data, length); - - - mDbAttributes->recordType(Schema::recordTypeFor(itemClass)); - - if(attrList) - { - for(UInt32 i=0; i < attrList->count; i++) - { - mDbAttributes->add(Schema::attributeInfo(attrList->attr[i].tag), CssmData(attrList->attr[i].data, attrList->attr[i].length)); - } - } -} - -// DbItemImpl constructor -ItemImpl::ItemImpl(const Keychain &keychain, const PrimaryKey &primaryKey, const DbUniqueRecord &uniqueId) - : mUniqueId(uniqueId), mKeychain(keychain), mPrimaryKey(primaryKey), - secd_PersistentRef(NULL), mDoNotEncrypt(false), mInCache(false), - mMutex(Mutex::recursive) -{ -} - -// PrimaryKey ItemImpl constructor -ItemImpl::ItemImpl(const Keychain &keychain, const PrimaryKey &primaryKey) -: mKeychain(keychain), mPrimaryKey(primaryKey), secd_PersistentRef(NULL), mDoNotEncrypt(false), - mInCache(false), - mMutex(Mutex::recursive) -{ -} - -ItemImpl* ItemImpl::make(const Keychain &keychain, const PrimaryKey &primaryKey, const CssmClient::DbUniqueRecord &uniqueId) -{ - ItemImpl* ii = new ItemImpl(keychain, primaryKey, uniqueId); - keychain->addItem(primaryKey, ii); - return ii; -} - - - -ItemImpl* ItemImpl::make(const Keychain &keychain, const PrimaryKey &primaryKey) -{ - ItemImpl* ii = new ItemImpl(keychain, primaryKey); - keychain->addItem(primaryKey, ii); - return ii; -} - - - -// Constructor used when copying an item to a keychain. - -ItemImpl::ItemImpl(ItemImpl &item) : - mData(item.modifiedData() ? NULL : new CssmDataContainer()), - mDbAttributes(new DbAttributes()), - mKeychain(NULL), - secd_PersistentRef(NULL), - mDoNotEncrypt(false), - mInCache(false), - mMutex(Mutex::recursive) -{ - mDbAttributes->recordType(item.recordType()); - CSSM_DB_RECORD_ATTRIBUTE_INFO *schemaAttributes = NULL; - - if (item.mKeychain) { - // get the entire source item from its keychain. This requires figuring - // out the schema for the item based on its record type. - - for (uint32 i = 0; i < Schema::DBInfo.NumberOfRecordTypes; i++) - if (item.recordType() == Schema::DBInfo.RecordAttributeNames[i].DataRecordType) { - schemaAttributes = &Schema::DBInfo.RecordAttributeNames[i]; - break; - } - - if (schemaAttributes == NULL) - // the source item is invalid - MacOSError::throwMe(errSecInvalidItemRef); - - for (uint32 i = 0; i < schemaAttributes->NumberOfAttributes; i++) - mDbAttributes->add(schemaAttributes->AttributeInfo[i]); - - item.getContent(mDbAttributes.get(), mData.get()); - } - - // @@@ We don't deal with modified attributes. - - if (item.modifiedData()) - // the copied data comes from the source item - mData = new CssmDataContainer(item.modifiedData()->Data, - item.modifiedData()->Length); -} - -ItemImpl::~ItemImpl() -{ - if (secd_PersistentRef) { - CFRelease(secd_PersistentRef); - } -} - - - -Mutex* -ItemImpl::getMutexForObject() -{ - if (mKeychain.get()) - { - return mKeychain->getKeychainMutex(); - } - - return NULL; -} - - - -void -ItemImpl::aboutToDestruct() -{ - if (mKeychain && *mPrimaryKey) - { - mKeychain->removeItem(mPrimaryKey, this); - } -} - - - -void -ItemImpl::didModify() -{ - StLock_(mMutex); - mData = NULL; - mDbAttributes.reset(NULL); -} - -const CSSM_DATA & -ItemImpl::defaultAttributeValue(const CSSM_DB_ATTRIBUTE_INFO &info) -{ - static const uint32 zeroInt = 0; - static const double zeroDouble = 0.0; - static const char timeBytes[] = "20010101000000Z"; - - static const CSSM_DATA defaultFourBytes = { 4, (uint8 *) &zeroInt }; - static const CSSM_DATA defaultEightBytes = { 8, (uint8 *) &zeroDouble }; - static const CSSM_DATA defaultTime = { 16, (uint8 *) timeBytes }; - static const CSSM_DATA defaultZeroBytes = { 0, NULL }; - - switch (info.AttributeFormat) - { - case CSSM_DB_ATTRIBUTE_FORMAT_SINT32: - case CSSM_DB_ATTRIBUTE_FORMAT_UINT32: - return defaultFourBytes; - - case CSSM_DB_ATTRIBUTE_FORMAT_REAL: - return defaultEightBytes; - - case CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE: - return defaultTime; - - default: - return defaultZeroBytes; - } -} - - - -PrimaryKey ItemImpl::addWithCopyInfo (Keychain &keychain, bool isCopy) -{ - StLock_(mMutex); - // If we already have a Keychain we can't be added. - if (mKeychain) - MacOSError::throwMe(errSecDuplicateItem); - - // If we don't have any attributes we can't be added. - // (this might occur if attempting to add the item twice, since our attributes - // and data are set to NULL at the end of this function.) - if (!mDbAttributes.get()) - MacOSError::throwMe(errSecDuplicateItem); - - CSSM_DB_RECORDTYPE recordType = mDbAttributes->recordType(); - - // update the creation and update dates on the new item - if (!isCopy) - { - KeychainSchema schema = keychain->keychainSchema(); - SInt64 date; - GetCurrentMacLongDateTime(date); - if (schema->hasAttribute(recordType, kSecCreationDateItemAttr)) - { - setAttribute(schema->attributeInfoFor(recordType, kSecCreationDateItemAttr), date); - } - - if (schema->hasAttribute(recordType, kSecModDateItemAttr)) - { - setAttribute(schema->attributeInfoFor(recordType, kSecModDateItemAttr), date); - } - } - - // If the label (PrintName) attribute isn't specified, set a default label. - if (!mDoNotEncrypt && !mDbAttributes->find(Schema::attributeInfo(kSecLabelItemAttr))) - { - // if doNotEncrypt was set all of the attributes are wrapped in the data blob. Don't calculate here. - CssmDbAttributeData *label = NULL; - switch (recordType) - { - case CSSM_DL_DB_RECORD_GENERIC_PASSWORD: - label = mDbAttributes->find(Schema::attributeInfo(kSecServiceItemAttr)); - break; - - case CSSM_DL_DB_RECORD_APPLESHARE_PASSWORD: - case CSSM_DL_DB_RECORD_INTERNET_PASSWORD: - label = mDbAttributes->find(Schema::attributeInfo(kSecServerItemAttr)); - // if AppleShare server name wasn't specified, try the server address - if (!label) label = mDbAttributes->find(Schema::attributeInfo(kSecAddressItemAttr)); - break; - - default: - break; - } - // if all else fails, use the account name. - if (!label) - label = mDbAttributes->find(Schema::attributeInfo(kSecAccountItemAttr)); - - if (label && label->size()) - setAttribute (Schema::attributeInfo(kSecLabelItemAttr), label->at(0)); - } - - // get the attributes that are part of the primary key - const CssmAutoDbRecordAttributeInfo &primaryKeyInfos = - keychain->primaryKeyInfosFor(recordType); - - // make sure each primary key element has a value in the item, otherwise - // the database will complain. we make a set of the provided attribute infos - // to avoid O(N^2) behavior. - - DbAttributes *attributes = mDbAttributes.get(); - typedef set InfoSet; - InfoSet infoSet; - - if (!mDoNotEncrypt) - { - // make a set of all the attributes in the key - for (uint32 i = 0; i < attributes->size(); i++) - infoSet.insert(attributes->at(i).Info); - - for (uint32 i = 0; i < primaryKeyInfos.size(); i++) { // check to make sure all required attributes are in the key - InfoSet::const_iterator it = infoSet.find(primaryKeyInfos.at(i)); - - if (it == infoSet.end()) { // not in the key? add the default - // we need to add a default value to the item attributes - attributes->add(primaryKeyInfos.at(i), defaultAttributeValue(primaryKeyInfos.at(i))); - } - } - } - - Db db(keychain->database()); - if (mDoNotEncrypt) - { - mUniqueId = db->insertWithoutEncryption (recordType, NULL, mData.get()); - } - else if (useSecureStorage(db)) - { - // Add the item to the secure storage db - SSDbImpl* impl = dynamic_cast(&(*db)); - if (impl == NULL) - { - CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER); - } - - SSDb ssDb(impl); - - TrackingAllocator allocator(Allocator::standard()); - - // hhs replaced with the new aclFactory class - AclFactory aclFactory; - const AccessCredentials *nullCred = aclFactory.nullCred(); - - SecPointer access = mAccess; - if (!access) { - // create default access controls for the new item - CssmDbAttributeData *data = mDbAttributes->find(Schema::attributeInfo(kSecLabelItemAttr)); - string printName = data ? CssmData::overlay(data->Value[0]).toString() : "keychain item"; - access = new Access(printName); - - // special case for "iTools" password - allow anyone to decrypt the item - if (recordType == CSSM_DL_DB_RECORD_GENERIC_PASSWORD) - { - CssmDbAttributeData *data = mDbAttributes->find(Schema::attributeInfo(kSecServiceItemAttr)); - if (data && data->Value[0].Length == 6 && !memcmp("iTools", data->Value[0].Data, 6)) - { - typedef vector > AclSet; - AclSet acls; - access->findAclsForRight(CSSM_ACL_AUTHORIZATION_DECRYPT, acls); - for (AclSet::const_iterator it = acls.begin(); it != acls.end(); it++) - (*it)->form(ACL::allowAllForm); - } - } - } - - // Get the handle of the DL underlying this CSPDL. - CSSM_DL_DB_HANDLE dldbh; - db->passThrough(CSSM_APPLECSPDL_DB_GET_HANDLE, NULL, - reinterpret_cast(&dldbh)); - - // Turn off autocommit on the underlying DL and remember the old state. - CSSM_BOOL autoCommit = CSSM_TRUE; - ObjectImpl::check(CSSM_DL_PassThrough(dldbh, - CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT, - 0, reinterpret_cast(&autoCommit))); - - try - { - // Create a new SSGroup with temporary access controls - Access::Maker maker; - ResourceControlContext prototype; - maker.initialOwner(prototype, nullCred); - SSGroup ssGroup(ssDb, &prototype); - - try - { - // Insert the record using the newly created group. - mUniqueId = ssDb->insert(recordType, mDbAttributes.get(), - mData.get(), ssGroup, maker.cred()); - } - catch(...) - { - ssGroup->deleteKey(nullCred); - throw; - } - - // now finalize the access controls on the group - access->setAccess(*ssGroup, maker); - mAccess = NULL; // use them and lose them - if (autoCommit) - { - // autoCommit was on so commit now that we are done and turn - // it back on. - ObjectImpl::check(CSSM_DL_PassThrough(dldbh, - CSSM_APPLEFILEDL_COMMIT, NULL, NULL)); - CSSM_DL_PassThrough(dldbh, CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT, - reinterpret_cast(autoCommit), NULL); - } - } - catch (...) - { - if (autoCommit) - { - // autoCommit was off so rollback since we failed and turn - // autoCommit back on. - CSSM_DL_PassThrough(dldbh, CSSM_APPLEFILEDL_ROLLBACK, NULL, NULL); - CSSM_DL_PassThrough(dldbh, CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT, - reinterpret_cast(autoCommit), NULL); - } - throw; - } - } - else - { - // add the item to the (regular) db - mUniqueId = db->insert(recordType, mDbAttributes.get(), mData.get()); - } - - mPrimaryKey = keychain->makePrimaryKey(recordType, mUniqueId); - mKeychain = keychain; - - // Forget our data and attributes. - mData = NULL; - mDbAttributes.reset(NULL); - - return mPrimaryKey; -} - - - -PrimaryKey -ItemImpl::add (Keychain &keychain) -{ - return addWithCopyInfo (keychain, false); -} - - - -Item -ItemImpl::copyTo(const Keychain &keychain, Access *newAccess) -{ - StLock_(mMutex); - Item item(*this); - if (newAccess) - item->setAccess(newAccess); - else - { - /* Attempt to copy the access from the current item to the newly created one. */ - SSGroup myGroup = group(); - if (myGroup) - { - SecPointer access = new Access(*myGroup); - item->setAccess(access); - } - } - - keychain->addCopy(item); - return item; -} - -void -ItemImpl::update() -{ - StLock_(mMutex); - if (!mKeychain) - MacOSError::throwMe(errSecNoSuchKeychain); - - // Don't update if nothing changed. - if (!isModified()) - return; - - CSSM_DB_RECORDTYPE aRecordType = recordType(); - KeychainSchema schema = mKeychain->keychainSchema(); - - // Update the modification date on the item if there is a mod date attribute. - if (schema->hasAttribute(aRecordType, kSecModDateItemAttr)) - { - SInt64 date; - GetCurrentMacLongDateTime(date); - setAttribute(schema->attributeInfoFor(aRecordType, kSecModDateItemAttr), date); - } - - // Make sure that we have mUniqueId - dbUniqueRecord(); - Db db(mUniqueId->database()); - if (mDoNotEncrypt) - { - CSSM_DB_RECORD_ATTRIBUTE_DATA attrData; - memset (&attrData, 0, sizeof (attrData)); - attrData.DataRecordType = aRecordType; - - mUniqueId->modifyWithoutEncryption(aRecordType, - &attrData, - mData.get(), - CSSM_DB_MODIFY_ATTRIBUTE_REPLACE); - } - else if (useSecureStorage(db)) - { - // Add the item to the secure storage db - SSDbUniqueRecordImpl * impl = dynamic_cast(&(*mUniqueId)); - if (impl == NULL) - { - CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER); - } - - SSDbUniqueRecord ssUniqueId(impl); - - // @@@ Share this instance - const AccessCredentials *autoPrompt = globals().itemCredentials(); - - - // Only call this is user interaction is enabled. - ssUniqueId->modify(aRecordType, - mDbAttributes.get(), - mData.get(), - CSSM_DB_MODIFY_ATTRIBUTE_REPLACE, - autoPrompt); - } - else - { - mUniqueId->modify(aRecordType, - mDbAttributes.get(), - mData.get(), - CSSM_DB_MODIFY_ATTRIBUTE_REPLACE); - } - - if (!mDoNotEncrypt) - { - PrimaryKey oldPK = mPrimaryKey; - mPrimaryKey = mKeychain->makePrimaryKey(aRecordType, mUniqueId); - - // Forget our data and attributes. - mData = NULL; - mDbAttributes.reset(NULL); - - // Let the Keychain update what it needs to. - mKeychain->didUpdate(this, oldPK, mPrimaryKey); - } -} - -void -ItemImpl::getClass(SecKeychainAttribute &attr, UInt32 *actualLength) -{ - StLock_(mMutex); - if (actualLength) - *actualLength = sizeof(SecItemClass); - - if (attr.length < sizeof(SecItemClass)) - MacOSError::throwMe(errSecBufferTooSmall); - - SecItemClass aClass = Schema::itemClassFor(recordType()); - memcpy(attr.data, &aClass, sizeof(SecItemClass)); -} - -void -ItemImpl::setAttribute(SecKeychainAttribute& attr) -{ - StLock_(mMutex); - setAttribute(Schema::attributeInfo(attr.tag), CssmData(attr.data, attr.length)); -} - -CSSM_DB_RECORDTYPE -ItemImpl::recordType() -{ - StLock_(mMutex); - if (mDbAttributes.get()) - return mDbAttributes->recordType(); - - return mPrimaryKey->recordType(); -} - -const DbAttributes * -ItemImpl::modifiedAttributes() -{ - StLock_(mMutex); - return mDbAttributes.get(); -} - -const CssmData * -ItemImpl::modifiedData() -{ - StLock_(mMutex); - return mData.get(); -} - -void -ItemImpl::setData(UInt32 length,const void *data) -{ - StLock_(mMutex); - mData = new CssmDataContainer(data, length); -} - -void -ItemImpl::setAccess(Access *newAccess) -{ - StLock_(mMutex); - mAccess = newAccess; -} - -CssmClient::DbUniqueRecord -ItemImpl::dbUniqueRecord() -{ - StLock_(mMutex); - if (!isPersistent()) // is there no database attached? - { - MacOSError::throwMe(errSecNotAvailable); - } - - if (!mUniqueId) - { - DbCursor cursor(mPrimaryKey->createCursor(mKeychain)); - if (!cursor->next(NULL, NULL, mUniqueId)) - MacOSError::throwMe(errSecInvalidItemRef); - } - - return mUniqueId; -} - -PrimaryKey -ItemImpl::primaryKey() -{ - return mPrimaryKey; -} - -bool -ItemImpl::isPersistent() -{ - return mKeychain; -} - -bool -ItemImpl::isModified() -{ - StLock_(mMutex); - return mData.get() || mDbAttributes.get(); -} - -Keychain -ItemImpl::keychain() -{ - return mKeychain; -} - -bool -ItemImpl::operator < (const ItemImpl &other) -{ - if (mData && *mData) - { - // Pointer compare - return this < &other; - } - - return mPrimaryKey < other.mPrimaryKey; -} - -void -ItemImpl::setAttribute(const CssmDbAttributeInfo &info, const CssmPolyData &data) -{ - StLock_(mMutex); - if (!mDbAttributes.get()) - { - mDbAttributes.reset(new DbAttributes()); - mDbAttributes->recordType(mPrimaryKey->recordType()); - } - - size_t length = data.Length; - const void *buf = reinterpret_cast(data.Data); - uint8 timeString[16]; - - // XXX This code is duplicated in KCCursorImpl::KCCursorImpl() - // Convert a 4 or 8 byte TIME_DATE to a CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE - // style attribute value. - if (info.format() == CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE) - { - if (length == sizeof(UInt32)) - { - MacSecondsToTimeString(*reinterpret_cast(buf), 16, &timeString); - buf = &timeString; - length = 16; - } - else if (length == sizeof(SInt64)) - { - MacLongDateTimeToTimeString(*reinterpret_cast(buf), 16, &timeString); - buf = &timeString; - length = 16; - } - } - - mDbAttributes->add(info, CssmData(const_cast(buf), length)); -} - -void -ItemImpl::modifyContent(const SecKeychainAttributeList *attrList, UInt32 dataLength, const void *inData) -{ - StLock_(mMutex); - if (!mDbAttributes.get()) - { - mDbAttributes.reset(new DbAttributes()); - mDbAttributes->recordType(mPrimaryKey->recordType()); - } - - if(attrList) // optional - { - for(UInt32 ix=0; ix < attrList->count; ix++) - { - SecKeychainAttrType attrTag = attrList->attr[ix].tag; - - if (attrTag == APPLEDB_CSSM_PRINTNAME_ATTRIBUTE) - { - // must remap a caller-supplied kSecKeyPrintName attribute tag for key items, since it isn't in the schema - // (note that this will ultimately match kGenericPrintName in Schema.cpp) - attrTag = kSecLabelItemAttr; - } - - mDbAttributes->add(Schema::attributeInfo(attrTag), CssmData(attrList->attr[ix].data, attrList->attr[ix].length)); - } - } - - if(inData) - { - mData = new CssmDataContainer(inData, dataLength); - } - - update(); -} - -void -ItemImpl::getContent(SecItemClass *itemClass, SecKeychainAttributeList *attrList, UInt32 *length, void **outData) -{ - StLock_(mMutex); - // If the data hasn't been set we can't return it. - if (!mKeychain && outData) - { - CssmData *data = mData.get(); - if (!data) - MacOSError::throwMe(errSecDataNotAvailable); - } - // TODO: need to check and make sure attrs are valid and handle error condition - - - if (itemClass) - *itemClass = Schema::itemClassFor(recordType()); - - bool getDataFromDatabase = mKeychain && mPrimaryKey; - if (getDataFromDatabase) // are we attached to a database? - { - dbUniqueRecord(); - - // get the number of attributes requested by the caller - UInt32 attrCount = attrList ? attrList->count : 0; - - // make a DBAttributes structure and populate it - DbAttributes dbAttributes(mUniqueId->database(), attrCount); - for (UInt32 ix = 0; ix < attrCount; ++ix) - { - dbAttributes.add(Schema::attributeInfo(attrList->attr[ix].tag)); - } - - // request the data from the database (since we are a reference "item" and the data is really stored there) - CssmDataContainer itemData; - getContent(&dbAttributes, outData ? &itemData : NULL); - - // retrieve the data from result - for (UInt32 ix = 0; ix < attrCount; ++ix) - { - if (dbAttributes.at(ix).NumberOfValues > 0) - { - attrList->attr[ix].data = dbAttributes.at(ix).Value[0].Data; - attrList->attr[ix].length = (UInt32)dbAttributes.at(ix).Value[0].Length; - - // We don't want the data released, it is up the client - dbAttributes.at(ix).Value[0].Data = NULL; - dbAttributes.at(ix).Value[0].Length = 0; - } - else - { - attrList->attr[ix].data = NULL; - attrList->attr[ix].length = 0; - } - } - - // clean up - if (outData) - { - *outData=itemData.data(); - itemData.Data = NULL; - - if (length) - *length=(UInt32)itemData.length(); - itemData.Length = 0; - } - } - else - { - getLocalContent(attrList, length, outData); - } - - // Inform anyone interested that we are doing this -#if SENDACCESSNOTIFICATIONS - if (outData) - { - secdebug("kcnotify", "ItemImpl::getContent(%p, %p, %p, %p) retrieved content", - itemClass, attrList, length, outData); - - KCEventNotifier::PostKeychainEvent(kSecDataAccessEvent, mKeychain, this); - } -#endif -} - -void -ItemImpl::freeContent(SecKeychainAttributeList *attrList, void *data) -{ - Allocator &allocator = Allocator::standard(); // @@@ This might not match the one used originally - if (data) - allocator.free(data); - - UInt32 attrCount = attrList ? attrList->count : 0; - for (UInt32 ix = 0; ix < attrCount; ++ix) - { - allocator.free(attrList->attr[ix].data); - attrList->attr[ix].data = NULL; - } -} - -void -ItemImpl::modifyAttributesAndData(const SecKeychainAttributeList *attrList, UInt32 dataLength, const void *inData) -{ - StLock_(mMutex); - if (!mKeychain) - MacOSError::throwMe(errSecNoSuchKeychain); - - if (!mDoNotEncrypt) - { - if (!mDbAttributes.get()) - { - mDbAttributes.reset(new DbAttributes()); - mDbAttributes->recordType(mPrimaryKey->recordType()); - } - - CSSM_DB_RECORDTYPE recordType = mDbAttributes->recordType(); - UInt32 attrCount = attrList ? attrList->count : 0; - for (UInt32 ix = 0; ix < attrCount; ix++) - { - SecKeychainAttrType attrTag = attrList->attr[ix].tag; - - if (attrTag == kSecLabelItemAttr) - { - // must remap a caller-supplied label attribute tag for password items, since it isn't in the schema - // (note that this will ultimately match kGenericPrintName in Schema.cpp) - if (IS_PASSWORD_ITEM_CLASS( Schema::itemClassFor(recordType) )) - attrTag = APPLEDB_GENERIC_PRINTNAME_ATTRIBUTE; - } - - CssmDbAttributeInfo info=mKeychain->attributeInfoFor(recordType, attrTag); - - if (attrList->attr[ix].length || info.AttributeFormat==CSSM_DB_ATTRIBUTE_FORMAT_STRING || info.AttributeFormat==CSSM_DB_ATTRIBUTE_FORMAT_BLOB - || info.AttributeFormat==CSSM_DB_ATTRIBUTE_FORMAT_STRING || info.AttributeFormat==CSSM_DB_ATTRIBUTE_FORMAT_BIG_NUM - || info.AttributeFormat==CSSM_DB_ATTRIBUTE_FORMAT_MULTI_UINT32) - mDbAttributes->add(info, CssmData(attrList->attr[ix].data, attrList->attr[ix].length)); - else - mDbAttributes->add(info); - } - } - - if(inData) - { - mData = new CssmDataContainer(inData, dataLength); - } - - update(); -} - -void -ItemImpl::getAttributesAndData(SecKeychainAttributeInfo *info, SecItemClass *itemClass, - SecKeychainAttributeList **attrList, UInt32 *length, void **outData) -{ - StLock_(mMutex); - // If the data hasn't been set we can't return it. - if (!mKeychain && outData) - { - CssmData *data = mData.get(); - if (!data) - MacOSError::throwMe(errSecDataNotAvailable); - } - // TODO: need to check and make sure attrs are valid and handle error condition - - SecItemClass myItemClass = Schema::itemClassFor(recordType()); - if (itemClass) - *itemClass = myItemClass; - - // @@@ This call won't work for floating items (like certificates). - dbUniqueRecord(); - - UInt32 attrCount = info ? info->count : 0; - DbAttributes dbAttributes(mUniqueId->database(), attrCount); - for (UInt32 ix = 0; ix < attrCount; ix++) - { - CssmDbAttributeData &record = dbAttributes.add(); - record.Info.AttributeNameFormat=CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER; - record.Info.Label.AttributeID=info->tag[ix]; - - if (record.Info.Label.AttributeID == kSecLabelItemAttr) - { - // must remap a caller-supplied label attribute tag for password items, since it isn't in the schema - if (IS_PASSWORD_ITEM_CLASS( myItemClass )) - record.Info.Label.AttributeID = APPLEDB_GENERIC_PRINTNAME_ATTRIBUTE; - } - } - - CssmDataContainer itemData; - getContent(&dbAttributes, outData ? &itemData : NULL); - - if (info && attrList) - { - SecKeychainAttributeList *theList=reinterpret_cast(malloc(sizeof(SecKeychainAttributeList))); - SecKeychainAttribute *attr=reinterpret_cast(malloc(sizeof(SecKeychainAttribute)*attrCount)); - theList->count=attrCount; - theList->attr=attr; - - for (UInt32 ix = 0; ix < attrCount; ++ix) - { - attr[ix].tag=info->tag[ix]; - - if (dbAttributes.at(ix).NumberOfValues > 0) - { - attr[ix].data = dbAttributes.at(ix).Value[0].Data; - attr[ix].length = (UInt32)dbAttributes.at(ix).Value[0].Length; - - // We don't want the data released, it is up the client - dbAttributes.at(ix).Value[0].Data = NULL; - dbAttributes.at(ix).Value[0].Length = 0; - } - else - { - attr[ix].data = NULL; - attr[ix].length = 0; - } - } - *attrList=theList; - } - - if (outData) - { - *outData=itemData.data(); - itemData.Data=NULL; - - if (length) *length=(UInt32)itemData.length(); - itemData.Length=0; - -#if SENDACCESSNOTIFICATIONS - secdebug("kcnotify", "ItemImpl::getAttributesAndData(%p, %p, %p, %p, %p) retrieved data", - info, itemClass, attrList, length, outData); - - KCEventNotifier::PostKeychainEvent(kSecDataAccessEvent, mKeychain, this); -#endif - } - -} - -void -ItemImpl::freeAttributesAndData(SecKeychainAttributeList *attrList, void *data) -{ - Allocator &allocator = Allocator::standard(); // @@@ This might not match the one used originally - - if (data) - allocator.free(data); - - if (attrList) - { - for (UInt32 ix = 0; ix < attrList->count; ++ix) - { - allocator.free(attrList->attr[ix].data); - } - free(attrList->attr); - free(attrList); - } -} - -void -ItemImpl::getAttribute(SecKeychainAttribute& attr, UInt32 *actualLength) -{ - StLock_(mMutex); - if (attr.tag == kSecClassItemAttr) - return getClass(attr, actualLength); - - if (mDbAttributes.get()) - { - CssmDbAttributeData *data = mDbAttributes->find(Schema::attributeInfo(attr.tag)); - if (data) - { - getAttributeFrom(data, attr, actualLength); - return; - } - } - - if (!mKeychain) - MacOSError::throwMe(errSecNoSuchAttr); - - dbUniqueRecord(); - DbAttributes dbAttributes(mUniqueId->database(), 1); - dbAttributes.add(Schema::attributeInfo(attr.tag)); - mUniqueId->get(&dbAttributes, NULL); - getAttributeFrom(&dbAttributes.at(0), attr, actualLength); -} - -void -ItemImpl::getAttributeFrom(CssmDbAttributeData *data, SecKeychainAttribute &attr, UInt32 *actualLength) -{ - StLock_(mMutex); - static const uint32 zero = 0; - UInt32 length; - const void *buf = NULL; - - // Temporary storage for buf. - sint64 macLDT; - uint32 macSeconds; - sint16 svalue16; - uint16 uvalue16; - sint8 svalue8; - uint8 uvalue8; - - if (!data) - length = 0; - else if (data->size() < 1) // Attribute has no values. - { - if (data->format() == CSSM_DB_ATTRIBUTE_FORMAT_SINT32 - || data->format() == CSSM_DB_ATTRIBUTE_FORMAT_UINT32) - { - length = sizeof(zero); - buf = &zero; - } - else if (CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE) - length = 0; // Should we throw here? - else // All other formats - length = 0; - } - else // Get the first value - { - length = (UInt32)data->Value[0].Length; - buf = data->Value[0].Data; - - if (data->format() == CSSM_DB_ATTRIBUTE_FORMAT_SINT32) - { - if (attr.length == sizeof(sint8)) - { - length = attr.length; - svalue8 = sint8(*reinterpret_cast(buf)); - buf = &svalue8; - } - else if (attr.length == sizeof(sint16)) - { - length = attr.length; - svalue16 = sint16(*reinterpret_cast(buf)); - buf = &svalue16; - } - } - else if (data->format() == CSSM_DB_ATTRIBUTE_FORMAT_UINT32) - { - if (attr.length == sizeof(uint8)) - { - length = attr.length; - uvalue8 = uint8(*reinterpret_cast(buf)); - buf = &uvalue8; - } - else if (attr.length == sizeof(uint16)) - { - length = attr.length; - uvalue16 = uint16(*reinterpret_cast(buf)); - buf = &uvalue16; - } - } - else if (data->format() == CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE) - { - if (attr.length == sizeof(uint32)) - { - TimeStringToMacSeconds(data->Value[0], macSeconds); - buf = &macSeconds; - length = attr.length; - } - else if (attr.length == sizeof(sint64)) - { - TimeStringToMacLongDateTime(data->Value[0], macLDT); - buf = &macLDT; - length = attr.length; - } - } - } - - if (actualLength) - *actualLength = length; - - if (length) - { - if (attr.length < length) - MacOSError::throwMe(errSecBufferTooSmall); - - memcpy(attr.data, buf, length); - } -} - -void -ItemImpl::getData(CssmDataContainer& outData) -{ - StLock_(mMutex); - if (!mKeychain) - { - CssmData *data = mData.get(); - // If the data hasn't been set we can't return it. - if (!data) - MacOSError::throwMe(errSecDataNotAvailable); - - outData = *data; - return; - } - - getContent(NULL, &outData); - -#if SENDACCESSNOTIFICATIONS - secdebug("kcnotify", "ItemImpl::getData retrieved data"); - - //%%% be done elsewhere, but here is good for now - KCEventNotifier::PostKeychainEvent(kSecDataAccessEvent, mKeychain, this); -#endif -} - -SSGroup -ItemImpl::group() -{ - StLock_(mMutex); - SSGroup group; - if (!!mUniqueId) - { - Db db(mKeychain->database()); - if (useSecureStorage(db)) - { - group = safer_cast(*mUniqueId).group(); - } - } - - return group; -} - -void ItemImpl::getLocalContent(SecKeychainAttributeList *attributeList, UInt32 *outLength, void **outData) -{ - StLock_(mMutex); - willRead(); - Allocator &allocator = Allocator::standard(); // @@@ This might not match the one used originally - if (outData) - { - CssmData *data = mData.get(); - if (!data) - MacOSError::throwMe(errSecDataNotAvailable); - - // Copy the data out of our internal cached copy. - UInt32 length = (UInt32)data->Length; - *outData = allocator.malloc(length); - memcpy(*outData, data->Data, length); - if (outLength) - *outLength = length; - } - - if (attributeList) - { - if (!mDbAttributes.get()) - MacOSError::throwMe(errSecDataNotAvailable); - - // Pull attributes out of a "floating" item, i.e. one that isn't attached to a database - for (UInt32 ix = 0; ix < attributeList->count; ++ix) - { - SecKeychainAttribute &attribute = attributeList->attr[ix]; - CssmDbAttributeData *data = mDbAttributes->find(Schema::attributeInfo(attribute.tag)); - if (data && data->NumberOfValues > 0) - { - // Copy the data out of our internal cached copy. - UInt32 length = (UInt32)data->Value[0].Length; - attribute.data = allocator.malloc(length); - memcpy(attribute.data, data->Value[0].Data, length); - attribute.length = length; - } - else - { - attribute.length = 0; - attribute.data = NULL; - } - } - } -} - -void -ItemImpl::getContent(DbAttributes *dbAttributes, CssmDataContainer *itemData) -{ - StLock_(mMutex); - // Make sure mUniqueId is set. - dbUniqueRecord(); - if (itemData) - { - Db db(mUniqueId->database()); - if (mDoNotEncrypt) - { - mUniqueId->getWithoutEncryption (dbAttributes, itemData); - return; - } - if (useSecureStorage(db)) - { - SSDbUniqueRecordImpl* impl = dynamic_cast(&(*mUniqueId)); - if (impl == NULL) - { - CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER); - } - - SSDbUniqueRecord ssUniqueId(impl); - const AccessCredentials *autoPrompt = globals().itemCredentials(); - ssUniqueId->get(dbAttributes, itemData, autoPrompt); - return; - } - } - - mUniqueId->get(dbAttributes, itemData); -} - -bool -ItemImpl::useSecureStorage(const Db &db) -{ - StLock_(mMutex); - switch (recordType()) - { - case CSSM_DL_DB_RECORD_GENERIC_PASSWORD: - case CSSM_DL_DB_RECORD_INTERNET_PASSWORD: - case CSSM_DL_DB_RECORD_APPLESHARE_PASSWORD: - if (db->dl()->subserviceMask() & CSSM_SERVICE_CSP) - return true; - break; - default: - break; - } - return false; -} - -void ItemImpl::willRead() -{ -} - -Item ItemImpl::makeFromPersistentReference(const CFDataRef persistentRef, bool *isIdentityRef) -{ - CssmData dictData((void*)::CFDataGetBytePtr(persistentRef), ::CFDataGetLength(persistentRef)); - NameValueDictionary dict(dictData); - - Keychain keychain; - Item item = (ItemImpl *) NULL; - - if (isIdentityRef) { - *isIdentityRef = (dict.FindByName(IDENTITY_KEY) != 0) ? true : false; - } - - // make sure we have a database identifier - if (dict.FindByName(SSUID_KEY) != 0) - { - DLDbIdentifier dlDbIdentifier = NameValueDictionary::MakeDLDbIdentifierFromNameValueDictionary(dict); - DLDbIdentifier newDlDbIdentifier(dlDbIdentifier.ssuid(), - DLDbListCFPref::ExpandTildesInPath(dlDbIdentifier.dbName()).c_str(), - dlDbIdentifier.dbLocation()); - - keychain = globals().storageManager.keychain(newDlDbIdentifier); - - const NameValuePair* aDictItem = dict.FindByName(ITEM_KEY); - if (aDictItem && keychain) - { - PrimaryKey primaryKey(aDictItem->Value()); - item = keychain->item(primaryKey); - } - } - KCThrowIf_( !item, errSecItemNotFound ); - return item; -} - -void ItemImpl::copyPersistentReference(CFDataRef &outDataRef, bool isSecIdentityRef) -{ - if (secd_PersistentRef) { - outDataRef = secd_PersistentRef; - return; - } - StLock_(mMutex); - // item must be in a keychain and have a primary key to be persistent - if (!mKeychain || !mPrimaryKey) { - MacOSError::throwMe(errSecItemNotFound); - } - DLDbIdentifier dlDbIdentifier = mKeychain->dlDbIdentifier(); - DLDbIdentifier newDlDbIdentifier(dlDbIdentifier.ssuid(), - DLDbListCFPref::AbbreviatedPath(mKeychain->name()).c_str(), - dlDbIdentifier.dbLocation()); - NameValueDictionary dict; - NameValueDictionary::MakeNameValueDictionaryFromDLDbIdentifier(newDlDbIdentifier, dict); - - CssmData* pKey = mPrimaryKey; - dict.Insert (new NameValuePair(ITEM_KEY, *pKey)); - - if (isSecIdentityRef) { - uint32_t value = -1; - CssmData valueData((void*)&value, sizeof(value)); - dict.Insert (new NameValuePair(IDENTITY_KEY, valueData)); - } - - // flatten the NameValueDictionary - CssmData dictData; - dict.Export(dictData); - outDataRef = ::CFDataCreate(kCFAllocatorDefault, dictData.Data, dictData.Length); - free (dictData.Data); -} - -void ItemImpl::copyRecordIdentifier(CSSM_DATA &data) -{ - StLock_(mMutex); - CssmClient::DbUniqueRecord uniqueRecord = dbUniqueRecord (); - uniqueRecord->getRecordIdentifier(data); -} - -/* - * Obtain blob used to bind a keychain item to an Extended Attribute record. - * We just use the PrimaryKey blob as the default. Note that for standard Items, - * this can cause the loss of extended attribute bindings if a Primary Key - * attribute changes. - */ -const CssmData &ItemImpl::itemID() -{ - StLock_(mMutex); - if(mPrimaryKey->length() == 0) { - /* not in a keychain; we don't have a primary key */ - MacOSError::throwMe(errSecNoSuchAttr); - } - return *mPrimaryKey; -} - -bool ItemImpl::equal(SecCFObject &other) -{ - // First check to see if both items have a primary key and - // if the primary key is the same. If so then these - // items must be equal - ItemImpl& other_item = (ItemImpl&)other; - if (mPrimaryKey != NULL && mPrimaryKey == other_item.mPrimaryKey) - { - return true; - } - - // The primary keys do not match so do a CFHash of the - // data of the item and compare those for equality - CFHashCode this_hash = hash(); - CFHashCode other_hash = other.hash(); - return (this_hash == other_hash); -} - -CFHashCode ItemImpl::hash() -{ - CFHashCode result = SecCFObject::hash(); - - StLock_(mMutex); - RefPointer data_to_hash; - - // Use the item data for the hash - if (mData && *mData) - { - data_to_hash = mData; - } - - // If there is no primary key AND not data ???? - // just return the 'old' hash value which is the - // object pointer. - if (NULL != data_to_hash.get()) - { - CFDataRef temp_data = NULL; - unsigned char digest[CC_SHA256_DIGEST_LENGTH]; - - if (data_to_hash->length() < 80) - { - // If it is less than 80 bytes then CFData can be used - temp_data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, - (const UInt8 *)data_to_hash->data(), data_to_hash->length(), kCFAllocatorNull); - - } - // CFData truncates its hash value to 80 bytes. ???? - // In order to do the 'right thing' a SHA 256 hash will be used to - // include all of the data - else - { - memset(digest, 0, CC_SHA256_DIGEST_LENGTH); - - CC_SHA256((const void *)data_to_hash->data(), (CC_LONG)data_to_hash->length(), digest); - - temp_data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, - (const UInt8 *)digest, CC_SHA256_DIGEST_LENGTH, kCFAllocatorNull); - } - - if (NULL != temp_data) - { - result = CFHash(temp_data); - CFRelease(temp_data); - } - - } - - return result; -} - - -void ItemImpl::postItemEvent(SecKeychainEvent theEvent) -{ - mKeychain->postEvent(theEvent, this); -} - - - -// -// Item -- This class is here to magically create the right subclass of ItemImpl -// when constructing new items. -// -Item::Item() -{ -} - -Item::Item(ItemImpl *impl) : SecPointer(impl) -{ -} - -Item::Item(SecItemClass itemClass, OSType itemCreator, UInt32 length, const void* data, bool inhibitCheck) -{ - if (!inhibitCheck) - { - if (itemClass == CSSM_DL_DB_RECORD_X509_CERTIFICATE - || itemClass == CSSM_DL_DB_RECORD_PUBLIC_KEY - || itemClass == CSSM_DL_DB_RECORD_PRIVATE_KEY - || itemClass == CSSM_DL_DB_RECORD_SYMMETRIC_KEY) - MacOSError::throwMe(errSecNoSuchClass); /* @@@ errSecInvalidClass */ - } - - *this = new ItemImpl(itemClass, itemCreator, length, data, inhibitCheck); -} - -Item::Item(SecItemClass itemClass, SecKeychainAttributeList *attrList, UInt32 length, const void* data) -{ - *this = new ItemImpl(itemClass, attrList, length, data); -} - -Item::Item(const Keychain &keychain, const PrimaryKey &primaryKey, const CssmClient::DbUniqueRecord &uniqueId) - : SecPointer( - primaryKey->recordType() == CSSM_DL_DB_RECORD_X509_CERTIFICATE - ? Certificate::make(keychain, primaryKey, uniqueId) - : (primaryKey->recordType() == CSSM_DL_DB_RECORD_PUBLIC_KEY - || primaryKey->recordType() == CSSM_DL_DB_RECORD_PRIVATE_KEY - || primaryKey->recordType() == CSSM_DL_DB_RECORD_SYMMETRIC_KEY) - ? KeyItem::make(keychain, primaryKey, uniqueId) - : primaryKey->recordType() == CSSM_DL_DB_RECORD_EXTENDED_ATTRIBUTE - ? ExtendedAttribute::make(keychain, primaryKey, uniqueId) - : ItemImpl::make(keychain, primaryKey, uniqueId)) -{ -} - -Item::Item(const Keychain &keychain, const PrimaryKey &primaryKey) - : SecPointer( - primaryKey->recordType() == CSSM_DL_DB_RECORD_X509_CERTIFICATE - ? Certificate::make(keychain, primaryKey) - : (primaryKey->recordType() == CSSM_DL_DB_RECORD_PUBLIC_KEY - || primaryKey->recordType() == CSSM_DL_DB_RECORD_PRIVATE_KEY - || primaryKey->recordType() == CSSM_DL_DB_RECORD_SYMMETRIC_KEY) - ? KeyItem::make(keychain, primaryKey) - : primaryKey->recordType() == CSSM_DL_DB_RECORD_EXTENDED_ATTRIBUTE - ? ExtendedAttribute::make(keychain, primaryKey) - : ItemImpl::make(keychain, primaryKey)) -{ -} - -Item::Item(ItemImpl &item) - : SecPointer( - item.recordType() == CSSM_DL_DB_RECORD_X509_CERTIFICATE - ? new Certificate(safer_cast(item)) - : (item.recordType() == CSSM_DL_DB_RECORD_PUBLIC_KEY - || item.recordType() == CSSM_DL_DB_RECORD_PRIVATE_KEY - || item.recordType() == CSSM_DL_DB_RECORD_SYMMETRIC_KEY) - ? new KeyItem(safer_cast(item)) - : item.recordType() == CSSM_DL_DB_RECORD_EXTENDED_ATTRIBUTE - ? new ExtendedAttribute(safer_cast(item)) - : new ItemImpl(item)) -{ -} - -CFIndex KeychainCore::GetItemRetainCount(Item& item) -{ - return CFGetRetainCount(item->handle(false)); -} - -void ItemImpl::setPersistentRef(CFDataRef ref) -{ - if (secd_PersistentRef) { - CFRelease(secd_PersistentRef); - } - secd_PersistentRef = ref; - CFRetain(ref); -} - -CFDataRef ItemImpl::getPersistentRef() -{ - return secd_PersistentRef; -} - - - -bool ItemImpl::mayDelete() -{ - ObjectImpl* uniqueIDImpl = mUniqueId.get(); - - if (uniqueIDImpl != NULL) - { - bool result = mUniqueId->isIdle(); - return result; - } - else - { - return true; - } -}