]> git.saurik.com Git - apple/security.git/blame - libsecurity_keychain/lib/Item.cpp
Security-55471.14.8.tar.gz
[apple/security.git] / libsecurity_keychain / lib / Item.cpp
CommitLineData
b1ab9ed8 1/*
427c49bc
A
2 * Copyright (c) 2000-2004,2012-2013 Apple Inc. All Rights Reserved.
3 *
b1ab9ed8 4 * @APPLE_LICENSE_HEADER_START@
427c49bc 5 *
b1ab9ed8
A
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.
427c49bc 12 *
b1ab9ed8
A
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.
427c49bc 20 *
b1ab9ed8
A
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"
427c49bc 38#include "KCExceptions.h"
b1ab9ed8
A
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>
427c49bc 45#include <CommonCrypto/CommonDigest.h>
b1ab9ed8
A
46
47#define SENDACCESSNOTIFICATIONS 1
48
49//%%% schema indexes should be defined in Schema.h
427c49bc 50#define _kSecAppleSharePasswordItemClass 'ashp'
b1ab9ed8
A
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 || \
427c49bc 55 (X) == _kSecAppleSharePasswordItemClass ) ? 1 : 0
b1ab9ed8
A
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),
427c49bc 68 secd_PersistentRef(NULL),
b1ab9ed8
A
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),
427c49bc 85 secd_PersistentRef(NULL),
b1ab9ed8
A
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
427c49bc 96 if(attrList)
b1ab9ed8
A
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),
427c49bc 108 secd_PersistentRef(NULL), mDoNotEncrypt(false), mInCache(false),
b1ab9ed8
A
109 mMutex(Mutex::recursive)
110{
111}
112
113// PrimaryKey ItemImpl constructor
114ItemImpl::ItemImpl(const Keychain &keychain, const PrimaryKey &primaryKey)
427c49bc 115: mKeychain(keychain), mPrimaryKey(primaryKey), secd_PersistentRef(NULL), mDoNotEncrypt(false),
b1ab9ed8
A
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
427c49bc 138
b1ab9ed8
A
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),
427c49bc 145 secd_PersistentRef(NULL),
b1ab9ed8
A
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.
427c49bc 156
b1ab9ed8
A
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 }
427c49bc 162
b1ab9ed8
A
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.
427c49bc 174
b1ab9ed8
A
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{
427c49bc
A
183 if (secd_PersistentRef) {
184 CFRelease(secd_PersistentRef);
185 }
b1ab9ed8
A
186}
187
188
189
190Mutex*
191ItemImpl::getMutexForObject()
192{
193 if (mKeychain.get())
194 {
195 return mKeychain->getKeychainMutex();
196 }
427c49bc 197
b1ab9ed8
A
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;
427c49bc 239
b1ab9ed8
A
240 case CSSM_DB_ATTRIBUTE_FORMAT_REAL:
241 return defaultEightBytes;
427c49bc 242
b1ab9ed8
A
243 case CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE:
244 return defaultTime;
427c49bc 245
b1ab9ed8
A
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 }
427c49bc 284
b1ab9ed8
A
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 }
427c49bc 341
b1ab9ed8
A
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 }
427c49bc 355
b1ab9ed8
A
356 SSDb ssDb(impl);
357
358 TrackingAllocator allocator(Allocator::standard());
427c49bc 359
b1ab9ed8
A
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);
427c49bc 370
b1ab9ed8
A
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);
427c49bc 404
b1ab9ed8
A
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)
427c49bc
A
496 MacOSError::throwMe(errSecNoSuchKeychain);
497
b1ab9ed8
A
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;
427c49bc 521
b1ab9ed8
A
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 }
427c49bc 535
b1ab9ed8
A
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
427c49bc 696 size_t length = data.Length;
b1ab9ed8
A
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 }
427c49bc 748
b1ab9ed8
A
749 if(inData)
750 {
751 mData = new CssmDataContainer(inData, dataLength);
752 }
427c49bc 753
b1ab9ed8
A
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());
427c49bc 773
b1ab9ed8
A
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;
427c49bc 781
b1ab9ed8
A
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 }
427c49bc 788
b1ab9ed8
A
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 {
427c49bc
A
798 attrList->attr[ix].data = dbAttributes.at(ix).Value[0].Data;
799 attrList->attr[ix].length = (UInt32)dbAttributes.at(ix).Value[0].Length;
800
b1ab9ed8
A
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 {
427c49bc 807 attrList->attr[ix].data = NULL;
b1ab9ed8
A
808 attrList->attr[ix].length = 0;
809 }
810 }
811
812 // clean up
813 if (outData)
814 {
815 *outData=itemData.data();
816 itemData.Data = NULL;
427c49bc 817
b1ab9ed8 818 if (length)
427c49bc 819 *length=(UInt32)itemData.length();
b1ab9ed8
A
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);
427c49bc 885
b1ab9ed8
A
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 }
427c49bc 894
b1ab9ed8
A
895 if(inData)
896 {
897 mData = new CssmDataContainer(inData, dataLength);
898 }
427c49bc 899
b1ab9ed8
A
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];
427c49bc 931
b1ab9ed8
A
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;
427c49bc 949
b1ab9ed8
A
950 for (UInt32 ix = 0; ix < attrCount; ++ix)
951 {
952 attr[ix].tag=info->tag[ix];
427c49bc 953
b1ab9ed8
A
954 if (dbAttributes.at(ix).NumberOfValues > 0)
955 {
427c49bc
A
956 attr[ix].data = dbAttributes.at(ix).Value[0].Data;
957 attr[ix].length = (UInt32)dbAttributes.at(ix).Value[0].Length;
958
b1ab9ed8
A
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 {
427c49bc 965 attr[ix].data = NULL;
b1ab9ed8
A
966 attr[ix].length = 0;
967 }
968 }
969 *attrList=theList;
970 }
971
972 if (outData)
973 {
974 *outData=itemData.data();
975 itemData.Data=NULL;
427c49bc
A
976
977 if (length) *length=(UInt32)itemData.length();
b1ab9ed8 978 itemData.Length=0;
427c49bc 979
b1ab9ed8
A
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 }
427c49bc 987
b1ab9ed8
A
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);
427c49bc 1028
b1ab9ed8
A
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;
427c49bc 1041 UInt32 length;
b1ab9ed8
A
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 {
427c49bc 1069 length = (UInt32)data->Value[0].Length;
b1ab9ed8
A
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.
427c49bc 1185 UInt32 length = (UInt32)data->Length;
b1ab9ed8
A
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.
427c49bc 1205 UInt32 length = (UInt32)data->Value[0].Length;
b1ab9ed8
A
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 }
427c49bc 1240
b1ab9ed8
A
1241 SSDbUniqueRecord ssUniqueId(impl);
1242 const AccessCredentials *autoPrompt = globals().itemCredentials();
1243 ssUniqueId->get(dbAttributes, itemData, autoPrompt);
1244 return;
1245 }
1246 }
1247
427c49bc 1248 mUniqueId->get(dbAttributes, itemData);
b1ab9ed8
A
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
427c49bc
A
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}
b1ab9ed8 1305
427c49bc 1306void ItemImpl::copyPersistentReference(CFDataRef &outDataRef, bool isSecIdentityRef)
b1ab9ed8 1307{
427c49bc
A
1308 if (secd_PersistentRef) {
1309 outDataRef = secd_PersistentRef;
1310 return;
1311 }
b1ab9ed8
A
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
427c49bc
A
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
b1ab9ed8
A
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
427c49bc 1351 * attribute changes.
b1ab9ed8
A
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
427c49bc
A
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
b1ab9ed8
A
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 }
427c49bc 1463
b1ab9ed8
A
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
427c49bc 1495 ? ExtendedAttribute::make(keychain, primaryKey)
b1ab9ed8
A
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))
427c49bc 1510 : new ItemImpl(item))
b1ab9ed8
A
1511{
1512}
1513
427c49bc 1514CFIndex KeychainCore::GetItemRetainCount(Item& item)
b1ab9ed8
A
1515{
1516 return CFGetRetainCount(item->handle(false));
1517}
1518
427c49bc
A
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}