2 * Copyright (c) 2000-2004,2011-2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
31 #include "Certificate.h"
33 #include "ExtendedAttribute.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 #include <utilities/der_plist.h>
48 #include <security_utilities/CSPDLTransaction.h>
49 #include <SecBasePriv.h>
51 #define SENDACCESSNOTIFICATIONS 1
53 //%%% schema indexes should be defined in Schema.h
54 #define _kSecAppleSharePasswordItemClass 'ashp'
55 #define APPLEDB_CSSM_PRINTNAME_ATTRIBUTE 1 /* schema index for label attribute of keys or certificates */
56 #define APPLEDB_GENERIC_PRINTNAME_ATTRIBUTE 7 /* schema index for label attribute of password items */
57 #define IS_PASSWORD_ITEM_CLASS(X) ( (X) == kSecInternetPasswordItemClass || \
58 (X) == kSecGenericPasswordItemClass || \
59 (X) == _kSecAppleSharePasswordItemClass ) ? 1 : 0
61 using namespace KeychainCore
;
62 using namespace CSSMDateTimeUtils
;
68 ItemImpl
*ItemImpl::required(SecKeychainItemRef ptr
)
71 if (ItemImpl
*pp
= optional(ptr
)) {
75 MacOSError::throwMe(errSecInvalidItemRef
);
78 ItemImpl
*ItemImpl::optional(SecKeychainItemRef ptr
)
80 if (ptr
!= NULL
&& CFGetTypeID(ptr
) == SecKeyGetTypeID()) {
81 return dynamic_cast<ItemImpl
*>(KeyItem::fromSecKeyRef(ptr
));
82 } else if (SecCFObject
*p
= SecCFObject::optional(ptr
)) {
83 if (ItemImpl
*pp
= dynamic_cast<ItemImpl
*>(p
)) {
86 MacOSError::throwMe(errSecInvalidItemRef
);
93 // NewItemImpl constructor
94 ItemImpl::ItemImpl(SecItemClass itemClass
, OSType itemCreator
, UInt32 length
, const void* data
, bool dontDoAttributes
)
95 : mDbAttributes(new DbAttributes()),
97 secd_PersistentRef(NULL
),
100 mMutex(Mutex::recursive
)
103 mData
= new CssmDataContainer(data
, length
);
105 mDbAttributes
->recordType(Schema::recordTypeFor(itemClass
));
108 mDbAttributes
->add(Schema::attributeInfo(kSecCreatorItemAttr
), itemCreator
);
111 ItemImpl::ItemImpl(SecItemClass itemClass
, SecKeychainAttributeList
*attrList
, UInt32 length
, const void* data
)
112 : mDbAttributes(new DbAttributes()),
114 secd_PersistentRef(NULL
),
115 mDoNotEncrypt(false),
117 mMutex(Mutex::recursive
)
120 mData
= new CssmDataContainer(data
, length
);
123 mDbAttributes
->recordType(Schema::recordTypeFor(itemClass
));
127 for(UInt32 i
=0; i
< attrList
->count
; i
++)
129 mDbAttributes
->add(Schema::attributeInfo(attrList
->attr
[i
].tag
), CssmData(attrList
->attr
[i
].data
, attrList
->attr
[i
].length
));
134 // DbItemImpl constructor
135 ItemImpl::ItemImpl(const Keychain
&keychain
, const PrimaryKey
&primaryKey
, const DbUniqueRecord
&uniqueId
)
136 : mUniqueId(uniqueId
), mKeychain(keychain
), mPrimaryKey(primaryKey
),
137 secd_PersistentRef(NULL
), mDoNotEncrypt(false), mInCache(false),
138 mMutex(Mutex::recursive
)
142 // PrimaryKey ItemImpl constructor
143 ItemImpl::ItemImpl(const Keychain
&keychain
, const PrimaryKey
&primaryKey
)
144 : mKeychain(keychain
), mPrimaryKey(primaryKey
), secd_PersistentRef(NULL
), mDoNotEncrypt(false),
146 mMutex(Mutex::recursive
)
150 ItemImpl
* ItemImpl::make(const Keychain
&keychain
, const PrimaryKey
&primaryKey
, const CssmClient::DbUniqueRecord
&uniqueId
)
152 ItemImpl
* ii
= new ItemImpl(keychain
, primaryKey
, uniqueId
);
153 keychain
->addItem(primaryKey
, ii
);
159 ItemImpl
* ItemImpl::make(const Keychain
&keychain
, const PrimaryKey
&primaryKey
)
161 ItemImpl
* ii
= new ItemImpl(keychain
, primaryKey
);
162 keychain
->addItem(primaryKey
, ii
);
168 // Constructor used when copying an item to a keychain.
170 ItemImpl::ItemImpl(ItemImpl
&item
) :
171 mData(item
.modifiedData() ? NULL
: new CssmDataContainer()),
172 mDbAttributes(new DbAttributes()),
174 secd_PersistentRef(NULL
),
175 mDoNotEncrypt(false),
177 mMutex(Mutex::recursive
)
179 mDbAttributes
->recordType(item
.recordType());
181 if (item
.mKeychain
) {
182 // get the entire source item from its keychain. This requires figuring
183 // out the schema for the item based on its record type.
184 // Ask the remote item to fill our attributes dictionary, because it probably has an attached keychain to ask
185 item
.fillDbAttributesFromSchema(*mDbAttributes
, item
.recordType());
187 item
.getContent(mDbAttributes
.get(), mData
.get());
190 // @@@ We don't deal with modified attributes.
192 if (item
.modifiedData())
193 // the copied data comes from the source item
194 mData
= new CssmDataContainer(item
.modifiedData()->Data
,
195 item
.modifiedData()->Length
);
198 ItemImpl::~ItemImpl()
200 if (secd_PersistentRef
) {
201 CFRelease(secd_PersistentRef
);
208 ItemImpl::getMutexForObject() const
212 return mKeychain
->getKeychainMutex();
220 ItemImpl::aboutToDestruct()
222 if(mKeychain
.get()) {
223 mKeychain
->forceRemoveFromCache(this);
230 ItemImpl::didModify()
232 StLock
<Mutex
>_(mMutex
);
234 mDbAttributes
.reset(NULL
);
238 ItemImpl::defaultAttributeValue(const CSSM_DB_ATTRIBUTE_INFO
&info
)
240 static const uint32 zeroInt
= 0;
241 static const double zeroDouble
= 0.0;
242 static const char timeBytes
[] = "20010101000000Z";
244 static const CSSM_DATA defaultFourBytes
= { 4, (uint8
*) &zeroInt
};
245 static const CSSM_DATA defaultEightBytes
= { 8, (uint8
*) &zeroDouble
};
246 static const CSSM_DATA defaultTime
= { 16, (uint8
*) timeBytes
};
247 static const CSSM_DATA defaultZeroBytes
= { 0, NULL
};
249 switch (info
.AttributeFormat
)
251 case CSSM_DB_ATTRIBUTE_FORMAT_SINT32
:
252 case CSSM_DB_ATTRIBUTE_FORMAT_UINT32
:
253 return defaultFourBytes
;
255 case CSSM_DB_ATTRIBUTE_FORMAT_REAL
:
256 return defaultEightBytes
;
258 case CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE
:
262 return defaultZeroBytes
;
266 void ItemImpl::fillDbAttributesFromSchema(DbAttributes
& dbAttributes
, CSSM_DB_RECORDTYPE recordType
, Keychain keychain
) {
267 // If we weren't passed a keychain, use our own.
268 keychain
= !!keychain
? keychain
: mKeychain
;
270 // Without a keychain, there's no one to ask.
275 SecKeychainAttributeInfo
* infos
;
276 keychain
->getAttributeInfoForItemID(recordType
, &infos
);
278 secnotice("integrity", "filling %u attributes for type %u", (unsigned int)infos
->count
, recordType
);
280 for (uint32 i
= 0; i
< infos
->count
; i
++) {
281 CSSM_DB_ATTRIBUTE_INFO info
;
282 memset(&info
, 0, sizeof(info
));
284 info
.AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER
;
285 info
.Label
.AttributeID
= infos
->tag
[i
];
286 info
.AttributeFormat
= infos
->format
[i
];
288 dbAttributes
.add(info
);
291 keychain
->freeAttributeInfo(infos
);
294 DbAttributes
* ItemImpl::getCurrentAttributes() {
295 DbAttributes
* dbAttributes
;
296 secnotice("integrity", "getting current attributes...");
298 if(mUniqueId
.get()) {
299 // If we have a unique id, there's an item in the database backing us. Ask for its attributes.
300 dbAttributes
= new DbAttributes(dbUniqueRecord()->database(), 1);
301 fillDbAttributesFromSchema(*dbAttributes
, recordType());
302 mUniqueId
->get(dbAttributes
, NULL
);
304 // and fold in any updates.
305 if(mDbAttributes
.get()) {
306 secnotice("integrity", "adding %d attributes from mDbAttributes", mDbAttributes
->size());
307 dbAttributes
->updateWithDbAttributes(&(*mDbAttributes
.get()));
309 } else if (mDbAttributes
.get()) {
310 // We don't have a backing item, so all our attributes are in mDbAttributes. Copy them.
311 secnotice("integrity", "no unique id, using %d attributes from mDbAttributes", mDbAttributes
->size());
312 dbAttributes
= new DbAttributes();
313 dbAttributes
->updateWithDbAttributes(&(*mDbAttributes
.get()));
315 // No attributes at all. We should maybe throw here, but let's not.
316 secnotice("integrity", "no attributes at all");
317 dbAttributes
= new DbAttributes();
319 dbAttributes
->recordType(recordType());
320 // TODO: We don't set semanticInformation. Issue?
326 void ItemImpl::encodeAttributes(CssmOwnedData
&attributeBlob
) {
327 // Sometimes we don't have our attributes. Find them.
328 auto_ptr
<DbAttributes
> dbAttributes(getCurrentAttributes());
329 encodeAttributesFromDictionary(attributeBlob
, dbAttributes
.get());
333 void ItemImpl::encodeAttributesFromDictionary(CssmOwnedData
&attributeBlob
, DbAttributes
* dbAttributes
) {
334 // Create a CFDictionary from dbAttributes and call der_encode_dictionary on it
335 CFRef
<CFMutableDictionaryRef
> attributes
;
336 attributes
.take(CFDictionaryCreateMutable(NULL
, dbAttributes
->size(), &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
));
338 secnotice("integrity", "looking at %d attributes", dbAttributes
->size());
339 // TODO: include record type and semantic information?
341 for(int i
= 0; i
< dbAttributes
->size(); i
++) {
342 CssmDbAttributeData
& data
= dbAttributes
->attributes()[i
];
343 CssmDbAttributeInfo
& datainfo
= data
.info();
345 // Sometimes we need to normalize the info. Calling Schema::attributeInfo is the best way to do that.
346 // There's no avoiding the try-catch structure here, since only some of the names are in Schema::attributeInfo,
347 // but it will only indicate that by throwing.
348 CssmDbAttributeInfo
& actualInfo
= datainfo
;
350 if(datainfo
.nameFormat() == CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER
&& Schema::haveAttributeInfo(datainfo
.intName())) {
351 actualInfo
= Schema::attributeInfo(datainfo
.intName());
354 actualInfo
= datainfo
;
357 // Pull the label/name out of this data
358 CFRef
<CFDataRef
> label
= NULL
;
360 switch(actualInfo
.nameFormat()) {
361 case CSSM_DB_ATTRIBUTE_NAME_AS_STRING
: {
362 const char* stringname
= actualInfo
.stringName();
363 label
.take(CFDataCreate(NULL
, reinterpret_cast<const UInt8
*>(stringname
), strlen(stringname
)));
366 case CSSM_DB_ATTRIBUTE_NAME_AS_OID
: {
367 const CssmOid
& oidname
= actualInfo
.oidName();
368 label
.take(CFDataCreate(NULL
, reinterpret_cast<const UInt8
*>(oidname
.data()), oidname
.length()));
371 case CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER
: {
372 uint32 iname
= actualInfo
.intName();
373 label
.take(CFDataCreate(NULL
, reinterpret_cast<const UInt8
*>(&(iname
)), sizeof(uint32
)));
378 if(data
.size() == 0) {
379 // This attribute doesn't have a value, and so shouldn't be included in the digest.
383 // Do not include the Creation or Modification date attributes in the hash.
384 // Use this complicated method of checking so we'll catch string and integer names.
385 SecKeychainAttrType cdat
= kSecCreationDateItemAttr
;
386 SecKeychainAttrType cmod
= kSecModDateItemAttr
;
387 if((CFDataGetLength(label
) == sizeof(SecKeychainAttrType
)) &&
388 ((memcmp(CFDataGetBytePtr(label
), &cdat
, sizeof(SecKeychainAttrType
)) == 0) ||
389 (memcmp(CFDataGetBytePtr(label
), &cmod
, sizeof(SecKeychainAttrType
)) == 0))) {
393 // Collect the raw data for each value of this CssmDbAttributeData
394 CFRef
<CFMutableArrayRef
> attributeDataContainer
;
395 attributeDataContainer
.take(CFArrayCreateMutable(NULL
, data
.size(), &kCFTypeArrayCallBacks
));
397 for(int j
= 0; j
< data
.size(); j
++) {
398 CssmData
& entry
= data
.values()[j
];
400 CFRef
<CFDataRef
> datadata
= NULL
;
401 switch(actualInfo
.format()) {
402 case CSSM_DB_ATTRIBUTE_FORMAT_BLOB
:
403 case CSSM_DB_ATTRIBUTE_FORMAT_STRING
:
404 case CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE
:
405 datadata
.take(CFDataCreate(NULL
, reinterpret_cast<const UInt8
*>(data
.values()[j
].data()), data
.values()[j
].length()));
408 case CSSM_DB_ATTRIBUTE_FORMAT_UINT32
: {
409 uint32 x
= entry
.length() == 1 ? *reinterpret_cast<uint8
*>(entry
.Data
) :
410 entry
.length() == 2 ? *reinterpret_cast<uint16
*>(entry
.Data
) :
411 entry
.length() == 4 ? *reinterpret_cast<uint32
*>(entry
.Data
) : 0;
412 datadata
.take(CFDataCreate(NULL
, reinterpret_cast<const UInt8
*>(&x
), sizeof(x
)));
416 case CSSM_DB_ATTRIBUTE_FORMAT_SINT32
: {
417 sint32 x
= entry
.length() == 1 ? *reinterpret_cast<sint8
*>(entry
.Data
) :
418 entry
.length() == 2 ? *reinterpret_cast<sint16
*>(entry
.Data
) :
419 entry
.length() == 4 ? *reinterpret_cast<sint32
*>(entry
.Data
) : 0;
420 datadata
.take(CFDataCreate(NULL
, reinterpret_cast<const UInt8
*>(&x
), sizeof(x
)));
423 // CSSM_DB_ATTRIBUTE_FORMAT_BIG_NUM is unimplemented here but
424 // has some canonicalization requirements, see DbValue.cpp
430 CFArrayAppendValue(attributeDataContainer
, datadata
);
432 CFDictionaryAddValue(attributes
, label
, attributeDataContainer
);
435 // Now that we have a CFDictionary containing a bunch of CFDatas, turn that
439 CFRef
<CFDataRef
> derBlob
;
440 derBlob
.take(CFPropertyListCreateDERData(NULL
, attributes
, &error
));
442 // TODO: How do we check error here?
448 attributeBlob
.length(CFDataGetLength(derBlob
));
449 attributeBlob
.copy(CFDataGetBytePtr(derBlob
), CFDataGetLength(derBlob
));
452 void ItemImpl::computeDigest(CssmOwnedData
&sha2
) {
453 auto_ptr
<DbAttributes
> dbAttributes(getCurrentAttributes());
454 ItemImpl::computeDigestFromDictionary(sha2
, dbAttributes
.get());
457 void ItemImpl::computeDigestFromDictionary(CssmOwnedData
&sha2
, DbAttributes
* dbAttributes
) {
459 CssmAutoData
attributeBlob(Allocator::standard());
460 encodeAttributesFromDictionary(attributeBlob
, dbAttributes
);
462 sha2
.length(CC_SHA256_DIGEST_LENGTH
);
463 CC_SHA256(attributeBlob
.get().data(), static_cast<CC_LONG
>(attributeBlob
.get().length()), sha2
);
464 secnotice("integrity", "finished: %s", sha2
.get().toHex().c_str());
465 } catch (MacOSError mose
) {
466 secnotice("integrity", "MacOSError: %d", (int)mose
.osStatus());
468 secnotice("integrity", "unknown exception");
472 void ItemImpl::addIntegrity(Access
&access
, bool force
) {
473 if(!force
&& (!mKeychain
|| !mKeychain
->hasIntegrityProtection())) {
474 secnotice("integrity", "skipping integrity add due to keychain version\n");
479 CssmAutoData
digest(Allocator::standard());
480 computeDigest(digest
);
482 // First, check if this already has an integrity tag
484 access
.findSpecificAclsForRight(CSSM_ACL_AUTHORIZATION_INTEGRITY
, acls
);
486 if(acls
.size() >= 1) {
487 // Use the existing ACL
489 secnotice("integrity", "previous integrity acl exists; setting integrity");
490 acl
->setIntegrity(digest
.get());
492 // Delete all extra ACLs
493 for(int i
= 1; i
< acls
.size(); i
++) {
494 secnotice("integrity", "extra integrity acls exist; removing %d",i
);
497 } else if(acls
.size() == 0) {
499 secnotice("integrity", "no previous integrity acl exists; making a new one");
500 acl
= new ACL(digest
.get());
505 void ItemImpl::setIntegrity(bool force
) {
506 if(!force
&& (!mKeychain
|| !mKeychain
->hasIntegrityProtection())) {
507 secnotice("integrity", "skipping integrity set due to keychain version");
511 // For Items, only passwords should have integrity
512 if(!(recordType() == CSSM_DL_DB_RECORD_GENERIC_PASSWORD
|| recordType() == CSSM_DL_DB_RECORD_INTERNET_PASSWORD
)) {
516 // If we're not on an SSDb, we shouldn't have integrity
517 Db
db(mKeychain
->database());
518 if (!useSecureStorage(db
)) {
522 setIntegrity(*group(), force
);
525 void ItemImpl::setIntegrity(AclBearer
&bearer
, bool force
) {
526 if(!force
&& (!mKeychain
|| !mKeychain
->hasIntegrityProtection())) {
527 secnotice("integrity", "skipping integrity acl set due to keychain version");
531 SecPointer
<Access
> access
= new Access(bearer
);
533 access
->removeAclsForRight(CSSM_ACL_AUTHORIZATION_PARTITION_ID
);
534 addIntegrity(*access
, force
);
535 access
->setAccess(bearer
, true);
538 void ItemImpl::removeIntegrity(const AccessCredentials
*cred
) {
539 removeIntegrity(*group(), cred
);
542 void ItemImpl::removeIntegrity(AclBearer
&bearer
, const AccessCredentials
*cred
) {
543 SecPointer
<Access
> access
= new Access(bearer
);
546 access
->findSpecificAclsForRight(CSSM_ACL_AUTHORIZATION_INTEGRITY
, acls
);
547 for(int i
= 0; i
< acls
.size(); i
++) {
551 access
->findSpecificAclsForRight(CSSM_ACL_AUTHORIZATION_PARTITION_ID
, acls
);
552 for(int i
= 0; i
< acls
.size(); i
++) {
556 access
->editAccess(bearer
, true, cred
);
559 bool ItemImpl::checkIntegrity() {
560 // Note: subclasses are responsible for checking themselves.
562 // If we don't have a keychain yet, we don't have any group. Return true?
563 if(!isPersistent()) {
564 secnotice("integrity", "no keychain, integrity is valid?");
568 if(!mKeychain
|| !mKeychain
->hasIntegrityProtection()) {
569 secnotice("integrity", "skipping integrity check due to keychain version");
573 // Collect our SSGroup, if it exists.
575 SSGroup ssGroup
= group();
577 return checkIntegrity(*ssGroup
);
580 // If we don't have an SSGroup, we can't be invalid. return true.
584 bool ItemImpl::checkIntegrity(AclBearer
& aclBearer
) {
585 if(!mKeychain
|| !mKeychain
->hasIntegrityProtection()) {
586 secnotice("integrity", "skipping integrity check due to keychain version");
590 auto_ptr
<DbAttributes
> dbAttributes(getCurrentAttributes());
591 return checkIntegrityFromDictionary(aclBearer
, dbAttributes
.get());
594 bool ItemImpl::checkIntegrityFromDictionary(AclBearer
& aclBearer
, DbAttributes
* dbAttributes
) {
596 AutoAclEntryInfoList aclInfos
;
597 aclBearer
.getAcl(aclInfos
, CSSM_APPLE_ACL_TAG_INTEGRITY
);
599 // We should only expect there to be one integrity tag. If there's not,
600 // take the first one and ignore the rest. We should probably attempt delete
603 AclEntryInfo
&info
= aclInfos
.at(0);
604 auto_ptr
<ACL
> acl(new ACL(info
, Allocator::standard()));
606 for(int i
= 1; i
< aclInfos
.count(); i
++) {
607 secnotice("integrity", "*** DUPLICATE INTEGRITY ACL, something has gone wrong");
610 CssmAutoData
digest(Allocator::standard());
611 computeDigestFromDictionary(digest
, dbAttributes
);
612 if (acl
->integrity() == digest
.get()) {
616 catch (CssmError cssme
) {
617 const char* errStr
= cssmErrorString(cssme
.error
);
618 secnotice("integrity", "caught CssmError: %d %s", (int) cssme
.error
, errStr
);
620 if(cssme
.error
== CSSMERR_CSP_ACL_ENTRY_TAG_NOT_FOUND
) {
621 // TODO: No entry, run migrator?
624 if(cssme
.error
== CSSMERR_CSP_INVALID_ACL_SUBJECT_VALUE
) {
625 // something went horribly wrong with fetching acl.
627 secnotice("integrity", "INVALID ITEM (too many integrity acls)");
630 if(cssme
.error
== CSSMERR_CSP_VERIFY_FAILED
) {
631 secnotice("integrity", "MAC verification failed; something has gone very wrong");
632 return false; // No MAC, no integrity.
638 secnotice("integrity", "***** INVALID ITEM");
642 PrimaryKey
ItemImpl::addWithCopyInfo (Keychain
&keychain
, bool isCopy
)
644 StLock
<Mutex
>_(mMutex
);
645 // If we already have a Keychain we can't be added.
647 MacOSError::throwMe(errSecDuplicateItem
);
649 // If we don't have any attributes we can't be added.
650 // (this might occur if attempting to add the item twice, since our attributes
651 // and data are set to NULL at the end of this function.)
652 if (!mDbAttributes
.get())
653 MacOSError::throwMe(errSecDuplicateItem
);
655 CSSM_DB_RECORDTYPE recordType
= mDbAttributes
->recordType();
657 // update the creation and update dates on the new item
660 KeychainSchema schema
= keychain
->keychainSchema();
662 GetCurrentMacLongDateTime(date
);
663 if (schema
->hasAttribute(recordType
, kSecCreationDateItemAttr
))
665 setAttribute(schema
->attributeInfoFor(recordType
, kSecCreationDateItemAttr
), date
);
668 if (schema
->hasAttribute(recordType
, kSecModDateItemAttr
))
670 setAttribute(schema
->attributeInfoFor(recordType
, kSecModDateItemAttr
), date
);
674 // If the label (PrintName) attribute isn't specified, set a default label.
675 mDbAttributes
->canonicalize(); // make sure we'll find the label with the thing Schema::attributeInfo returns
676 if (!mDoNotEncrypt
&& !mDbAttributes
->find(Schema::attributeInfo(kSecLabelItemAttr
)))
678 // if doNotEncrypt was set all of the attributes are wrapped in the data blob. Don't calculate here.
679 CssmDbAttributeData
*label
= NULL
;
682 case CSSM_DL_DB_RECORD_GENERIC_PASSWORD
:
683 label
= mDbAttributes
->find(Schema::attributeInfo(kSecServiceItemAttr
));
686 case CSSM_DL_DB_RECORD_APPLESHARE_PASSWORD
:
687 case CSSM_DL_DB_RECORD_INTERNET_PASSWORD
:
688 label
= mDbAttributes
->find(Schema::attributeInfo(kSecServerItemAttr
));
689 // if AppleShare server name wasn't specified, try the server address
690 if (!label
) label
= mDbAttributes
->find(Schema::attributeInfo(kSecAddressItemAttr
));
696 // if all else fails, use the account name.
698 label
= mDbAttributes
->find(Schema::attributeInfo(kSecAccountItemAttr
));
700 if (label
&& label
->size())
701 setAttribute (Schema::attributeInfo(kSecLabelItemAttr
), label
->at
<CssmData
>(0));
704 // get the attributes that are part of the primary key
705 const CssmAutoDbRecordAttributeInfo
&primaryKeyInfos
=
706 keychain
->primaryKeyInfosFor(recordType
);
708 // make sure each primary key element has a value in the item, otherwise
709 // the database will complain. we make a set of the provided attribute infos
710 // to avoid O(N^2) behavior.
712 DbAttributes
*attributes
= mDbAttributes
.get();
713 typedef set
<CssmDbAttributeInfo
> InfoSet
;
718 // make a set of all the attributes in the key
719 for (uint32 i
= 0; i
< attributes
->size(); i
++)
720 infoSet
.insert(attributes
->at(i
).Info
);
722 for (uint32 i
= 0; i
< primaryKeyInfos
.size(); i
++) { // check to make sure all required attributes are in the key
723 InfoSet::const_iterator it
= infoSet
.find(primaryKeyInfos
.at(i
));
725 if (it
== infoSet
.end()) { // not in the key? add the default
726 // we need to add a default value to the item attributes
727 attributes
->add(primaryKeyInfos
.at(i
), defaultAttributeValue(primaryKeyInfos
.at(i
)));
733 mKeychain
= keychain
;
735 Db
db(keychain
->database());
738 mUniqueId
= db
->insertWithoutEncryption (recordType
, NULL
, mData
.get());
740 else if (useSecureStorage(db
))
742 updateSSGroup(db
, recordType
, mData
.get(), keychain
, mAccess
);
743 mAccess
= NULL
; // use them and lose them - TODO: should this only be unset if there's no error in saveToNewSSGroup? Unclear.
747 // add the item to the (regular) db
748 mUniqueId
= db
->insert(recordType
, mDbAttributes
.get(), mData
.get());
751 mPrimaryKey
= keychain
->makePrimaryKey(recordType
, mUniqueId
);
757 // Forget our data and attributes.
759 mDbAttributes
.reset(NULL
);
767 ItemImpl::add (Keychain
&keychain
)
769 return addWithCopyInfo (keychain
, false);
775 ItemImpl::copyTo(const Keychain
&keychain
, Access
*newAccess
)
777 // We'll be removing any Partition or Integrity ACLs from this item during
778 // the copy. Note that creating a new item from this one fetches the data,
779 // so this process must now be on the ACL/partition ID list for this item,
780 // and an attacker without access can't cause this removal.
782 // The integrity and partition ID acls will get re-added once the item lands
783 // in the new keychain, if it supports them. If it doesn't, removing the
784 // integrity acl as it leaves will prevent any issues if the item is
785 // modified in the unsupported keychain and then re-copied back into an
786 // integrity keychain.
788 StLock
<Mutex
>_(mMutex
);
791 newAccess
->removeAclsForRight(CSSM_ACL_AUTHORIZATION_PARTITION_ID
);
792 newAccess
->removeAclsForRight(CSSM_ACL_AUTHORIZATION_INTEGRITY
);
793 item
->setAccess(newAccess
);
795 /* Attempt to copy the access from the current item to the newly created one. */
796 SSGroup myGroup
= group();
799 SecPointer
<Access
> access
= new Access(*myGroup
);
800 access
->removeAclsForRight(CSSM_ACL_AUTHORIZATION_PARTITION_ID
);
801 access
->removeAclsForRight(CSSM_ACL_AUTHORIZATION_INTEGRITY
);
802 item
->setAccess(access
);
806 keychain
->addCopy(item
);
813 StLock
<Mutex
>_(mMutex
);
815 MacOSError::throwMe(errSecNoSuchKeychain
);
817 // Don't update if nothing changed.
821 CSSM_DB_RECORDTYPE aRecordType
= recordType();
822 KeychainSchema schema
= mKeychain
->keychainSchema();
824 // Update the modification date on the item if there is a mod date attribute.
825 if (schema
->hasAttribute(aRecordType
, kSecModDateItemAttr
))
828 GetCurrentMacLongDateTime(date
);
829 setAttribute(schema
->attributeInfoFor(aRecordType
, kSecModDateItemAttr
), date
);
832 Db
db(dbUniqueRecord()->database());
835 CSSM_DB_RECORD_ATTRIBUTE_DATA attrData
;
836 memset (&attrData
, 0, sizeof (attrData
));
837 attrData
.DataRecordType
= aRecordType
;
839 dbUniqueRecord()->modifyWithoutEncryption(aRecordType
,
842 CSSM_DB_MODIFY_ATTRIBUTE_REPLACE
);
844 else if (useSecureStorage(db
))
846 // Pass mData to updateSSGroup. If we have any data to change (and it's
847 // therefore non-null), it'll save to a new SSGroup; otherwise, it will
848 // update the old ssgroup. This prevents a RAA on attribute update, while
849 // still protecting new data from being decrypted by old SSGroups with
850 // outdated attributes.
851 updateSSGroup(db
, recordType(), mData
.get());
855 dbUniqueRecord()->modify(aRecordType
,
858 CSSM_DB_MODIFY_ATTRIBUTE_REPLACE
);
863 PrimaryKey oldPK
= mPrimaryKey
;
864 mPrimaryKey
= mKeychain
->makePrimaryKey(aRecordType
, mUniqueId
);
866 // Forget our data and attributes.
868 mDbAttributes
.reset(NULL
);
870 // Let the Keychain update what it needs to.
871 mKeychain
->didUpdate(this, oldPK
, mPrimaryKey
);
876 ItemImpl::updateSSGroup(Db
& db
, CSSM_DB_RECORDTYPE recordType
, CssmDataContainer
* newdata
, Keychain keychain
, SecPointer
<Access
> access
)
878 // hhs replaced with the new aclFactory class
879 AclFactory aclFactory
;
880 const AccessCredentials
*nullCred
= aclFactory
.nullCred();
882 bool haveOldUniqueId
= !!mUniqueId
.get();
883 SSDbUniqueRecord
ssUniqueId(NULL
);
884 SSGroup
ssGroup(NULL
);
885 if(haveOldUniqueId
) {
886 ssUniqueId
= SSDbUniqueRecord(dynamic_cast<SSDbUniqueRecordImpl
*>(&(*mUniqueId
)));
887 if (ssUniqueId
.get() == NULL
) {
888 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER
);
890 ssGroup
= ssUniqueId
->group();
893 // If we have new data OR no old unique id, save to a new group
894 bool saveToNewSSGroup
= (!!newdata
) || (!haveOldUniqueId
);
896 // If there aren't any attributes, make up some blank ones.
897 if (!mDbAttributes
.get())
899 secnotice("integrity", "making new dbattributes");
900 mDbAttributes
.reset(new DbAttributes());
901 mDbAttributes
->recordType(mPrimaryKey
->recordType());
904 // Add the item to the secure storage db
905 SSDbImpl
* impl
= dynamic_cast<SSDbImpl
*>(&(*db
));
908 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER
);
913 TrackingAllocator
allocator(Allocator::standard());
915 if ((!access
) && (haveOldUniqueId
)) {
916 // Copy the ACL from the old group.
917 secnotice("integrity", "copying old ACL");
918 access
= new Access(*(ssGroup
));
920 // We can't copy these over to the new item; they're going to be reset.
921 // Remove them before securityd complains.
922 access
->removeAclsForRight(CSSM_ACL_AUTHORIZATION_PARTITION_ID
);
923 access
->removeAclsForRight(CSSM_ACL_AUTHORIZATION_INTEGRITY
);
924 } else if (!access
) {
925 secnotice("integrity", "setting up new ACL");
926 // create default access controls for the new item
927 CssmDbAttributeData
*data
= mDbAttributes
->find(Schema::attributeInfo(kSecLabelItemAttr
));
928 string printName
= data
? CssmData::overlay(data
->Value
[0]).toString() : "keychain item";
929 access
= new Access(printName
);
931 // special case for "iTools" password - allow anyone to decrypt the item
932 if (recordType
== CSSM_DL_DB_RECORD_GENERIC_PASSWORD
)
934 CssmDbAttributeData
*data
= mDbAttributes
->find(Schema::attributeInfo(kSecServiceItemAttr
));
935 if (data
&& data
->Value
[0].Length
== 6 && !memcmp("iTools", data
->Value
[0].Data
, 6))
937 typedef vector
<SecPointer
<ACL
> > AclSet
;
939 access
->findAclsForRight(CSSM_ACL_AUTHORIZATION_DECRYPT
, acls
);
940 for (AclSet::const_iterator it
= acls
.begin(); it
!= acls
.end(); it
++)
941 (*it
)->form(ACL::allowAllForm
);
945 secnotice("integrity", "passed an Access, use it");
946 // Access is non-null. Do nothing.
949 // If we have an old group and an old mUniqueId, then we're in the middle of an update.
950 // mDbAttributes contains the newest attributes, but not the old ones. Find
951 // them, merge them, and shove them all back into mDbAttributes. This lets
952 // us re-apply them all to the new item.
953 if(haveOldUniqueId
) {
954 mDbAttributes
.reset(getCurrentAttributes());
957 // Create a CSPDL transaction. Note that this does things when it goes out of scope.
958 CSPDLTransaction
transaction(db
);
961 ResourceControlContext prototype
;
962 maker
.initialOwner(prototype
, nullCred
);
964 if(saveToNewSSGroup
) {
965 secnotice("integrity", "saving to a new SSGroup");
967 // If we're updating an item, it has an old group and possibly an
968 // old mUniqueId. Delete these from the database, so we can insert
970 if(haveOldUniqueId
) {
971 secnotice("integrity", "deleting old mUniqueId");
972 mUniqueId
->deleteRecord();
975 secnotice("integrity", "no old mUniqueId");
978 // Create a new SSGroup with temporary access controls
979 SSGroup
newSSGroup(ssDb
, &prototype
);
980 const AccessCredentials
* cred
= maker
.cred();
983 doChange(keychain
, recordType
, ^{
984 mUniqueId
= ssDb
->ssInsert(recordType
, mDbAttributes
.get(), newdata
, newSSGroup
, cred
);
987 // now finalize the access controls on the group
988 addIntegrity(*access
);
989 access
->setAccess(*newSSGroup
, maker
);
991 // We have to reset this after we add the integrity, since it needs the attributes
992 mDbAttributes
.reset(NULL
);
994 transaction
.commit();
996 catch (CssmError cssme
) {
997 const char* errStr
= cssmErrorString(cssme
.error
);
998 secnotice("integrity", "caught CssmError during add: %d %s", (int) cssme
.error
, errStr
);
1000 // Delete the new SSGroup that we just created
1001 deleteSSGroup(newSSGroup
, nullCred
);
1004 catch (MacOSError mose
) {
1005 secnotice("integrity", "caught MacOSError during add: %d", (int) mose
.osStatus());
1007 deleteSSGroup(newSSGroup
, nullCred
);
1012 secnotice("integrity", "caught unknown exception during add");
1014 deleteSSGroup(newSSGroup
, nullCred
);
1018 // Modify the old SSGroup
1019 secnotice("integrity", "modifying the existing SSGroup");
1022 doChange(keychain
, recordType
, ^{
1024 const AccessCredentials
*autoPrompt
= globals().itemCredentials();
1025 ssUniqueId
->modify(recordType
,
1026 mDbAttributes
.get(),
1028 CSSM_DB_MODIFY_ATTRIBUTE_REPLACE
,
1032 // Update the integrity on the SSGroup
1033 setIntegrity(*ssGroup
);
1035 // We have to reset this after we add the integrity, since it needs the attributes
1036 mDbAttributes
.reset(NULL
);
1038 transaction
.commit();
1040 catch (CssmError cssme
) {
1041 const char* errStr
= cssmErrorString(cssme
.error
);
1042 secnotice("integrity", "caught CssmError during modify: %d %s", (int) cssme
.error
, errStr
);
1045 catch (MacOSError mose
) {
1046 secnotice("integrity", "caught MacOSError during modify: %d", (int) mose
.osStatus());
1051 secnotice("integrity", "caught unknown exception during modify");
1058 // Helper function to delete a group and swallow all errors
1059 void ItemImpl::deleteSSGroup(SSGroup
& ssgroup
, const AccessCredentials
* nullCred
) {
1061 ssgroup
->deleteKey(nullCred
);
1062 } catch(CssmError error
) {
1063 secnotice("integrity", "caught cssm error during deletion of group: %d %s", (int) error
.osStatus(), error
.what());
1064 } catch(MacOSError error
) {
1065 secnotice("integrity", "caught macos error during deletion of group: %d %s", (int) error
.osStatus(), error
.what());
1066 } catch(UnixError error
) {
1067 secnotice("integrity", "caught unix error during deletion of group: %d %s", (int) error
.osStatus(), error
.what());
1072 ItemImpl::doChange(Keychain keychain
, CSSM_DB_RECORDTYPE recordType
, void (^tryChange
) ())
1074 // Insert the record using the newly created group.
1077 } catch (CssmError cssme
) {
1078 // If there's a "duplicate" of this item, it might be an item with corrupt/invalid attributes
1079 // Try to extract the item and check its attributes, then try again if necessary
1080 auto_ptr
<CssmClient::DbAttributes
> primaryKeyAttrs
;
1081 if(cssme
.error
== CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA
) {
1082 secnotice("integrity", "possible duplicate, trying to delete invalid items");
1084 Keychain kc
= (keychain
? keychain
: mKeychain
);
1086 secnotice("integrity", "no valid keychain");
1089 // Only check for corrupt items if the keychain supports them
1090 if((!kc
) || !kc
->hasIntegrityProtection()) {
1091 secnotice("integrity", "skipping integrity check for corrupt items due to keychain support");
1094 primaryKeyAttrs
.reset(getCurrentAttributes());
1095 PrimaryKey pk
= kc
->makePrimaryKey(recordType
, primaryKeyAttrs
.get());
1097 bool tryAgain
= false;
1099 // Because things are lazy, maybe our keychain has a version
1100 // of this item with different attributes. Ask it!
1101 ItemImpl
* maybeItem
= kc
->_lookupItem(pk
);
1103 if(!maybeItem
->checkIntegrity()) {
1104 Item
item(maybeItem
);
1105 kc
->deleteItem(item
);
1109 // Our keychain doesn't know about any item with this primary key, so maybe
1110 // we have a corrupt item in the database. Let's check.
1112 secnotice("integrity", "making a cursor from primary key");
1113 CssmClient::DbCursor cursor
= pk
->createCursor(kc
);
1114 DbUniqueRecord uniqueId
;
1116 StLock
<Mutex
> _mutexLocker(*kc
->getKeychainMutex());
1118 // The item on-disk might have more or different attributes than we do, since we're
1119 // only searching via primary key. Fetch all of its attributes.
1120 auto_ptr
<DbAttributes
>dbDupAttributes (new DbAttributes(kc
->database(), 1));
1121 fillDbAttributesFromSchema(*dbDupAttributes
, recordType
, kc
);
1123 // Occasionally this cursor won't return the item attributes (for an unknown reason).
1124 // However, we know the attributes any item with this primary key should have, so use those instead.
1125 while (cursor
->next(dbDupAttributes
.get(), NULL
, uniqueId
)) {
1126 secnotice("integrity", "got an item...");
1128 SSGroup group
= safer_cast
<SSDbUniqueRecordImpl
&>(*uniqueId
).group();
1129 if(!ItemImpl::checkIntegrityFromDictionary(*group
, dbDupAttributes
.get())) {
1130 secnotice("integrity", "item is invalid! deleting...");
1131 uniqueId
->deleteRecord();
1138 secnotice("integrity", "trying again...");
1141 // We didn't find an invalid item, the duplicate item exception is real
1142 secnotice("integrity", "duplicate item exception is real; throwing it on");
1153 ItemImpl::getClass(SecKeychainAttribute
&attr
, UInt32
*actualLength
)
1155 StLock
<Mutex
>_(mMutex
);
1157 *actualLength
= sizeof(SecItemClass
);
1159 if (attr
.length
< sizeof(SecItemClass
))
1160 MacOSError::throwMe(errSecBufferTooSmall
);
1162 SecItemClass aClass
= Schema::itemClassFor(recordType());
1163 memcpy(attr
.data
, &aClass
, sizeof(SecItemClass
));
1167 ItemImpl::setAttribute(SecKeychainAttribute
& attr
)
1169 StLock
<Mutex
>_(mMutex
);
1170 setAttribute(Schema::attributeInfo(attr
.tag
), CssmData(attr
.data
, attr
.length
));
1174 ItemImpl::recordType()
1176 StLock
<Mutex
>_(mMutex
);
1177 if (mDbAttributes
.get())
1178 return mDbAttributes
->recordType();
1180 return mPrimaryKey
->recordType();
1183 const DbAttributes
*
1184 ItemImpl::modifiedAttributes()
1186 StLock
<Mutex
>_(mMutex
);
1187 return mDbAttributes
.get();
1191 ItemImpl::modifiedData()
1193 StLock
<Mutex
>_(mMutex
);
1198 ItemImpl::setData(UInt32 length
,const void *data
)
1200 StLock
<Mutex
>_(mMutex
);
1201 mData
= new CssmDataContainer(data
, length
);
1205 ItemImpl::setAccess(Access
*newAccess
)
1207 StLock
<Mutex
>_(mMutex
);
1208 mAccess
= newAccess
;
1211 CssmClient::DbUniqueRecord
1212 ItemImpl::dbUniqueRecord()
1214 StLock
<Mutex
>_(mMutex
);
1215 if (!isPersistent()) // is there no database attached?
1217 MacOSError::throwMe(errSecNotAvailable
);
1222 DbCursor
cursor(mPrimaryKey
->createCursor(mKeychain
));
1223 if (!cursor
->next(NULL
, NULL
, mUniqueId
))
1224 MacOSError::throwMe(errSecInvalidItemRef
);
1227 // Check that our Db still matches our keychain's db. If not, find this item again in the new Db.
1228 // Why silly !(x == y) construction? Compiler calls operator bool() on each pointer otherwise.
1229 if(!(mUniqueId
->database() == keychain()->database())) {
1230 secnotice("integrity", "updating db of mUniqueRecord");
1232 DbCursor
cursor(mPrimaryKey
->createCursor(mKeychain
));
1233 if (!cursor
->next(NULL
, NULL
, mUniqueId
))
1234 MacOSError::throwMe(errSecInvalidItemRef
);
1241 ItemImpl::primaryKey()
1247 ItemImpl::isPersistent()
1253 ItemImpl::isModified()
1255 StLock
<Mutex
>_(mMutex
);
1256 return mData
.get() || mDbAttributes
.get();
1260 ItemImpl::keychain()
1266 ItemImpl::operator < (const ItemImpl
&other
)
1268 if (mData
&& *mData
)
1271 return this < &other
;
1274 return mPrimaryKey
< other
.mPrimaryKey
;
1278 ItemImpl::setAttribute(const CssmDbAttributeInfo
&info
, const CssmPolyData
&data
)
1280 StLock
<Mutex
>_(mMutex
);
1281 if (!mDbAttributes
.get())
1283 mDbAttributes
.reset(new DbAttributes());
1284 mDbAttributes
->recordType(mPrimaryKey
->recordType());
1287 size_t length
= data
.Length
;
1288 const void *buf
= reinterpret_cast<const void *>(data
.Data
);
1289 uint8 timeString
[16];
1291 // XXX This code is duplicated in KCCursorImpl::KCCursorImpl()
1292 // Convert a 4 or 8 byte TIME_DATE to a CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE
1293 // style attribute value.
1294 if (info
.format() == CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE
)
1296 if (length
== sizeof(UInt32
))
1298 MacSecondsToTimeString(*reinterpret_cast<const UInt32
*>(buf
), 16, &timeString
);
1302 else if (length
== sizeof(SInt64
))
1304 MacLongDateTimeToTimeString(*reinterpret_cast<const SInt64
*>(buf
), 16, &timeString
);
1310 mDbAttributes
->add(info
, CssmData(const_cast<void*>(buf
), length
));
1314 ItemImpl::modifyContent(const SecKeychainAttributeList
*attrList
, UInt32 dataLength
, const void *inData
)
1316 StLock
<Mutex
>_(mMutex
);
1317 unique_ptr
<StReadWriteLock
> __(mKeychain
== NULL
? NULL
: new StReadWriteLock(*(mKeychain
->getKeychainReadWriteLock()), StReadWriteLock::Write
));
1319 if (!mDbAttributes
.get())
1321 mDbAttributes
.reset(new DbAttributes());
1322 mDbAttributes
->recordType(mPrimaryKey
->recordType());
1325 if(attrList
) // optional
1327 for(UInt32 ix
=0; ix
< attrList
->count
; ix
++)
1329 SecKeychainAttrType attrTag
= attrList
->attr
[ix
].tag
;
1331 if (attrTag
== APPLEDB_CSSM_PRINTNAME_ATTRIBUTE
)
1333 // must remap a caller-supplied kSecKeyPrintName attribute tag for key items, since it isn't in the schema
1334 // (note that this will ultimately match kGenericPrintName in Schema.cpp)
1335 attrTag
= kSecLabelItemAttr
;
1338 mDbAttributes
->add(Schema::attributeInfo(attrTag
), CssmData(attrList
->attr
[ix
].data
, attrList
->attr
[ix
].length
));
1344 mData
= new CssmDataContainer(inData
, dataLength
);
1351 ItemImpl::getContent(SecItemClass
*itemClass
, SecKeychainAttributeList
*attrList
, UInt32
*length
, void **outData
)
1353 StLock
<Mutex
>_(mMutex
);
1354 // If the data hasn't been set we can't return it.
1355 if (!mKeychain
&& outData
)
1357 CssmData
*data
= mData
.get();
1359 MacOSError::throwMe(errSecDataNotAvailable
);
1361 // TODO: need to check and make sure attrs are valid and handle error condition
1365 *itemClass
= Schema::itemClassFor(recordType());
1367 bool getDataFromDatabase
= mKeychain
&& mPrimaryKey
;
1368 if (getDataFromDatabase
) // are we attached to a database?
1372 // get the number of attributes requested by the caller
1373 UInt32 attrCount
= attrList
? attrList
->count
: 0;
1375 // make a DBAttributes structure and populate it
1376 DbAttributes
dbAttributes(dbUniqueRecord()->database(), attrCount
);
1377 for (UInt32 ix
= 0; ix
< attrCount
; ++ix
)
1379 dbAttributes
.add(Schema::attributeInfo(attrList
->attr
[ix
].tag
));
1382 // request the data from the database (since we are a reference "item" and the data is really stored there)
1383 CssmDataContainer itemData
;
1384 getContent(&dbAttributes
, outData
? &itemData
: NULL
);
1386 // retrieve the data from result
1387 for (UInt32 ix
= 0; ix
< attrCount
; ++ix
)
1389 if (dbAttributes
.at(ix
).NumberOfValues
> 0)
1391 attrList
->attr
[ix
].data
= dbAttributes
.at(ix
).Value
[0].Data
;
1392 attrList
->attr
[ix
].length
= (UInt32
)dbAttributes
.at(ix
).Value
[0].Length
;
1394 // We don't want the data released, it is up the client
1395 dbAttributes
.at(ix
).Value
[0].Data
= NULL
;
1396 dbAttributes
.at(ix
).Value
[0].Length
= 0;
1400 attrList
->attr
[ix
].data
= NULL
;
1401 attrList
->attr
[ix
].length
= 0;
1408 *outData
=itemData
.data();
1409 itemData
.Data
= NULL
;
1412 *length
=(UInt32
)itemData
.length();
1413 itemData
.Length
= 0;
1418 getLocalContent(attrList
, length
, outData
);
1421 // Inform anyone interested that we are doing this
1422 #if SENDACCESSNOTIFICATIONS
1425 secinfo("kcnotify", "ItemImpl::getContent(%p, %p, %p, %p) retrieved content",
1426 itemClass
, attrList
, length
, outData
);
1428 KCEventNotifier::PostKeychainEvent(kSecDataAccessEvent
, mKeychain
, this);
1434 ItemImpl::freeContent(SecKeychainAttributeList
*attrList
, void *data
)
1436 Allocator
&allocator
= Allocator::standard(); // @@@ This might not match the one used originally
1438 allocator
.free(data
);
1440 UInt32 attrCount
= attrList
? attrList
->count
: 0;
1441 for (UInt32 ix
= 0; ix
< attrCount
; ++ix
)
1443 allocator
.free(attrList
->attr
[ix
].data
);
1444 attrList
->attr
[ix
].data
= NULL
;
1449 ItemImpl::modifyAttributesAndData(const SecKeychainAttributeList
*attrList
, UInt32 dataLength
, const void *inData
)
1451 StLock
<Mutex
>_(mMutex
);
1453 MacOSError::throwMe(errSecNoSuchKeychain
);
1457 if (!mDbAttributes
.get())
1459 mDbAttributes
.reset(new DbAttributes());
1460 mDbAttributes
->recordType(mPrimaryKey
->recordType());
1463 CSSM_DB_RECORDTYPE recordType
= mDbAttributes
->recordType();
1464 UInt32 attrCount
= attrList
? attrList
->count
: 0;
1465 for (UInt32 ix
= 0; ix
< attrCount
; ix
++)
1467 SecKeychainAttrType attrTag
= attrList
->attr
[ix
].tag
;
1469 if (attrTag
== kSecLabelItemAttr
)
1471 // must remap a caller-supplied label attribute tag for password items, since it isn't in the schema
1472 // (note that this will ultimately match kGenericPrintName in Schema.cpp)
1473 if (IS_PASSWORD_ITEM_CLASS( Schema::itemClassFor(recordType
) ))
1474 attrTag
= APPLEDB_GENERIC_PRINTNAME_ATTRIBUTE
;
1477 CssmDbAttributeInfo info
=mKeychain
->attributeInfoFor(recordType
, attrTag
);
1479 if (attrList
->attr
[ix
].length
|| info
.AttributeFormat
==CSSM_DB_ATTRIBUTE_FORMAT_STRING
|| info
.AttributeFormat
==CSSM_DB_ATTRIBUTE_FORMAT_BLOB
1480 || info
.AttributeFormat
==CSSM_DB_ATTRIBUTE_FORMAT_STRING
|| info
.AttributeFormat
==CSSM_DB_ATTRIBUTE_FORMAT_BIG_NUM
1481 || info
.AttributeFormat
==CSSM_DB_ATTRIBUTE_FORMAT_MULTI_UINT32
)
1482 mDbAttributes
->add(info
, CssmData(attrList
->attr
[ix
].data
, attrList
->attr
[ix
].length
));
1484 mDbAttributes
->add(info
);
1490 mData
= new CssmDataContainer(inData
, dataLength
);
1497 ItemImpl::getAttributesAndData(SecKeychainAttributeInfo
*info
, SecItemClass
*itemClass
,
1498 SecKeychainAttributeList
**attrList
, UInt32
*length
, void **outData
)
1500 StLock
<Mutex
>_(mMutex
);
1501 // If the data hasn't been set we can't return it.
1502 if (!mKeychain
&& outData
)
1504 CssmData
*data
= mData
.get();
1506 MacOSError::throwMe(errSecDataNotAvailable
);
1508 // TODO: need to check and make sure attrs are valid and handle error condition
1510 SecItemClass myItemClass
= Schema::itemClassFor(recordType());
1512 *itemClass
= myItemClass
;
1514 // @@@ This call won't work for floating items (like certificates).
1517 UInt32 attrCount
= info
? info
->count
: 0;
1518 DbAttributes
dbAttributes(dbUniqueRecord()->database(), attrCount
);
1519 for (UInt32 ix
= 0; ix
< attrCount
; ix
++)
1521 CssmDbAttributeData
&record
= dbAttributes
.add();
1522 record
.Info
.AttributeNameFormat
=CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER
;
1523 record
.Info
.Label
.AttributeID
=info
->tag
[ix
];
1525 if (record
.Info
.Label
.AttributeID
== kSecLabelItemAttr
)
1527 // must remap a caller-supplied label attribute tag for password items, since it isn't in the schema
1528 if (IS_PASSWORD_ITEM_CLASS( myItemClass
))
1529 record
.Info
.Label
.AttributeID
= APPLEDB_GENERIC_PRINTNAME_ATTRIBUTE
;
1533 CssmDataContainer itemData
;
1534 getContent(&dbAttributes
, outData
? &itemData
: NULL
);
1536 if (info
&& attrList
)
1538 SecKeychainAttributeList
*theList
=reinterpret_cast<SecKeychainAttributeList
*>(malloc(sizeof(SecKeychainAttributeList
)));
1539 SecKeychainAttribute
*attr
=reinterpret_cast<SecKeychainAttribute
*>(malloc(sizeof(SecKeychainAttribute
)*attrCount
));
1540 theList
->count
=attrCount
;
1543 for (UInt32 ix
= 0; ix
< attrCount
; ++ix
)
1545 attr
[ix
].tag
=info
->tag
[ix
];
1547 if (dbAttributes
.at(ix
).NumberOfValues
> 0)
1549 attr
[ix
].data
= dbAttributes
.at(ix
).Value
[0].Data
;
1550 attr
[ix
].length
= (UInt32
)dbAttributes
.at(ix
).Value
[0].Length
;
1552 // We don't want the data released, it is up the client
1553 dbAttributes
.at(ix
).Value
[0].Data
= NULL
;
1554 dbAttributes
.at(ix
).Value
[0].Length
= 0;
1558 attr
[ix
].data
= NULL
;
1559 attr
[ix
].length
= 0;
1567 *outData
=itemData
.data();
1570 if (length
) *length
=(UInt32
)itemData
.length();
1573 #if SENDACCESSNOTIFICATIONS
1574 secinfo("kcnotify", "ItemImpl::getAttributesAndData(%p, %p, %p, %p, %p) retrieved data",
1575 info
, itemClass
, attrList
, length
, outData
);
1577 KCEventNotifier::PostKeychainEvent(kSecDataAccessEvent
, mKeychain
, this);
1584 ItemImpl::freeAttributesAndData(SecKeychainAttributeList
*attrList
, void *data
)
1586 Allocator
&allocator
= Allocator::standard(); // @@@ This might not match the one used originally
1589 allocator
.free(data
);
1593 for (UInt32 ix
= 0; ix
< attrList
->count
; ++ix
)
1595 allocator
.free(attrList
->attr
[ix
].data
);
1597 free(attrList
->attr
);
1603 ItemImpl::getAttribute(SecKeychainAttribute
& attr
, UInt32
*actualLength
)
1605 StLock
<Mutex
>_(mMutex
);
1606 if (attr
.tag
== kSecClassItemAttr
)
1607 return getClass(attr
, actualLength
);
1609 if (mDbAttributes
.get())
1611 CssmDbAttributeData
*data
= mDbAttributes
->find(Schema::attributeInfo(attr
.tag
));
1614 getAttributeFrom(data
, attr
, actualLength
);
1620 MacOSError::throwMe(errSecNoSuchAttr
);
1622 DbAttributes
dbAttributes(dbUniqueRecord()->database(), 1);
1623 dbAttributes
.add(Schema::attributeInfo(attr
.tag
));
1624 dbUniqueRecord()->get(&dbAttributes
, NULL
);
1625 getAttributeFrom(&dbAttributes
.at(0), attr
, actualLength
);
1629 ItemImpl::getAttributeFrom(CssmDbAttributeData
*data
, SecKeychainAttribute
&attr
, UInt32
*actualLength
)
1631 StLock
<Mutex
>_(mMutex
);
1632 static const uint32 zero
= 0;
1634 const void *buf
= NULL
;
1636 // Temporary storage for buf.
1646 else if (data
->size() < 1) // Attribute has no values.
1648 if (data
->format() == CSSM_DB_ATTRIBUTE_FORMAT_SINT32
1649 || data
->format() == CSSM_DB_ATTRIBUTE_FORMAT_UINT32
)
1651 length
= sizeof(zero
);
1654 else if (data
->format() == CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE
)
1655 length
= 0; // Should we throw here?
1656 else // All other formats
1659 else // Get the first value
1661 length
= (UInt32
)data
->Value
[0].Length
;
1662 buf
= data
->Value
[0].Data
;
1664 if (data
->format() == CSSM_DB_ATTRIBUTE_FORMAT_SINT32
)
1666 if (attr
.length
== sizeof(sint8
))
1668 length
= attr
.length
;
1669 svalue8
= sint8(*reinterpret_cast<const sint32
*>(buf
));
1672 else if (attr
.length
== sizeof(sint16
))
1674 length
= attr
.length
;
1675 svalue16
= sint16(*reinterpret_cast<const sint32
*>(buf
));
1679 else if (data
->format() == CSSM_DB_ATTRIBUTE_FORMAT_UINT32
)
1681 if (attr
.length
== sizeof(uint8
))
1683 length
= attr
.length
;
1684 uvalue8
= uint8(*reinterpret_cast<const uint32
*>(buf
));
1687 else if (attr
.length
== sizeof(uint16
))
1689 length
= attr
.length
;
1690 uvalue16
= uint16(*reinterpret_cast<const uint32
*>(buf
));
1694 else if (data
->format() == CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE
)
1696 if (attr
.length
== sizeof(uint32
))
1698 TimeStringToMacSeconds(data
->Value
[0], macSeconds
);
1700 length
= attr
.length
;
1702 else if (attr
.length
== sizeof(sint64
))
1704 TimeStringToMacLongDateTime(data
->Value
[0], macLDT
);
1706 length
= attr
.length
;
1712 *actualLength
= length
;
1716 if (attr
.length
< length
)
1717 MacOSError::throwMe(errSecBufferTooSmall
);
1719 memcpy(attr
.data
, buf
, length
);
1724 ItemImpl::getData(CssmDataContainer
& outData
)
1726 StLock
<Mutex
>_(mMutex
);
1729 CssmData
*data
= mData
.get();
1730 // If the data hasn't been set we can't return it.
1732 MacOSError::throwMe(errSecDataNotAvailable
);
1738 getContent(NULL
, &outData
);
1740 #if SENDACCESSNOTIFICATIONS
1741 secinfo("kcnotify", "ItemImpl::getData retrieved data");
1743 //%%%<might> be done elsewhere, but here is good for now
1744 KCEventNotifier::PostKeychainEvent(kSecDataAccessEvent
, mKeychain
, this);
1751 StLock
<Mutex
>_(mMutex
);
1755 Db
db(mKeychain
->database());
1756 if (useSecureStorage(db
))
1758 group
= safer_cast
<SSDbUniqueRecordImpl
&>(*dbUniqueRecord()).group();
1765 void ItemImpl::getLocalContent(SecKeychainAttributeList
*attributeList
, UInt32
*outLength
, void **outData
)
1767 StLock
<Mutex
>_(mMutex
);
1769 Allocator
&allocator
= Allocator::standard(); // @@@ This might not match the one used originally
1772 CssmData
*data
= mData
.get();
1774 MacOSError::throwMe(errSecDataNotAvailable
);
1776 // Copy the data out of our internal cached copy.
1777 UInt32 length
= (UInt32
)data
->Length
;
1778 *outData
= allocator
.malloc(length
);
1779 memcpy(*outData
, data
->Data
, length
);
1781 *outLength
= length
;
1786 if (!mDbAttributes
.get())
1787 MacOSError::throwMe(errSecDataNotAvailable
);
1789 // Pull attributes out of a "floating" item, i.e. one that isn't attached to a database
1790 for (UInt32 ix
= 0; ix
< attributeList
->count
; ++ix
)
1792 SecKeychainAttribute
&attribute
= attributeList
->attr
[ix
];
1793 CssmDbAttributeData
*data
= mDbAttributes
->find(Schema::attributeInfo(attribute
.tag
));
1794 if (data
&& data
->NumberOfValues
> 0)
1796 // Copy the data out of our internal cached copy.
1797 UInt32 length
= (UInt32
)data
->Value
[0].Length
;
1798 attribute
.data
= allocator
.malloc(length
);
1799 memcpy(attribute
.data
, data
->Value
[0].Data
, length
);
1800 attribute
.length
= length
;
1804 attribute
.length
= 0;
1805 attribute
.data
= NULL
;
1812 ItemImpl::getContent(DbAttributes
*dbAttributes
, CssmDataContainer
*itemData
)
1814 StLock
<Mutex
>_(mMutex
);
1817 Db
db(dbUniqueRecord()->database());
1820 dbUniqueRecord()->getWithoutEncryption (dbAttributes
, itemData
);
1823 if (useSecureStorage(db
))
1826 if(!checkIntegrity()) {
1827 secnotice("integrity", "item has no integrity, denying access");
1828 CssmError::throwMe(errSecInvalidItemRef
);
1830 } catch(CssmError cssme
) {
1831 secnotice("integrity", "error while checking integrity, denying access: %s", cssme
.what());
1835 SSDbUniqueRecordImpl
* impl
= dynamic_cast<SSDbUniqueRecordImpl
*>(&(*dbUniqueRecord()));
1838 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER
);
1841 SSDbUniqueRecord
ssUniqueId(impl
);
1842 const AccessCredentials
*autoPrompt
= globals().itemCredentials();
1843 ssUniqueId
->get(dbAttributes
, itemData
, autoPrompt
);
1848 dbUniqueRecord()->get(dbAttributes
, itemData
);
1852 ItemImpl::useSecureStorage(const Db
&db
)
1854 StLock
<Mutex
>_(mMutex
);
1855 switch (recordType())
1857 case CSSM_DL_DB_RECORD_GENERIC_PASSWORD
:
1858 case CSSM_DL_DB_RECORD_INTERNET_PASSWORD
:
1859 case CSSM_DL_DB_RECORD_APPLESHARE_PASSWORD
:
1860 if (db
->dl()->subserviceMask() & CSSM_SERVICE_CSP
)
1869 void ItemImpl::willRead()
1873 Item
ItemImpl::makeFromPersistentReference(const CFDataRef persistentRef
, bool *isIdentityRef
)
1875 CssmData
dictData((void*)::CFDataGetBytePtr(persistentRef
), ::CFDataGetLength(persistentRef
));
1876 NameValueDictionary
dict(dictData
);
1879 Item item
= (ItemImpl
*) NULL
;
1881 if (isIdentityRef
) {
1882 *isIdentityRef
= (dict
.FindByName(IDENTITY_KEY
) != 0) ? true : false;
1885 // make sure we have a database identifier
1886 if (dict
.FindByName(SSUID_KEY
) != 0)
1888 DLDbIdentifier dlDbIdentifier
= NameValueDictionary::MakeDLDbIdentifierFromNameValueDictionary(dict
);
1889 DLDbIdentifier
newDlDbIdentifier(dlDbIdentifier
.ssuid(),
1890 DLDbListCFPref::ExpandTildesInPath(dlDbIdentifier
.dbName()).c_str(),
1891 dlDbIdentifier
.dbLocation());
1893 keychain
= globals().storageManager
.keychain(newDlDbIdentifier
);
1895 const NameValuePair
* aDictItem
= dict
.FindByName(ITEM_KEY
);
1896 if (aDictItem
&& keychain
)
1898 PrimaryKey
primaryKey(aDictItem
->Value());
1899 item
= keychain
->item(primaryKey
);
1902 KCThrowIf_( !item
, errSecItemNotFound
);
1906 void ItemImpl::copyPersistentReference(CFDataRef
&outDataRef
, bool isSecIdentityRef
)
1908 if (secd_PersistentRef
) {
1909 outDataRef
= secd_PersistentRef
;
1912 StLock
<Mutex
>_(mMutex
);
1913 // item must be in a keychain and have a primary key to be persistent
1914 if (!mKeychain
|| !mPrimaryKey
) {
1915 MacOSError::throwMe(errSecItemNotFound
);
1917 DLDbIdentifier dlDbIdentifier
= mKeychain
->dlDbIdentifier();
1918 DLDbIdentifier
newDlDbIdentifier(dlDbIdentifier
.ssuid(),
1919 DLDbListCFPref::AbbreviatedPath(mKeychain
->name()).c_str(),
1920 dlDbIdentifier
.dbLocation());
1921 NameValueDictionary dict
;
1922 NameValueDictionary::MakeNameValueDictionaryFromDLDbIdentifier(newDlDbIdentifier
, dict
);
1924 CssmData
* pKey
= mPrimaryKey
;
1925 dict
.Insert (new NameValuePair(ITEM_KEY
, *pKey
));
1927 if (isSecIdentityRef
) {
1928 uint32_t value
= -1;
1929 CssmData
valueData((void*)&value
, sizeof(value
));
1930 dict
.Insert (new NameValuePair(IDENTITY_KEY
, valueData
));
1933 // flatten the NameValueDictionary
1935 dict
.Export(dictData
);
1936 outDataRef
= ::CFDataCreate(kCFAllocatorDefault
, dictData
.Data
, dictData
.Length
);
1937 free (dictData
.Data
);
1940 void ItemImpl::copyRecordIdentifier(CSSM_DATA
&data
)
1942 StLock
<Mutex
>_(mMutex
);
1943 CssmClient::DbUniqueRecord uniqueRecord
= dbUniqueRecord ();
1944 uniqueRecord
->getRecordIdentifier(data
);
1948 * Obtain blob used to bind a keychain item to an Extended Attribute record.
1949 * We just use the PrimaryKey blob as the default. Note that for standard Items,
1950 * this can cause the loss of extended attribute bindings if a Primary Key
1951 * attribute changes.
1953 const CssmData
&ItemImpl::itemID()
1955 StLock
<Mutex
>_(mMutex
);
1956 if(mPrimaryKey
->length() == 0) {
1957 /* not in a keychain; we don't have a primary key */
1958 MacOSError::throwMe(errSecNoSuchAttr
);
1960 return *mPrimaryKey
;
1963 bool ItemImpl::equal(SecCFObject
&other
)
1965 // First check to see if both items have a primary key and
1966 // if the primary key is the same. If so then these
1967 // items must be equal
1968 ItemImpl
& other_item
= (ItemImpl
&)other
;
1969 if (mPrimaryKey
!= NULL
&& mPrimaryKey
== other_item
.mPrimaryKey
)
1974 // The primary keys do not match so do a CFHash of the
1975 // data of the item and compare those for equality
1976 CFHashCode this_hash
= hash();
1977 CFHashCode other_hash
= other
.hash();
1978 return (this_hash
== other_hash
);
1981 CFHashCode
ItemImpl::hash()
1983 CFHashCode result
= SecCFObject::hash();
1985 StLock
<Mutex
>_(mMutex
);
1986 RefPointer
<CssmDataContainer
> data_to_hash
;
1988 // Use the item data for the hash
1989 if (mData
&& *mData
)
1991 data_to_hash
= mData
;
1994 // If there is no primary key AND not data ????
1995 // just return the 'old' hash value which is the
1997 if (NULL
!= data_to_hash
.get())
1999 CFDataRef temp_data
= NULL
;
2000 unsigned char digest
[CC_SHA256_DIGEST_LENGTH
];
2002 if (data_to_hash
->length() < 80)
2004 // If it is less than 80 bytes then CFData can be used
2005 temp_data
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
,
2006 (const UInt8
*)data_to_hash
->data(), data_to_hash
->length(), kCFAllocatorNull
);
2009 // CFData truncates its hash value to 80 bytes. ????
2010 // In order to do the 'right thing' a SHA 256 hash will be used to
2011 // include all of the data
2014 memset(digest
, 0, CC_SHA256_DIGEST_LENGTH
);
2016 CC_SHA256((const void *)data_to_hash
->data(), (CC_LONG
)data_to_hash
->length(), digest
);
2018 temp_data
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
,
2019 (const UInt8
*)digest
, CC_SHA256_DIGEST_LENGTH
, kCFAllocatorNull
);
2022 if (NULL
!= temp_data
)
2024 result
= CFHash(temp_data
);
2025 CFRelease(temp_data
);
2034 void ItemImpl::postItemEvent(SecKeychainEvent theEvent
)
2036 mKeychain
->postEvent(theEvent
, this);
2042 // Item -- This class is here to magically create the right subclass of ItemImpl
2043 // when constructing new items.
2049 Item::Item(ItemImpl
*impl
) : SecPointer
<ItemImpl
>(impl
)
2053 Item::Item(SecItemClass itemClass
, OSType itemCreator
, UInt32 length
, const void* data
, bool inhibitCheck
)
2057 if (itemClass
== CSSM_DL_DB_RECORD_X509_CERTIFICATE
2058 || itemClass
== CSSM_DL_DB_RECORD_PUBLIC_KEY
2059 || itemClass
== CSSM_DL_DB_RECORD_PRIVATE_KEY
2060 || itemClass
== CSSM_DL_DB_RECORD_SYMMETRIC_KEY
)
2061 MacOSError::throwMe(errSecNoSuchClass
); /* @@@ errSecInvalidClass */
2064 *this = new ItemImpl(itemClass
, itemCreator
, length
, data
, inhibitCheck
);
2067 Item::Item(SecItemClass itemClass
, SecKeychainAttributeList
*attrList
, UInt32 length
, const void* data
)
2069 *this = new ItemImpl(itemClass
, attrList
, length
, data
);
2072 Item::Item(const Keychain
&keychain
, const PrimaryKey
&primaryKey
, const CssmClient::DbUniqueRecord
&uniqueId
)
2073 : SecPointer
<ItemImpl
>(
2074 primaryKey
->recordType() == CSSM_DL_DB_RECORD_X509_CERTIFICATE
2075 ? Certificate::make(keychain
, primaryKey
, uniqueId
)
2076 : (primaryKey
->recordType() == CSSM_DL_DB_RECORD_PUBLIC_KEY
2077 || primaryKey
->recordType() == CSSM_DL_DB_RECORD_PRIVATE_KEY
2078 || primaryKey
->recordType() == CSSM_DL_DB_RECORD_SYMMETRIC_KEY
)
2079 ? KeyItem::make(keychain
, primaryKey
, uniqueId
)
2080 : primaryKey
->recordType() == CSSM_DL_DB_RECORD_EXTENDED_ATTRIBUTE
2081 ? ExtendedAttribute::make(keychain
, primaryKey
, uniqueId
)
2082 : ItemImpl::make(keychain
, primaryKey
, uniqueId
))
2086 Item::Item(const Keychain
&keychain
, const PrimaryKey
&primaryKey
)
2087 : SecPointer
<ItemImpl
>(
2088 primaryKey
->recordType() == CSSM_DL_DB_RECORD_X509_CERTIFICATE
2089 ? Certificate::make(keychain
, primaryKey
)
2090 : (primaryKey
->recordType() == CSSM_DL_DB_RECORD_PUBLIC_KEY
2091 || primaryKey
->recordType() == CSSM_DL_DB_RECORD_PRIVATE_KEY
2092 || primaryKey
->recordType() == CSSM_DL_DB_RECORD_SYMMETRIC_KEY
)
2093 ? KeyItem::make(keychain
, primaryKey
)
2094 : primaryKey
->recordType() == CSSM_DL_DB_RECORD_EXTENDED_ATTRIBUTE
2095 ? ExtendedAttribute::make(keychain
, primaryKey
)
2096 : ItemImpl::make(keychain
, primaryKey
))
2100 Item::Item(ItemImpl
&item
)
2101 : SecPointer
<ItemImpl
>(
2102 item
.recordType() == CSSM_DL_DB_RECORD_X509_CERTIFICATE
2103 ? new Certificate(safer_cast
<Certificate
&>(item
))
2104 : (item
.recordType() == CSSM_DL_DB_RECORD_PUBLIC_KEY
2105 || item
.recordType() == CSSM_DL_DB_RECORD_PRIVATE_KEY
2106 || item
.recordType() == CSSM_DL_DB_RECORD_SYMMETRIC_KEY
)
2107 ? new KeyItem(safer_cast
<KeyItem
&>(item
))
2108 : item
.recordType() == CSSM_DL_DB_RECORD_EXTENDED_ATTRIBUTE
2109 ? new ExtendedAttribute(safer_cast
<ExtendedAttribute
&>(item
))
2110 : new ItemImpl(item
))
2114 CFIndex
KeychainCore::GetItemRetainCount(Item
& item
)
2116 return CFGetRetainCount(item
->handle(false));
2119 void ItemImpl::setPersistentRef(CFDataRef ref
)
2121 if (secd_PersistentRef
) {
2122 CFRelease(secd_PersistentRef
);
2124 secd_PersistentRef
= ref
;
2128 CFDataRef
ItemImpl::getPersistentRef()
2130 return secd_PersistentRef
;
2135 bool ItemImpl::mayDelete()
2137 ObjectImpl
* uniqueIDImpl
= mUniqueId
.get();
2139 if (uniqueIDImpl
!= NULL
)
2141 bool result
= mUniqueId
->isIdle();