--- /dev/null
+/*
+ * 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 <security_cdsa_utilities/Schema.h>
+#include "KCEventNotifier.h"
+#include "KCExceptions.h"
+#include "cssmdatetime.h"
+#include <security_cdsa_client/keychainacl.h>
+#include <security_utilities/osxcode.h>
+#include <security_utilities/trackingallocator.h>
+#include <Security/SecKeychainItemPriv.h>
+#include <Security/cssmapple.h>
+#include <CommonCrypto/CommonDigest.h>
+
+#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<Mutex>_(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<Mutex>_(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<CssmData>(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<CssmDbAttributeInfo> 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<SSDbImpl *>(&(*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> 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<SecPointer<ACL> > 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<void **>(&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<void **>(&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<const void *>(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<const void *>(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<Mutex>_(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> access = new Access(*myGroup);
+ item->setAccess(access);
+ }
+ }
+
+ keychain->addCopy(item);
+ return item;
+}
+
+void
+ItemImpl::update()
+{
+ StLock<Mutex>_(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<SSDbUniqueRecordImpl *>(&(*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<Mutex>_(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<Mutex>_(mMutex);
+ setAttribute(Schema::attributeInfo(attr.tag), CssmData(attr.data, attr.length));
+}
+
+CSSM_DB_RECORDTYPE
+ItemImpl::recordType()
+{
+ StLock<Mutex>_(mMutex);
+ if (mDbAttributes.get())
+ return mDbAttributes->recordType();
+
+ return mPrimaryKey->recordType();
+}
+
+const DbAttributes *
+ItemImpl::modifiedAttributes()
+{
+ StLock<Mutex>_(mMutex);
+ return mDbAttributes.get();
+}
+
+const CssmData *
+ItemImpl::modifiedData()
+{
+ StLock<Mutex>_(mMutex);
+ return mData.get();
+}
+
+void
+ItemImpl::setData(UInt32 length,const void *data)
+{
+ StLock<Mutex>_(mMutex);
+ mData = new CssmDataContainer(data, length);
+}
+
+void
+ItemImpl::setAccess(Access *newAccess)
+{
+ StLock<Mutex>_(mMutex);
+ mAccess = newAccess;
+}
+
+CssmClient::DbUniqueRecord
+ItemImpl::dbUniqueRecord()
+{
+ StLock<Mutex>_(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<Mutex>_(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<Mutex>_(mMutex);
+ if (!mDbAttributes.get())
+ {
+ mDbAttributes.reset(new DbAttributes());
+ mDbAttributes->recordType(mPrimaryKey->recordType());
+ }
+
+ size_t length = data.Length;
+ const void *buf = reinterpret_cast<const void *>(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<const UInt32 *>(buf), 16, &timeString);
+ buf = &timeString;
+ length = 16;
+ }
+ else if (length == sizeof(SInt64))
+ {
+ MacLongDateTimeToTimeString(*reinterpret_cast<const SInt64 *>(buf), 16, &timeString);
+ buf = &timeString;
+ length = 16;
+ }
+ }
+
+ mDbAttributes->add(info, CssmData(const_cast<void*>(buf), length));
+}
+
+void
+ItemImpl::modifyContent(const SecKeychainAttributeList *attrList, UInt32 dataLength, const void *inData)
+{
+ StLock<Mutex>_(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<Mutex>_(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<Mutex>_(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<Mutex>_(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<SecKeychainAttributeList *>(malloc(sizeof(SecKeychainAttributeList)));
+ SecKeychainAttribute *attr=reinterpret_cast<SecKeychainAttribute *>(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<Mutex>_(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<Mutex>_(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<const sint32 *>(buf));
+ buf = &svalue8;
+ }
+ else if (attr.length == sizeof(sint16))
+ {
+ length = attr.length;
+ svalue16 = sint16(*reinterpret_cast<const sint32 *>(buf));
+ buf = &svalue16;
+ }
+ }
+ else if (data->format() == CSSM_DB_ATTRIBUTE_FORMAT_UINT32)
+ {
+ if (attr.length == sizeof(uint8))
+ {
+ length = attr.length;
+ uvalue8 = uint8(*reinterpret_cast<const uint32 *>(buf));
+ buf = &uvalue8;
+ }
+ else if (attr.length == sizeof(uint16))
+ {
+ length = attr.length;
+ uvalue16 = uint16(*reinterpret_cast<const uint32 *>(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<Mutex>_(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");
+
+ //%%%<might> be done elsewhere, but here is good for now
+ KCEventNotifier::PostKeychainEvent(kSecDataAccessEvent, mKeychain, this);
+#endif
+}
+
+SSGroup
+ItemImpl::group()
+{
+ StLock<Mutex>_(mMutex);
+ SSGroup group;
+ if (!!mUniqueId)
+ {
+ Db db(mKeychain->database());
+ if (useSecureStorage(db))
+ {
+ group = safer_cast<SSDbUniqueRecordImpl &>(*mUniqueId).group();
+ }
+ }
+
+ return group;
+}
+
+void ItemImpl::getLocalContent(SecKeychainAttributeList *attributeList, UInt32 *outLength, void **outData)
+{
+ StLock<Mutex>_(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<Mutex>_(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<SSDbUniqueRecordImpl *>(&(*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<Mutex>_(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<Mutex>_(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<Mutex>_(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<Mutex>_(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<Mutex>_(mMutex);
+ RefPointer<CssmDataContainer> 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<ItemImpl>(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<ItemImpl>(
+ 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<ItemImpl>(
+ 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<ItemImpl>(
+ item.recordType() == CSSM_DL_DB_RECORD_X509_CERTIFICATE
+ ? new Certificate(safer_cast<Certificate &>(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<KeyItem &>(item))
+ : item.recordType() == CSSM_DL_DB_RECORD_EXTENDED_ATTRIBUTE
+ ? new ExtendedAttribute(safer_cast<ExtendedAttribute &>(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;
+ }
+}