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