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@
24 #include <Security/SecKeychainItem.h>
25 #include <Security/SecKeychainItemPriv.h>
27 #include <security_keychain/Keychains.h>
28 #include <security_keychain/KeyItem.h>
29 #include <security_keychain/Item.h>
30 #include <security_keychain/KCCursor.h> // @@@ Remove this when SecKeychainItemFindFirst moves to SecKeychainSearch
32 #include <securityd_client/dictionary.h>
33 #include <security_cdsa_utilities/Schema.h>
34 #include <Security/cssmapplePriv.h>
36 #include "SecBridge.h"
37 #include "KCExceptions.h"
39 #include "SecKeychainItemExtendedAttributes.h"
42 // Given a polymorphic Sec type object, return
43 // its AclBearer component.
44 // Note: Login ACLs are not hooked into this layer;
45 // modules or attachments have no Sec* layer representation.
47 RefPointer
<AclBearer
> aclBearer(CFTypeRef itemRef
)
49 // well, exactly what kind of something are you?
50 CFTypeID id
= CFGetTypeID(itemRef
);
51 if (id
== gTypes().ItemImpl
.typeID
) {
52 // keychain item. If it's in a protected group, return the group key
53 if (SSGroup group
= ItemImpl::required(SecKeychainItemRef(itemRef
))->group())
55 } else if (id
== gTypes().KeyItem
.typeID
) {
56 // key item, return the key itself.
57 if (CssmClient::Key key
= KeyItem::required(SecKeyRef(itemRef
))->key())
59 } else if (id
== gTypes().KeychainImpl
.typeID
) {
60 // keychain (this yields the database ACL)
61 //@@@ not hooked up yet
64 MacOSError::throwMe(errSecNoAccessForItem
);
69 SecKeychainItemGetTypeID(void)
73 return gTypes().ItemImpl
.typeID
;
75 END_SECAPI1(_kCFRuntimeNotATypeID
)
80 SecKeychainItemCreateFromContent(SecItemClass itemClass
, SecKeychainAttributeList
*attrList
,
81 UInt32 length
, const void *data
, SecKeychainRef keychainRef
,
82 SecAccessRef initialAccess
, SecKeychainItemRef
*itemRef
)
85 KCThrowParamErrIf_(length
!=0 && data
==NULL
);
86 Item
item(itemClass
, attrList
, length
, data
);
88 item
->setAccess(Access::required(initialAccess
));
90 Keychain keychain
= nil
;
93 keychain
= Keychain::optional(keychainRef
);
94 if ( !keychain
->exists() )
96 MacOSError::throwMe(errSecNoSuchKeychain
); // Might be deleted or not available at this time.
101 keychain
= globals().storageManager
.defaultKeychainUI(item
);
106 *itemRef
= item
->handle();
112 SecKeychainItemModifyContent(SecKeychainItemRef itemRef
, const SecKeychainAttributeList
*attrList
, UInt32 length
, const void *data
)
115 Item item
= ItemImpl::required(itemRef
);
116 item
->modifyContent(attrList
, length
, data
);
122 SecKeychainItemCopyContent(SecKeychainItemRef itemRef
, SecItemClass
*itemClass
, SecKeychainAttributeList
*attrList
, UInt32
*length
, void **outData
)
125 Item item
= ItemImpl::required(itemRef
);
126 item
->getContent(itemClass
, attrList
, length
, outData
);
132 SecKeychainItemFreeContent(SecKeychainAttributeList
*attrList
, void *data
)
135 ItemImpl::freeContent(attrList
, data
);
141 SecKeychainItemModifyAttributesAndData(SecKeychainItemRef itemRef
, const SecKeychainAttributeList
*attrList
, UInt32 length
, const void *data
)
144 Item item
= ItemImpl::required(itemRef
);
145 item
->modifyAttributesAndData(attrList
, length
, data
);
151 SecKeychainItemCopyAttributesAndData(SecKeychainItemRef itemRef
, SecKeychainAttributeInfo
*info
, SecItemClass
*itemClass
, SecKeychainAttributeList
**attrList
, UInt32
*length
, void **outData
)
154 Item item
= ItemImpl::required(itemRef
);
155 item
->getAttributesAndData(info
, itemClass
, attrList
, length
, outData
);
161 SecKeychainItemFreeAttributesAndData(SecKeychainAttributeList
*attrList
, void *data
)
164 ItemImpl::freeAttributesAndData(attrList
, data
);
170 SecKeychainItemDelete(SecKeychainItemRef itemRef
)
173 Item item
= ItemImpl::required( itemRef
);
174 Keychain keychain
= item
->keychain();
175 // item must be persistent.
176 KCThrowIf_( !keychain
, errSecInvalidItemRef
);
179 * Before deleting the item, delete any existing Extended Attributes.
182 CFArrayRef attrNames
= NULL
;
183 ortn
= SecKeychainItemCopyAllExtendedAttributes(itemRef
, &attrNames
, NULL
);
185 CFIndex numAttrs
= CFArrayGetCount(attrNames
);
186 for(CFIndex dex
=0; dex
<numAttrs
; dex
++) {
187 CFStringRef attrName
= (CFStringRef
)CFArrayGetValueAtIndex(attrNames
, dex
);
188 /* setting value to NULL ==> delete */
189 SecKeychainItemSetExtendedAttribute(itemRef
, attrName
, NULL
);
193 /* now delete the item */
194 keychain
->deleteItem( item
);
200 SecKeychainItemCopyKeychain(SecKeychainItemRef itemRef
, SecKeychainRef
* keychainRef
)
203 // make sure this item has a keychain
204 Keychain kc
= ItemImpl::required(itemRef
)->keychain ();
207 MacOSError::throwMe(errSecNoSuchKeychain
);
210 Required(keychainRef
) = kc
->handle();
216 SecKeychainItemCreateCopy(SecKeychainItemRef itemRef
, SecKeychainRef destKeychainRef
,
217 SecAccessRef initialAccess
, SecKeychainItemRef
*itemCopy
)
220 Item copy
= ItemImpl::required(itemRef
)->copyTo(Keychain::optional(destKeychainRef
), Access::optional(initialAccess
));
222 *itemCopy
= copy
->handle();
228 SecKeychainItemGetUniqueRecordID(SecKeychainItemRef itemRef
, const CSSM_DB_UNIQUE_RECORD
**uniqueRecordID
)
231 Required(uniqueRecordID
) = ItemImpl::required(itemRef
)->dbUniqueRecord();
237 SecKeychainItemGetDLDBHandle(SecKeychainItemRef itemRef
, CSSM_DL_DB_HANDLE
* dldbHandle
)
240 *dldbHandle
= ItemImpl::required(itemRef
)->keychain()->database()->handle();
245 OSStatus
SecAccessCreateFromObject(CFTypeRef sourceRef
,
246 SecAccessRef
*accessRef
)
249 Required(accessRef
); // preflight
250 SecPointer
<Access
> access
= new Access(*aclBearer(sourceRef
));
251 *accessRef
= access
->handle();
258 OSStatus
SecAccessModifyObject(SecAccessRef accessRef
, CFTypeRef sourceRef
)
261 Access::required(accessRef
)->setAccess(*aclBearer(sourceRef
), true);
266 SecKeychainItemCopyAccess(SecKeychainItemRef itemRef
, SecAccessRef
* accessRef
)
270 Required(accessRef
); // preflight
271 SecPointer
<Access
> access
= new Access(*aclBearer(reinterpret_cast<CFTypeRef
>(itemRef
)));
272 *accessRef
= access
->handle();
279 SecKeychainItemSetAccess(SecKeychainItemRef itemRef
, SecAccessRef accessRef
)
283 Access::required(accessRef
)->setAccess(*aclBearer(reinterpret_cast<CFTypeRef
>(itemRef
)), true);
285 ItemImpl::required(itemRef
)->postItemEvent (kSecUpdateEvent
);
290 /* Sets an item's data for legacy "KC" CoreServices APIs.
291 Note this version sets the data, but doesn't update the item
292 as the KC behavior dictates.
294 OSStatus
SecKeychainItemSetData(SecKeychainItemRef itemRef
, UInt32 length
, const void* data
)
297 ItemImpl::required(itemRef
)->setData(length
, data
);
301 /* Gets an item's data for legacy "KC" CoreServices APIs.
302 Note this version doesn't take a SecItemClass parameter.
304 OSStatus
SecKeychainItemGetData(SecKeychainItemRef itemRef
, UInt32 maxLength
, void* data
, UInt32
* actualLength
)
307 /* The caller either needs to specify data and maxLength or an actualLength, so we return either the data itself or the actual length of the data or both. */
308 if (!((data
&& maxLength
) || actualLength
))
309 MacOSError::throwMe(paramErr
);
311 CssmDataContainer aData
;
312 ItemImpl::required(itemRef
)->getData(aData
);
314 *actualLength
= aData
.length();
318 // Make sure the buffer is big enough
319 if (aData
.length() > maxLength
)
320 MacOSError::throwMe(errKCBufferTooSmall
);
321 memcpy(data
, aData
.data(), aData
.length());
326 /* Update a keychain item for legacy "KC" CoreServices APIs.
327 The "KC" API's do a 'set attribute', then an 'update'.
329 OSStatus
SecKeychainItemUpdate(SecKeychainItemRef itemRef
)
332 ItemImpl::required(itemRef
)->update();
336 /* Add a 'floating' keychain item without UI for legacy "KC" CoreServices APIs.
338 OSStatus
SecKeychainItemAddNoUI(SecKeychainRef keychainRef
, SecKeychainItemRef itemRef
)
341 Item item
= ItemImpl::required(itemRef
);
342 Keychain::optional(keychainRef
)->add(item
);
346 /* Add a 'floating' keychain item to the default keychain with possible UI for legacy "KC" Carbon APIs.
348 OSStatus
SecKeychainItemAdd(SecKeychainItemRef itemRef
)
351 Item item
= ItemImpl::required(itemRef
);
352 Keychain defaultKeychain
= globals().storageManager
.defaultKeychainUI(item
);
353 defaultKeychain
->add(item
);
357 /* Creates a floating keychain item for legacy "KC" CoreServices APIs
359 OSStatus
SecKeychainItemCreateNew(SecItemClass itemClass
, OSType itemCreator
, UInt32 length
, const void* data
, SecKeychainItemRef
* itemRef
)
362 RequiredParam(itemRef
) = Item(itemClass
, itemCreator
, length
, data
, false)->handle();
366 /* Gets an individual attribute for legacy "KC" CoreServices APIs
368 OSStatus
SecKeychainItemGetAttribute(SecKeychainItemRef itemRef
, SecKeychainAttribute
* attribute
, UInt32
* actualLength
)
371 ItemImpl::required(itemRef
)->getAttribute(RequiredParam(attribute
), actualLength
);
375 /* Sets an individual attribute for legacy "KC" CoreServices APIs
377 OSStatus
SecKeychainItemSetAttribute(SecKeychainItemRef itemRef
, SecKeychainAttribute
* attribute
)
380 ItemImpl::required(itemRef
)->setAttribute(RequiredParam(attribute
));
384 /* Finds a keychain item for legacy "KC" CoreServices APIs.
385 Note: This version doesn't take a SecItemClass because
386 SecKeychainSearchCreateFromAttributes() requires it.
387 @@@ This should move to SecKeychainSearch.cpp
389 OSStatus
SecKeychainItemFindFirst(SecKeychainRef keychainRef
, const SecKeychainAttributeList
*attrList
, SecKeychainSearchRef
*searchRef
, SecKeychainItemRef
*itemRef
)
394 cursor
= KeychainImpl::required(keychainRef
)->createCursor(attrList
);
396 cursor
= globals().storageManager
.createCursor(attrList
);
399 if (!cursor
->next(item
))
400 return errKCItemNotFound
;
402 *itemRef
=item
->handle();
404 *searchRef
=cursor
->handle();
408 OSStatus
SecKeychainItemCreatePersistentReference(SecKeychainItemRef itemRef
, CFDataRef
*persistentItemRef
)
411 KCThrowParamErrIf_(!itemRef
|| !persistentItemRef
);
412 Item item
= ItemImpl::required(itemRef
);
413 item
->copyPersistentReference(*persistentItemRef
);
417 OSStatus
SecKeychainItemCopyFromPersistentReference(CFDataRef persistentItemRef
, SecKeychainItemRef
*itemRef
)
420 KCThrowParamErrIf_(!persistentItemRef
|| !itemRef
);
421 // make a NameValueDictionary from the data we received
422 CssmData
dictData((void*)::CFDataGetBytePtr(persistentItemRef
), ::CFDataGetLength(persistentItemRef
));
423 NameValueDictionary
dict(dictData
);
428 // make sure we have a database identifier
429 if (dict
.FindByName(SSUID_KEY
) != 0)
431 DLDbIdentifier dlDbIdentifier
= NameValueDictionary::MakeDLDbIdentifierFromNameValueDictionary(dict
);
432 DLDbIdentifier
newDlDbIdentifier(dlDbIdentifier
.ssuid(),
433 DLDbListCFPref::ExpandTildesInPath(dlDbIdentifier
.dbName()).c_str(),
434 dlDbIdentifier
.dbLocation());
436 keychain
= globals().storageManager
.keychain(newDlDbIdentifier
);
438 const NameValuePair
* aDictItem
= dict
.FindByName(ITEM_KEY
);
439 if (aDictItem
&& keychain
)
441 PrimaryKey
primaryKey(aDictItem
->Value());
442 item
= keychain
->item(primaryKey
);
444 // We can safely release the global API lock now since keychain and item
445 // are CFRetained and will be until they go out of scope.
447 KCThrowIf_( !item
, errSecItemNotFound
);
448 *itemRef
= item
->handle();
452 OSStatus
SecKeychainItemCopyRecordIdentifier(SecKeychainItemRef itemRef
, CFDataRef
*recordIdentifier
)
456 RequiredParam (recordIdentifier
);
457 Item item
= ItemImpl::required(itemRef
);
458 item
->copyRecordIdentifier (data
);
459 *recordIdentifier
= ::CFDataCreate(kCFAllocatorDefault
, (UInt8
*) data
.Data
, data
.Length
);
465 SecKeychainItemCopyFromRecordIdentifier(SecKeychainRef keychainRef
,
466 SecKeychainItemRef
*itemRef
,
467 CFDataRef recordIdentifier
)
470 // make a local Keychain reference
471 RequiredParam (keychainRef
);
472 Keychain keychain
= KeychainImpl::optional (keychainRef
);
473 RequiredParam (itemRef
);
474 RequiredParam (recordIdentifier
);
476 Db
db(keychain
->database());
478 // make a raw database call to get the data
479 CSSM_DL_DB_HANDLE dbHandle
= db
.handle ();
480 CSSM_DB_UNIQUE_RECORD uniqueRecord
;
482 // according to source, we should be able to reconsitute the uniqueRecord
483 // from the data we earlier retained
485 // prepare the record id
486 memset (&uniqueRecord
, 0, sizeof (uniqueRecord
));
487 uniqueRecord
.RecordIdentifier
.Data
= (uint8
*) CFDataGetBytePtr (recordIdentifier
);
488 uniqueRecord
.RecordIdentifier
.Length
= CFDataGetLength (recordIdentifier
);
490 // convert this unique id to a CSSM_DB_UNIQUE_RECORD that works for the CSP/DL
491 CSSM_DB_UNIQUE_RECORD_PTR outputUniqueRecordPtr
;
493 result
= CSSM_DL_PassThrough (dbHandle
, CSSM_APPLECSPDL_DB_CONVERT_RECORD_IDENTIFIER
, &uniqueRecord
, (void**) &outputUniqueRecordPtr
);
494 KCThrowIf_(result
!= 0, errSecItemNotFound
);
496 // from this, get the record type
497 CSSM_DB_RECORD_ATTRIBUTE_DATA attributeData
;
498 memset (&attributeData
, 0, sizeof (attributeData
));
500 result
= CSSM_DL_DataGetFromUniqueRecordId (dbHandle
, outputUniqueRecordPtr
, &attributeData
, NULL
);
501 KCThrowIf_(result
!= 0, errSecItemNotFound
);
502 CSSM_DB_RECORDTYPE recordType
= attributeData
.DataRecordType
;
504 // make the unique record item -- precursor to creation of a SecKeychainItemRef
505 DbUniqueRecord
unique(db
);
506 CSSM_DB_UNIQUE_RECORD_PTR
*uniquePtr
= unique
;
507 *uniquePtr
= outputUniqueRecordPtr
;
510 Item item
= keychain
->item (recordType
, unique
);
513 *itemRef
= item
->handle();
518 OSStatus
SecKeychainItemCreateFromEncryptedContent(SecItemClass itemClass
,
519 UInt32 length
, const void *data
, SecKeychainRef keychainRef
,
520 SecAccessRef initialAccess
, SecKeychainItemRef
*itemRef
, CFDataRef
*localID
)
523 KCThrowParamErrIf_(length
!=0 && data
==NULL
);
525 RequiredParam (localID
);
526 RequiredParam (keychainRef
);
528 Item
item(itemClass
, (uint32
) 0, length
, data
, true);
530 item
->setAccess(Access::required(initialAccess
));
532 Keychain keychain
= Keychain::optional(keychainRef
);
533 if (!keychain
->exists())
535 MacOSError::throwMe(errSecNoSuchKeychain
); // Might be deleted or not available at this time.
538 item
->doNotEncrypt ();
543 catch (const CommonError
&err
)
545 if (err
.osStatus () == errSecNoSuchClass
)
547 // the only time this should happen is if the item is a certificate (for keychain syncing)
548 if (itemClass
== CSSM_DL_DB_RECORD_X509_CERTIFICATE
)
550 // create the certificate relation
551 Db
db(keychain
->database());
553 db
->createRelation(CSSM_DL_DB_RECORD_X509_CERTIFICATE
,
554 "CSSM_DL_DB_RECORD_X509_CERTIFICATE",
555 Schema::X509CertificateSchemaAttributeCount
,
556 Schema::X509CertificateSchemaAttributeList
,
557 Schema::X509CertificateSchemaIndexCount
,
558 Schema::X509CertificateSchemaIndexList
);
559 keychain
->keychainSchema()->didCreateRelation(
560 CSSM_DL_DB_RECORD_X509_CERTIFICATE
,
561 "CSSM_DL_DB_RECORD_X509_CERTIFICATE",
562 Schema::X509CertificateSchemaAttributeCount
,
563 Schema::X509CertificateSchemaAttributeList
,
564 Schema::X509CertificateSchemaIndexCount
,
565 Schema::X509CertificateSchemaIndexList
);
567 // add the item again
578 *itemRef
= item
->handle();
581 item
->copyRecordIdentifier (recordID
);
583 *localID
= CFDataCreate(kCFAllocatorDefault
, (UInt8
*) recordID
.Data
, recordID
.Length
);
584 free (recordID
.Data
);
588 OSStatus
SecKeychainItemCopyAttributesAndEncryptedData(SecKeychainItemRef itemRef
, SecKeychainAttributeInfo
*info
,
589 SecItemClass
*itemClass
, SecKeychainAttributeList
**attrList
,
590 UInt32
*length
, void **outData
)
593 Item item
= ItemImpl::required(itemRef
);
594 item
->doNotEncrypt ();
595 item
->getAttributesAndData(info
, itemClass
, attrList
, length
, outData
);
599 OSStatus
SecKeychainItemModifyEncryptedData(SecKeychainItemRef itemRef
, UInt32 length
, const void *data
)
602 Item item
= ItemImpl::required(itemRef
);
603 item
->doNotEncrypt ();
604 item
->modifyAttributesAndData(NULL
, length
, data
);