2 * Copyright (c) 2000-2004 Apple Computer, 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 "cssmdatetime.h"
39 #include <security_cdsa_client/keychainacl.h>
40 #include <security_utilities/osxcode.h>
41 #include <security_utilities/trackingallocator.h>
42 #include <Security/SecKeychainItemPriv.h>
43 #include <Security/cssmapple.h>
45 #define SENDACCESSNOTIFICATIONS 1
47 //%%% schema indexes should be defined in Schema.h
48 #define APPLEDB_CSSM_PRINTNAME_ATTRIBUTE 1 /* schema index for label attribute of keys or certificates */
49 #define APPLEDB_GENERIC_PRINTNAME_ATTRIBUTE 7 /* schema index for label attribute of password items */
50 #define IS_PASSWORD_ITEM_CLASS(X) ( (X) == kSecInternetPasswordItemClass || \
51 (X) == kSecGenericPasswordItemClass || \
52 (X) == kSecAppleSharePasswordItemClass ) ? 1 : 0
54 using namespace KeychainCore
;
55 using namespace CSSMDateTimeUtils
;
61 // NewItemImpl constructor
62 ItemImpl::ItemImpl(SecItemClass itemClass
, OSType itemCreator
, UInt32 length
, const void* data
, bool dontDoAttributes
)
63 : mDbAttributes(new DbAttributes()),
67 mMutex(Mutex::recursive
)
70 mData
= new CssmDataContainer(data
, length
);
72 mDbAttributes
->recordType(Schema::recordTypeFor(itemClass
));
75 mDbAttributes
->add(Schema::attributeInfo(kSecCreatorItemAttr
), itemCreator
);
78 ItemImpl::ItemImpl(SecItemClass itemClass
, SecKeychainAttributeList
*attrList
, UInt32 length
, const void* data
)
79 : mDbAttributes(new DbAttributes()),
83 mMutex(Mutex::recursive
)
86 mData
= new CssmDataContainer(data
, length
);
89 mDbAttributes
->recordType(Schema::recordTypeFor(itemClass
));
93 for(UInt32 i
=0; i
< attrList
->count
; i
++)
95 mDbAttributes
->add(Schema::attributeInfo(attrList
->attr
[i
].tag
), CssmData(attrList
->attr
[i
].data
, attrList
->attr
[i
].length
));
100 // DbItemImpl constructor
101 ItemImpl::ItemImpl(const Keychain
&keychain
, const PrimaryKey
&primaryKey
, const DbUniqueRecord
&uniqueId
)
102 : mUniqueId(uniqueId
), mKeychain(keychain
), mPrimaryKey(primaryKey
),
103 mDoNotEncrypt(false), mInCache(false),
104 mMutex(Mutex::recursive
)
108 // PrimaryKey ItemImpl constructor
109 ItemImpl::ItemImpl(const Keychain
&keychain
, const PrimaryKey
&primaryKey
)
110 : mKeychain(keychain
), mPrimaryKey(primaryKey
), mDoNotEncrypt(false),
112 mMutex(Mutex::recursive
)
116 ItemImpl
* ItemImpl::make(const Keychain
&keychain
, const PrimaryKey
&primaryKey
, const CssmClient::DbUniqueRecord
&uniqueId
)
118 ItemImpl
* ii
= new ItemImpl(keychain
, primaryKey
, uniqueId
);
119 keychain
->addItem(primaryKey
, ii
);
125 ItemImpl
* ItemImpl::make(const Keychain
&keychain
, const PrimaryKey
&primaryKey
)
127 ItemImpl
* ii
= new ItemImpl(keychain
, primaryKey
);
128 keychain
->addItem(primaryKey
, ii
);
134 // Constructor used when copying an item to a keychain.
136 ItemImpl::ItemImpl(ItemImpl
&item
) :
137 mData(item
.modifiedData() ? NULL
: new CssmDataContainer()),
138 mDbAttributes(new DbAttributes()),
140 mDoNotEncrypt(false),
142 mMutex(Mutex::recursive
)
144 mDbAttributes
->recordType(item
.recordType());
145 CSSM_DB_RECORD_ATTRIBUTE_INFO
*schemaAttributes
= NULL
;
147 if (item
.mKeychain
) {
148 // get the entire source item from its keychain. This requires figuring
149 // out the schema for the item based on its record type.
151 for (uint32 i
= 0; i
< Schema::DBInfo
.NumberOfRecordTypes
; i
++)
152 if (item
.recordType() == Schema::DBInfo
.RecordAttributeNames
[i
].DataRecordType
) {
153 schemaAttributes
= &Schema::DBInfo
.RecordAttributeNames
[i
];
157 if (schemaAttributes
== NULL
)
158 // the source item is invalid
159 MacOSError::throwMe(errSecInvalidItemRef
);
161 for (uint32 i
= 0; i
< schemaAttributes
->NumberOfAttributes
; i
++)
162 mDbAttributes
->add(schemaAttributes
->AttributeInfo
[i
]);
164 item
.getContent(mDbAttributes
.get(), mData
.get());
167 // @@@ We don't deal with modified attributes.
169 if (item
.modifiedData())
170 // the copied data comes from the source item
171 mData
= new CssmDataContainer(item
.modifiedData()->Data
,
172 item
.modifiedData()->Length
);
175 ItemImpl::~ItemImpl()
182 ItemImpl::getMutexForObject()
186 return mKeychain
->getKeychainMutex();
195 ItemImpl::aboutToDestruct()
197 if (mKeychain
&& *mPrimaryKey
)
199 mKeychain
->removeItem(mPrimaryKey
, this);
206 ItemImpl::didModify()
208 StLock
<Mutex
>_(mMutex
);
210 mDbAttributes
.reset(NULL
);
214 ItemImpl::defaultAttributeValue(const CSSM_DB_ATTRIBUTE_INFO
&info
)
216 static const uint32 zeroInt
= 0;
217 static const double zeroDouble
= 0.0;
218 static const char timeBytes
[] = "20010101000000Z";
220 static const CSSM_DATA defaultFourBytes
= { 4, (uint8
*) &zeroInt
};
221 static const CSSM_DATA defaultEightBytes
= { 8, (uint8
*) &zeroDouble
};
222 static const CSSM_DATA defaultTime
= { 16, (uint8
*) timeBytes
};
223 static const CSSM_DATA defaultZeroBytes
= { 0, NULL
};
225 switch (info
.AttributeFormat
)
227 case CSSM_DB_ATTRIBUTE_FORMAT_SINT32
:
228 case CSSM_DB_ATTRIBUTE_FORMAT_UINT32
:
229 return defaultFourBytes
;
231 case CSSM_DB_ATTRIBUTE_FORMAT_REAL
:
232 return defaultEightBytes
;
234 case CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE
:
238 return defaultZeroBytes
;
244 PrimaryKey
ItemImpl::addWithCopyInfo (Keychain
&keychain
, bool isCopy
)
246 StLock
<Mutex
>_(mMutex
);
247 // If we already have a Keychain we can't be added.
249 MacOSError::throwMe(errSecDuplicateItem
);
251 // If we don't have any attributes we can't be added.
252 // (this might occur if attempting to add the item twice, since our attributes
253 // and data are set to NULL at the end of this function.)
254 if (!mDbAttributes
.get())
255 MacOSError::throwMe(errSecDuplicateItem
);
257 CSSM_DB_RECORDTYPE recordType
= mDbAttributes
->recordType();
259 // update the creation and update dates on the new item
262 KeychainSchema schema
= keychain
->keychainSchema();
264 GetCurrentMacLongDateTime(date
);
265 if (schema
->hasAttribute(recordType
, kSecCreationDateItemAttr
))
267 setAttribute(schema
->attributeInfoFor(recordType
, kSecCreationDateItemAttr
), date
);
270 if (schema
->hasAttribute(recordType
, kSecModDateItemAttr
))
272 setAttribute(schema
->attributeInfoFor(recordType
, kSecModDateItemAttr
), date
);
276 // If the label (PrintName) attribute isn't specified, set a default label.
277 if (!mDoNotEncrypt
&& !mDbAttributes
->find(Schema::attributeInfo(kSecLabelItemAttr
)))
279 // if doNotEncrypt was set all of the attributes are wrapped in the data blob. Don't calculate here.
280 CssmDbAttributeData
*label
= NULL
;
283 case CSSM_DL_DB_RECORD_GENERIC_PASSWORD
:
284 label
= mDbAttributes
->find(Schema::attributeInfo(kSecServiceItemAttr
));
287 case CSSM_DL_DB_RECORD_APPLESHARE_PASSWORD
:
288 case CSSM_DL_DB_RECORD_INTERNET_PASSWORD
:
289 label
= mDbAttributes
->find(Schema::attributeInfo(kSecServerItemAttr
));
290 // if AppleShare server name wasn't specified, try the server address
291 if (!label
) label
= mDbAttributes
->find(Schema::attributeInfo(kSecAddressItemAttr
));
297 // if all else fails, use the account name.
299 label
= mDbAttributes
->find(Schema::attributeInfo(kSecAccountItemAttr
));
301 if (label
&& label
->size())
302 setAttribute (Schema::attributeInfo(kSecLabelItemAttr
), label
->at
<CssmData
>(0));
305 // get the attributes that are part of the primary key
306 const CssmAutoDbRecordAttributeInfo
&primaryKeyInfos
=
307 keychain
->primaryKeyInfosFor(recordType
);
309 // make sure each primary key element has a value in the item, otherwise
310 // the database will complain. we make a set of the provided attribute infos
311 // to avoid O(N^2) behavior.
313 DbAttributes
*attributes
= mDbAttributes
.get();
314 typedef set
<CssmDbAttributeInfo
> InfoSet
;
319 // make a set of all the attributes in the key
320 for (uint32 i
= 0; i
< attributes
->size(); i
++)
321 infoSet
.insert(attributes
->at(i
).Info
);
323 for (uint32 i
= 0; i
< primaryKeyInfos
.size(); i
++) { // check to make sure all required attributes are in the key
324 InfoSet::const_iterator it
= infoSet
.find(primaryKeyInfos
.at(i
));
326 if (it
== infoSet
.end()) { // not in the key? add the default
327 // we need to add a default value to the item attributes
328 attributes
->add(primaryKeyInfos
.at(i
), defaultAttributeValue(primaryKeyInfos
.at(i
)));
333 Db
db(keychain
->database());
336 mUniqueId
= db
->insertWithoutEncryption (recordType
, NULL
, mData
.get());
338 else if (useSecureStorage(db
))
340 // Add the item to the secure storage db
341 SSDbImpl
* impl
= dynamic_cast<SSDbImpl
*>(&(*db
));
344 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER
);
349 TrackingAllocator
allocator(Allocator::standard());
351 // hhs replaced with the new aclFactory class
352 AclFactory aclFactory
;
353 const AccessCredentials
*nullCred
= aclFactory
.nullCred();
355 SecPointer
<Access
> access
= mAccess
;
357 // create default access controls for the new item
358 CssmDbAttributeData
*data
= mDbAttributes
->find(Schema::attributeInfo(kSecLabelItemAttr
));
359 string printName
= data
? CssmData::overlay(data
->Value
[0]).toString() : "keychain item";
360 access
= new Access(printName
);
362 // special case for "iTools" password - allow anyone to decrypt the item
363 if (recordType
== CSSM_DL_DB_RECORD_GENERIC_PASSWORD
)
365 CssmDbAttributeData
*data
= mDbAttributes
->find(Schema::attributeInfo(kSecServiceItemAttr
));
366 if (data
&& data
->Value
[0].Length
== 6 && !memcmp("iTools", data
->Value
[0].Data
, 6))
368 typedef vector
<SecPointer
<ACL
> > AclSet
;
370 access
->findAclsForRight(CSSM_ACL_AUTHORIZATION_DECRYPT
, acls
);
371 for (AclSet::const_iterator it
= acls
.begin(); it
!= acls
.end(); it
++)
372 (*it
)->form(ACL::allowAllForm
);
377 // Get the handle of the DL underlying this CSPDL.
378 CSSM_DL_DB_HANDLE dldbh
;
379 db
->passThrough(CSSM_APPLECSPDL_DB_GET_HANDLE
, NULL
,
380 reinterpret_cast<void **>(&dldbh
));
382 // Turn off autocommit on the underlying DL and remember the old state.
383 CSSM_BOOL autoCommit
= CSSM_TRUE
;
384 ObjectImpl::check(CSSM_DL_PassThrough(dldbh
,
385 CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT
,
386 0, reinterpret_cast<void **>(&autoCommit
)));
390 // Create a new SSGroup with temporary access controls
392 ResourceControlContext prototype
;
393 maker
.initialOwner(prototype
, nullCred
);
394 SSGroup
ssGroup(ssDb
, &prototype
);
398 // Insert the record using the newly created group.
399 mUniqueId
= ssDb
->insert(recordType
, mDbAttributes
.get(),
400 mData
.get(), ssGroup
, maker
.cred());
404 ssGroup
->deleteKey(nullCred
);
408 // now finalize the access controls on the group
409 access
->setAccess(*ssGroup
, maker
);
410 mAccess
= NULL
; // use them and lose them
413 // autoCommit was on so commit now that we are done and turn
415 ObjectImpl::check(CSSM_DL_PassThrough(dldbh
,
416 CSSM_APPLEFILEDL_COMMIT
, NULL
, NULL
));
417 CSSM_DL_PassThrough(dldbh
, CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT
,
418 reinterpret_cast<const void *>(autoCommit
), NULL
);
425 // autoCommit was off so rollback since we failed and turn
426 // autoCommit back on.
427 CSSM_DL_PassThrough(dldbh
, CSSM_APPLEFILEDL_ROLLBACK
, NULL
, NULL
);
428 CSSM_DL_PassThrough(dldbh
, CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT
,
429 reinterpret_cast<const void *>(autoCommit
), NULL
);
436 // add the item to the (regular) db
437 mUniqueId
= db
->insert(recordType
, mDbAttributes
.get(), mData
.get());
440 mPrimaryKey
= keychain
->makePrimaryKey(recordType
, mUniqueId
);
441 mKeychain
= keychain
;
443 // Forget our data and attributes.
445 mDbAttributes
.reset(NULL
);
453 ItemImpl::add (Keychain
&keychain
)
455 return addWithCopyInfo (keychain
, false);
461 ItemImpl::copyTo(const Keychain
&keychain
, Access
*newAccess
)
463 StLock
<Mutex
>_(mMutex
);
466 item
->setAccess(newAccess
);
469 /* Attempt to copy the access from the current item to the newly created one. */
470 SSGroup myGroup
= group();
473 SecPointer
<Access
> access
= new Access(*myGroup
);
474 item
->setAccess(access
);
478 keychain
->addCopy(item
);
485 StLock
<Mutex
>_(mMutex
);
487 MacOSError::throwMe(errSecNoSuchKeychain
);
489 // Don't update if nothing changed.
493 CSSM_DB_RECORDTYPE aRecordType
= recordType();
494 KeychainSchema schema
= mKeychain
->keychainSchema();
496 // Update the modification date on the item if there is a mod date attribute.
497 if (schema
->hasAttribute(aRecordType
, kSecModDateItemAttr
))
500 GetCurrentMacLongDateTime(date
);
501 setAttribute(schema
->attributeInfoFor(aRecordType
, kSecModDateItemAttr
), date
);
504 // Make sure that we have mUniqueId
506 Db
db(mUniqueId
->database());
509 CSSM_DB_RECORD_ATTRIBUTE_DATA attrData
;
510 memset (&attrData
, 0, sizeof (attrData
));
511 attrData
.DataRecordType
= aRecordType
;
513 mUniqueId
->modifyWithoutEncryption(aRecordType
,
516 CSSM_DB_MODIFY_ATTRIBUTE_REPLACE
);
518 else if (useSecureStorage(db
))
520 // Add the item to the secure storage db
521 SSDbUniqueRecordImpl
* impl
= dynamic_cast<SSDbUniqueRecordImpl
*>(&(*mUniqueId
));
524 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER
);
527 SSDbUniqueRecord
ssUniqueId(impl
);
529 // @@@ Share this instance
530 const AccessCredentials
*autoPrompt
= globals().itemCredentials();
533 // Only call this is user interaction is enabled.
534 ssUniqueId
->modify(aRecordType
,
537 CSSM_DB_MODIFY_ATTRIBUTE_REPLACE
,
542 mUniqueId
->modify(aRecordType
,
545 CSSM_DB_MODIFY_ATTRIBUTE_REPLACE
);
550 PrimaryKey oldPK
= mPrimaryKey
;
551 mPrimaryKey
= mKeychain
->makePrimaryKey(aRecordType
, mUniqueId
);
553 // Forget our data and attributes.
555 mDbAttributes
.reset(NULL
);
557 // Let the Keychain update what it needs to.
558 mKeychain
->didUpdate(this, oldPK
, mPrimaryKey
);
563 ItemImpl::getClass(SecKeychainAttribute
&attr
, UInt32
*actualLength
)
565 StLock
<Mutex
>_(mMutex
);
567 *actualLength
= sizeof(SecItemClass
);
569 if (attr
.length
< sizeof(SecItemClass
))
570 MacOSError::throwMe(errSecBufferTooSmall
);
572 SecItemClass aClass
= Schema::itemClassFor(recordType());
573 memcpy(attr
.data
, &aClass
, sizeof(SecItemClass
));
577 ItemImpl::setAttribute(SecKeychainAttribute
& attr
)
579 StLock
<Mutex
>_(mMutex
);
580 setAttribute(Schema::attributeInfo(attr
.tag
), CssmData(attr
.data
, attr
.length
));
584 ItemImpl::recordType()
586 StLock
<Mutex
>_(mMutex
);
587 if (mDbAttributes
.get())
588 return mDbAttributes
->recordType();
590 return mPrimaryKey
->recordType();
594 ItemImpl::modifiedAttributes()
596 StLock
<Mutex
>_(mMutex
);
597 return mDbAttributes
.get();
601 ItemImpl::modifiedData()
603 StLock
<Mutex
>_(mMutex
);
608 ItemImpl::setData(UInt32 length
,const void *data
)
610 StLock
<Mutex
>_(mMutex
);
611 mData
= new CssmDataContainer(data
, length
);
615 ItemImpl::setAccess(Access
*newAccess
)
617 StLock
<Mutex
>_(mMutex
);
621 CssmClient::DbUniqueRecord
622 ItemImpl::dbUniqueRecord()
624 StLock
<Mutex
>_(mMutex
);
625 if (!isPersistent()) // is there no database attached?
627 MacOSError::throwMe(errSecNotAvailable
);
632 DbCursor
cursor(mPrimaryKey
->createCursor(mKeychain
));
633 if (!cursor
->next(NULL
, NULL
, mUniqueId
))
634 MacOSError::throwMe(errSecInvalidItemRef
);
641 ItemImpl::primaryKey()
647 ItemImpl::isPersistent()
653 ItemImpl::isModified()
655 StLock
<Mutex
>_(mMutex
);
656 return mData
.get() || mDbAttributes
.get();
666 ItemImpl::operator < (const ItemImpl
&other
)
671 return this < &other
;
674 return mPrimaryKey
< other
.mPrimaryKey
;
678 ItemImpl::setAttribute(const CssmDbAttributeInfo
&info
, const CssmPolyData
&data
)
680 StLock
<Mutex
>_(mMutex
);
681 if (!mDbAttributes
.get())
683 mDbAttributes
.reset(new DbAttributes());
684 mDbAttributes
->recordType(mPrimaryKey
->recordType());
687 uint32 length
= data
.Length
;
688 const void *buf
= reinterpret_cast<const void *>(data
.Data
);
689 uint8 timeString
[16];
691 // XXX This code is duplicated in KCCursorImpl::KCCursorImpl()
692 // Convert a 4 or 8 byte TIME_DATE to a CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE
693 // style attribute value.
694 if (info
.format() == CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE
)
696 if (length
== sizeof(UInt32
))
698 MacSecondsToTimeString(*reinterpret_cast<const UInt32
*>(buf
), 16, &timeString
);
702 else if (length
== sizeof(SInt64
))
704 MacLongDateTimeToTimeString(*reinterpret_cast<const SInt64
*>(buf
), 16, &timeString
);
710 mDbAttributes
->add(info
, CssmData(const_cast<void*>(buf
), length
));
714 ItemImpl::modifyContent(const SecKeychainAttributeList
*attrList
, UInt32 dataLength
, const void *inData
)
716 StLock
<Mutex
>_(mMutex
);
717 if (!mDbAttributes
.get())
719 mDbAttributes
.reset(new DbAttributes());
720 mDbAttributes
->recordType(mPrimaryKey
->recordType());
723 if(attrList
) // optional
725 for(UInt32 ix
=0; ix
< attrList
->count
; ix
++)
727 SecKeychainAttrType attrTag
= attrList
->attr
[ix
].tag
;
729 if (attrTag
== APPLEDB_CSSM_PRINTNAME_ATTRIBUTE
)
731 // must remap a caller-supplied kSecKeyPrintName attribute tag for key items, since it isn't in the schema
732 // (note that this will ultimately match kGenericPrintName in Schema.cpp)
733 attrTag
= kSecLabelItemAttr
;
736 mDbAttributes
->add(Schema::attributeInfo(attrTag
), CssmData(attrList
->attr
[ix
].data
, attrList
->attr
[ix
].length
));
742 mData
= new CssmDataContainer(inData
, dataLength
);
749 ItemImpl::getContent(SecItemClass
*itemClass
, SecKeychainAttributeList
*attrList
, UInt32
*length
, void **outData
)
751 StLock
<Mutex
>_(mMutex
);
752 // If the data hasn't been set we can't return it.
753 if (!mKeychain
&& outData
)
755 CssmData
*data
= mData
.get();
757 MacOSError::throwMe(errSecDataNotAvailable
);
759 // TODO: need to check and make sure attrs are valid and handle error condition
763 *itemClass
= Schema::itemClassFor(recordType());
765 bool getDataFromDatabase
= mKeychain
&& mPrimaryKey
;
766 if (getDataFromDatabase
) // are we attached to a database?
770 // get the number of attributes requested by the caller
771 UInt32 attrCount
= attrList
? attrList
->count
: 0;
773 // make a DBAttributes structure and populate it
774 DbAttributes
dbAttributes(mUniqueId
->database(), attrCount
);
775 for (UInt32 ix
= 0; ix
< attrCount
; ++ix
)
777 dbAttributes
.add(Schema::attributeInfo(attrList
->attr
[ix
].tag
));
780 // request the data from the database (since we are a reference "item" and the data is really stored there)
781 CssmDataContainer itemData
;
782 getContent(&dbAttributes
, outData
? &itemData
: NULL
);
784 // retrieve the data from result
785 for (UInt32 ix
= 0; ix
< attrCount
; ++ix
)
787 if (dbAttributes
.at(ix
).NumberOfValues
> 0)
789 attrList
->attr
[ix
].data
= dbAttributes
.at(ix
).Value
[0].Data
;
790 attrList
->attr
[ix
].length
= dbAttributes
.at(ix
).Value
[0].Length
;
792 // We don't want the data released, it is up the client
793 dbAttributes
.at(ix
).Value
[0].Data
= NULL
;
794 dbAttributes
.at(ix
).Value
[0].Length
= 0;
798 attrList
->attr
[ix
].data
= NULL
;
799 attrList
->attr
[ix
].length
= 0;
806 *outData
=itemData
.data();
807 itemData
.Data
= NULL
;
810 *length
=itemData
.length();
816 getLocalContent(attrList
, length
, outData
);
819 // Inform anyone interested that we are doing this
820 #if SENDACCESSNOTIFICATIONS
823 secdebug("kcnotify", "ItemImpl::getContent(%p, %p, %p, %p) retrieved content",
824 itemClass
, attrList
, length
, outData
);
826 KCEventNotifier::PostKeychainEvent(kSecDataAccessEvent
, mKeychain
, this);
832 ItemImpl::freeContent(SecKeychainAttributeList
*attrList
, void *data
)
834 Allocator
&allocator
= Allocator::standard(); // @@@ This might not match the one used originally
836 allocator
.free(data
);
838 UInt32 attrCount
= attrList
? attrList
->count
: 0;
839 for (UInt32 ix
= 0; ix
< attrCount
; ++ix
)
841 allocator
.free(attrList
->attr
[ix
].data
);
842 attrList
->attr
[ix
].data
= NULL
;
847 ItemImpl::modifyAttributesAndData(const SecKeychainAttributeList
*attrList
, UInt32 dataLength
, const void *inData
)
849 StLock
<Mutex
>_(mMutex
);
851 MacOSError::throwMe(errSecNoSuchKeychain
);
855 if (!mDbAttributes
.get())
857 mDbAttributes
.reset(new DbAttributes());
858 mDbAttributes
->recordType(mPrimaryKey
->recordType());
861 CSSM_DB_RECORDTYPE recordType
= mDbAttributes
->recordType();
862 UInt32 attrCount
= attrList
? attrList
->count
: 0;
863 for (UInt32 ix
= 0; ix
< attrCount
; ix
++)
865 SecKeychainAttrType attrTag
= attrList
->attr
[ix
].tag
;
867 if (attrTag
== kSecLabelItemAttr
)
869 // must remap a caller-supplied label attribute tag for password items, since it isn't in the schema
870 // (note that this will ultimately match kGenericPrintName in Schema.cpp)
871 if (IS_PASSWORD_ITEM_CLASS( Schema::itemClassFor(recordType
) ))
872 attrTag
= APPLEDB_GENERIC_PRINTNAME_ATTRIBUTE
;
875 CssmDbAttributeInfo info
=mKeychain
->attributeInfoFor(recordType
, attrTag
);
877 if (attrList
->attr
[ix
].length
|| info
.AttributeFormat
==CSSM_DB_ATTRIBUTE_FORMAT_STRING
|| info
.AttributeFormat
==CSSM_DB_ATTRIBUTE_FORMAT_BLOB
878 || info
.AttributeFormat
==CSSM_DB_ATTRIBUTE_FORMAT_STRING
|| info
.AttributeFormat
==CSSM_DB_ATTRIBUTE_FORMAT_BIG_NUM
879 || info
.AttributeFormat
==CSSM_DB_ATTRIBUTE_FORMAT_MULTI_UINT32
)
880 mDbAttributes
->add(info
, CssmData(attrList
->attr
[ix
].data
, attrList
->attr
[ix
].length
));
882 mDbAttributes
->add(info
);
888 mData
= new CssmDataContainer(inData
, dataLength
);
895 ItemImpl::getAttributesAndData(SecKeychainAttributeInfo
*info
, SecItemClass
*itemClass
,
896 SecKeychainAttributeList
**attrList
, UInt32
*length
, void **outData
)
898 StLock
<Mutex
>_(mMutex
);
899 // If the data hasn't been set we can't return it.
900 if (!mKeychain
&& outData
)
902 CssmData
*data
= mData
.get();
904 MacOSError::throwMe(errSecDataNotAvailable
);
906 // TODO: need to check and make sure attrs are valid and handle error condition
908 SecItemClass myItemClass
= Schema::itemClassFor(recordType());
910 *itemClass
= myItemClass
;
912 // @@@ This call won't work for floating items (like certificates).
915 UInt32 attrCount
= info
? info
->count
: 0;
916 DbAttributes
dbAttributes(mUniqueId
->database(), attrCount
);
917 for (UInt32 ix
= 0; ix
< attrCount
; ix
++)
919 CssmDbAttributeData
&record
= dbAttributes
.add();
920 record
.Info
.AttributeNameFormat
=CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER
;
921 record
.Info
.Label
.AttributeID
=info
->tag
[ix
];
923 if (record
.Info
.Label
.AttributeID
== kSecLabelItemAttr
)
925 // must remap a caller-supplied label attribute tag for password items, since it isn't in the schema
926 if (IS_PASSWORD_ITEM_CLASS( myItemClass
))
927 record
.Info
.Label
.AttributeID
= APPLEDB_GENERIC_PRINTNAME_ATTRIBUTE
;
931 CssmDataContainer itemData
;
932 getContent(&dbAttributes
, outData
? &itemData
: NULL
);
934 if (info
&& attrList
)
936 SecKeychainAttributeList
*theList
=reinterpret_cast<SecKeychainAttributeList
*>(malloc(sizeof(SecKeychainAttributeList
)));
937 SecKeychainAttribute
*attr
=reinterpret_cast<SecKeychainAttribute
*>(malloc(sizeof(SecKeychainAttribute
)*attrCount
));
938 theList
->count
=attrCount
;
941 for (UInt32 ix
= 0; ix
< attrCount
; ++ix
)
943 attr
[ix
].tag
=info
->tag
[ix
];
945 if (dbAttributes
.at(ix
).NumberOfValues
> 0)
947 attr
[ix
].data
= dbAttributes
.at(ix
).Value
[0].Data
;
948 attr
[ix
].length
= dbAttributes
.at(ix
).Value
[0].Length
;
950 // We don't want the data released, it is up the client
951 dbAttributes
.at(ix
).Value
[0].Data
= NULL
;
952 dbAttributes
.at(ix
).Value
[0].Length
= 0;
956 attr
[ix
].data
= NULL
;
965 *outData
=itemData
.data();
968 if (length
) *length
=itemData
.length();
971 #if SENDACCESSNOTIFICATIONS
972 secdebug("kcnotify", "ItemImpl::getAttributesAndData(%p, %p, %p, %p, %p) retrieved data",
973 info
, itemClass
, attrList
, length
, outData
);
975 KCEventNotifier::PostKeychainEvent(kSecDataAccessEvent
, mKeychain
, this);
982 ItemImpl::freeAttributesAndData(SecKeychainAttributeList
*attrList
, void *data
)
984 Allocator
&allocator
= Allocator::standard(); // @@@ This might not match the one used originally
987 allocator
.free(data
);
991 for (UInt32 ix
= 0; ix
< attrList
->count
; ++ix
)
993 allocator
.free(attrList
->attr
[ix
].data
);
995 free(attrList
->attr
);
1001 ItemImpl::getAttribute(SecKeychainAttribute
& attr
, UInt32
*actualLength
)
1003 StLock
<Mutex
>_(mMutex
);
1004 if (attr
.tag
== kSecClassItemAttr
)
1005 return getClass(attr
, actualLength
);
1007 if (mDbAttributes
.get())
1009 CssmDbAttributeData
*data
= mDbAttributes
->find(Schema::attributeInfo(attr
.tag
));
1012 getAttributeFrom(data
, attr
, actualLength
);
1018 MacOSError::throwMe(errSecNoSuchAttr
);
1021 DbAttributes
dbAttributes(mUniqueId
->database(), 1);
1022 dbAttributes
.add(Schema::attributeInfo(attr
.tag
));
1023 mUniqueId
->get(&dbAttributes
, NULL
);
1024 getAttributeFrom(&dbAttributes
.at(0), attr
, actualLength
);
1028 ItemImpl::getAttributeFrom(CssmDbAttributeData
*data
, SecKeychainAttribute
&attr
, UInt32
*actualLength
)
1030 StLock
<Mutex
>_(mMutex
);
1031 static const uint32 zero
= 0;
1033 const void *buf
= NULL
;
1035 // Temporary storage for buf.
1045 else if (data
->size() < 1) // Attribute has no values.
1047 if (data
->format() == CSSM_DB_ATTRIBUTE_FORMAT_SINT32
1048 || data
->format() == CSSM_DB_ATTRIBUTE_FORMAT_UINT32
)
1050 length
= sizeof(zero
);
1053 else if (CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE
)
1054 length
= 0; // Should we throw here?
1055 else // All other formats
1058 else // Get the first value
1060 length
= data
->Value
[0].Length
;
1061 buf
= data
->Value
[0].Data
;
1063 if (data
->format() == CSSM_DB_ATTRIBUTE_FORMAT_SINT32
)
1065 if (attr
.length
== sizeof(sint8
))
1067 length
= attr
.length
;
1068 svalue8
= sint8(*reinterpret_cast<const sint32
*>(buf
));
1071 else if (attr
.length
== sizeof(sint16
))
1073 length
= attr
.length
;
1074 svalue16
= sint16(*reinterpret_cast<const sint32
*>(buf
));
1078 else if (data
->format() == CSSM_DB_ATTRIBUTE_FORMAT_UINT32
)
1080 if (attr
.length
== sizeof(uint8
))
1082 length
= attr
.length
;
1083 uvalue8
= uint8(*reinterpret_cast<const uint32
*>(buf
));
1086 else if (attr
.length
== sizeof(uint16
))
1088 length
= attr
.length
;
1089 uvalue16
= uint16(*reinterpret_cast<const uint32
*>(buf
));
1093 else if (data
->format() == CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE
)
1095 if (attr
.length
== sizeof(uint32
))
1097 TimeStringToMacSeconds(data
->Value
[0], macSeconds
);
1099 length
= attr
.length
;
1101 else if (attr
.length
== sizeof(sint64
))
1103 TimeStringToMacLongDateTime(data
->Value
[0], macLDT
);
1105 length
= attr
.length
;
1111 *actualLength
= length
;
1115 if (attr
.length
< length
)
1116 MacOSError::throwMe(errSecBufferTooSmall
);
1118 memcpy(attr
.data
, buf
, length
);
1123 ItemImpl::getData(CssmDataContainer
& outData
)
1125 StLock
<Mutex
>_(mMutex
);
1128 CssmData
*data
= mData
.get();
1129 // If the data hasn't been set we can't return it.
1131 MacOSError::throwMe(errSecDataNotAvailable
);
1137 getContent(NULL
, &outData
);
1139 #if SENDACCESSNOTIFICATIONS
1140 secdebug("kcnotify", "ItemImpl::getData retrieved data");
1142 //%%%<might> be done elsewhere, but here is good for now
1143 KCEventNotifier::PostKeychainEvent(kSecDataAccessEvent
, mKeychain
, this);
1150 StLock
<Mutex
>_(mMutex
);
1154 Db
db(mKeychain
->database());
1155 if (useSecureStorage(db
))
1157 group
= safer_cast
<SSDbUniqueRecordImpl
&>(*mUniqueId
).group();
1164 void ItemImpl::getLocalContent(SecKeychainAttributeList
*attributeList
, UInt32
*outLength
, void **outData
)
1166 StLock
<Mutex
>_(mMutex
);
1168 Allocator
&allocator
= Allocator::standard(); // @@@ This might not match the one used originally
1171 CssmData
*data
= mData
.get();
1173 MacOSError::throwMe(errSecDataNotAvailable
);
1175 // Copy the data out of our internal cached copy.
1176 uint32 length
= data
->Length
;
1177 *outData
= allocator
.malloc(length
);
1178 memcpy(*outData
, data
->Data
, length
);
1180 *outLength
= length
;
1185 if (!mDbAttributes
.get())
1186 MacOSError::throwMe(errSecDataNotAvailable
);
1188 // Pull attributes out of a "floating" item, i.e. one that isn't attached to a database
1189 for (UInt32 ix
= 0; ix
< attributeList
->count
; ++ix
)
1191 SecKeychainAttribute
&attribute
= attributeList
->attr
[ix
];
1192 CssmDbAttributeData
*data
= mDbAttributes
->find(Schema::attributeInfo(attribute
.tag
));
1193 if (data
&& data
->NumberOfValues
> 0)
1195 // Copy the data out of our internal cached copy.
1196 uint32 length
= data
->Value
[0].Length
;
1197 attribute
.data
= allocator
.malloc(length
);
1198 memcpy(attribute
.data
, data
->Value
[0].Data
, length
);
1199 attribute
.length
= length
;
1203 attribute
.length
= 0;
1204 attribute
.data
= NULL
;
1211 ItemImpl::getContent(DbAttributes
*dbAttributes
, CssmDataContainer
*itemData
)
1213 StLock
<Mutex
>_(mMutex
);
1214 // Make sure mUniqueId is set.
1218 Db
db(mUniqueId
->database());
1221 mUniqueId
->getWithoutEncryption (dbAttributes
, itemData
);
1224 if (useSecureStorage(db
))
1226 SSDbUniqueRecordImpl
* impl
= dynamic_cast<SSDbUniqueRecordImpl
*>(&(*mUniqueId
));
1229 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER
);
1232 SSDbUniqueRecord
ssUniqueId(impl
);
1233 const AccessCredentials
*autoPrompt
= globals().itemCredentials();
1234 ssUniqueId
->get(dbAttributes
, itemData
, autoPrompt
);
1239 mUniqueId
->get(dbAttributes
, itemData
);
1243 ItemImpl::useSecureStorage(const Db
&db
)
1245 StLock
<Mutex
>_(mMutex
);
1246 switch (recordType())
1248 case CSSM_DL_DB_RECORD_GENERIC_PASSWORD
:
1249 case CSSM_DL_DB_RECORD_INTERNET_PASSWORD
:
1250 case CSSM_DL_DB_RECORD_APPLESHARE_PASSWORD
:
1251 if (db
->dl()->subserviceMask() & CSSM_SERVICE_CSP
)
1260 void ItemImpl::willRead()
1265 void ItemImpl::copyPersistentReference(CFDataRef
&outDataRef
)
1267 StLock
<Mutex
>_(mMutex
);
1268 // item must be in a keychain and have a primary key to be persistent
1269 if (!mKeychain
|| !mPrimaryKey
) {
1270 MacOSError::throwMe(errSecItemNotFound
);
1272 DLDbIdentifier dlDbIdentifier
= mKeychain
->dlDbIdentifier();
1273 DLDbIdentifier
newDlDbIdentifier(dlDbIdentifier
.ssuid(),
1274 DLDbListCFPref::AbbreviatedPath(mKeychain
->name()).c_str(),
1275 dlDbIdentifier
.dbLocation());
1276 NameValueDictionary dict
;
1277 NameValueDictionary::MakeNameValueDictionaryFromDLDbIdentifier(newDlDbIdentifier
, dict
);
1279 CssmData
* pKey
= mPrimaryKey
;
1280 dict
.Insert (new NameValuePair(ITEM_KEY
, *pKey
));
1282 // flatten the NameValueDictionary
1284 dict
.Export(dictData
);
1285 outDataRef
= ::CFDataCreate(kCFAllocatorDefault
, dictData
.Data
, dictData
.Length
);
1286 free (dictData
.Data
);
1289 void ItemImpl::copyRecordIdentifier(CSSM_DATA
&data
)
1291 StLock
<Mutex
>_(mMutex
);
1292 CssmClient::DbUniqueRecord uniqueRecord
= dbUniqueRecord ();
1293 uniqueRecord
->getRecordIdentifier(data
);
1297 * Obtain blob used to bind a keychain item to an Extended Attribute record.
1298 * We just use the PrimaryKey blob as the default. Note that for standard Items,
1299 * this can cause the loss of extended attribute bindings if a Primary Key
1300 * attribute changes.
1302 const CssmData
&ItemImpl::itemID()
1304 StLock
<Mutex
>_(mMutex
);
1305 if(mPrimaryKey
->length() == 0) {
1306 /* not in a keychain; we don't have a primary key */
1307 MacOSError::throwMe(errSecNoSuchAttr
);
1309 return *mPrimaryKey
;
1313 void ItemImpl::postItemEvent(SecKeychainEvent theEvent
)
1315 mKeychain
->postEvent(theEvent
, this);
1321 // Item -- This class is here to magically create the right subclass of ItemImpl
1322 // when constructing new items.
1328 Item::Item(ItemImpl
*impl
) : SecPointer
<ItemImpl
>(impl
)
1332 Item::Item(SecItemClass itemClass
, OSType itemCreator
, UInt32 length
, const void* data
, bool inhibitCheck
)
1336 if (itemClass
== CSSM_DL_DB_RECORD_X509_CERTIFICATE
1337 || itemClass
== CSSM_DL_DB_RECORD_PUBLIC_KEY
1338 || itemClass
== CSSM_DL_DB_RECORD_PRIVATE_KEY
1339 || itemClass
== CSSM_DL_DB_RECORD_SYMMETRIC_KEY
)
1340 MacOSError::throwMe(errSecNoSuchClass
); /* @@@ errSecInvalidClass */
1343 *this = new ItemImpl(itemClass
, itemCreator
, length
, data
, inhibitCheck
);
1346 Item::Item(SecItemClass itemClass
, SecKeychainAttributeList
*attrList
, UInt32 length
, const void* data
)
1348 *this = new ItemImpl(itemClass
, attrList
, length
, data
);
1351 Item::Item(const Keychain
&keychain
, const PrimaryKey
&primaryKey
, const CssmClient::DbUniqueRecord
&uniqueId
)
1352 : SecPointer
<ItemImpl
>(
1353 primaryKey
->recordType() == CSSM_DL_DB_RECORD_X509_CERTIFICATE
1354 ? Certificate::make(keychain
, primaryKey
, uniqueId
)
1355 : (primaryKey
->recordType() == CSSM_DL_DB_RECORD_PUBLIC_KEY
1356 || primaryKey
->recordType() == CSSM_DL_DB_RECORD_PRIVATE_KEY
1357 || primaryKey
->recordType() == CSSM_DL_DB_RECORD_SYMMETRIC_KEY
)
1358 ? KeyItem::make(keychain
, primaryKey
, uniqueId
)
1359 : primaryKey
->recordType() == CSSM_DL_DB_RECORD_EXTENDED_ATTRIBUTE
1360 ? ExtendedAttribute::make(keychain
, primaryKey
, uniqueId
)
1361 : ItemImpl::make(keychain
, primaryKey
, uniqueId
))
1365 Item::Item(const Keychain
&keychain
, const PrimaryKey
&primaryKey
)
1366 : SecPointer
<ItemImpl
>(
1367 primaryKey
->recordType() == CSSM_DL_DB_RECORD_X509_CERTIFICATE
1368 ? Certificate::make(keychain
, primaryKey
)
1369 : (primaryKey
->recordType() == CSSM_DL_DB_RECORD_PUBLIC_KEY
1370 || primaryKey
->recordType() == CSSM_DL_DB_RECORD_PRIVATE_KEY
1371 || primaryKey
->recordType() == CSSM_DL_DB_RECORD_SYMMETRIC_KEY
)
1372 ? KeyItem::make(keychain
, primaryKey
)
1373 : primaryKey
->recordType() == CSSM_DL_DB_RECORD_EXTENDED_ATTRIBUTE
1374 ? ExtendedAttribute::make(keychain
, primaryKey
)
1375 : ItemImpl::make(keychain
, primaryKey
))
1379 Item::Item(ItemImpl
&item
)
1380 : SecPointer
<ItemImpl
>(
1381 item
.recordType() == CSSM_DL_DB_RECORD_X509_CERTIFICATE
1382 ? new Certificate(safer_cast
<Certificate
&>(item
))
1383 : (item
.recordType() == CSSM_DL_DB_RECORD_PUBLIC_KEY
1384 || item
.recordType() == CSSM_DL_DB_RECORD_PRIVATE_KEY
1385 || item
.recordType() == CSSM_DL_DB_RECORD_SYMMETRIC_KEY
)
1386 ? new KeyItem(safer_cast
<KeyItem
&>(item
))
1387 : item
.recordType() == CSSM_DL_DB_RECORD_EXTENDED_ATTRIBUTE
1388 ? new ExtendedAttribute(safer_cast
<ExtendedAttribute
&>(item
))
1389 : new ItemImpl(item
))
1393 CFIndex
GetItemRetainCount(Item
& item
)
1395 return CFGetRetainCount(item
->handle(false));