]> git.saurik.com Git - apple/security.git/blobdiff - OSX/libsecurity_keychain/Security/SecKeychainItem.cpp
Security-57336.1.9.tar.gz
[apple/security.git] / OSX / libsecurity_keychain / Security / SecKeychainItem.cpp
diff --git a/OSX/libsecurity_keychain/Security/SecKeychainItem.cpp b/OSX/libsecurity_keychain/Security/SecKeychainItem.cpp
new file mode 100644 (file)
index 0000000..30bb1d3
--- /dev/null
@@ -0,0 +1,912 @@
+/*
+ * Copyright (c) 2000-2004,2011-2015 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include <Security/SecBase.h>
+#include <Security/SecKeychainItem.h>
+#include <Security/SecKeychainItemPriv.h>
+#include <Security/SecCertificatePriv.h>
+
+#include <security_keychain/Keychains.h>
+#include <security_keychain/KeyItem.h>
+#include <security_keychain/Item.h>
+#include <security_keychain/Certificate.h>
+#include <security_keychain/Identity.h>
+#include <security_keychain/KCCursor.h> // @@@ Remove this when SecKeychainItemFindFirst moves to SecKeychainSearch
+
+#include <securityd_client/dictionary.h>
+#include <security_cdsa_utilities/Schema.h>
+#include <Security/cssmapplePriv.h>
+#include <syslog.h>
+
+#include "SecBridge.h"
+#include "KCExceptions.h"
+#include "Access.h"
+#include "SecKeychainItemExtendedAttributes.h"
+
+
+//
+// Given a polymorphic Sec type object, return
+// its AclBearer component.
+// Note: Login ACLs are not hooked into this layer;
+// modules or attachments have no Sec* layer representation.
+//
+static
+RefPointer<AclBearer> aclBearer(CFTypeRef itemRef)
+{
+       // well, exactly what kind of something are you?
+       CFTypeID id = CFGetTypeID(itemRef);
+       if (id == gTypes().ItemImpl.typeID) {
+               // keychain item. If it's in a protected group, return the group key
+               if (SSGroup group = ItemImpl::required(SecKeychainItemRef(itemRef))->group())
+                       return &*group;
+       } else if (id == gTypes().KeyItem.typeID) {
+               // key item, return the key itself.
+               if (CssmClient::Key key = KeyItem::required(SecKeyRef(itemRef))->key())
+                       return &*key;
+       } else if (id == gTypes().KeychainImpl.typeID) {
+               // keychain (this yields the database ACL)
+               //@@@ not hooked up yet
+       }
+       // Guess not. Bummer
+       MacOSError::throwMe(errSecNoAccessForItem);
+}
+
+
+CFTypeID
+SecKeychainItemGetTypeID(void)
+{
+       BEGIN_SECAPI
+
+       return gTypes().ItemImpl.typeID;
+
+       END_SECAPI1(_kCFRuntimeNotATypeID)
+}
+
+
+OSStatus
+SecKeychainItemCreateFromContent(SecItemClass itemClass, SecKeychainAttributeList *attrList,
+               UInt32 length, const void *data, SecKeychainRef keychainRef,
+               SecAccessRef initialAccess, SecKeychainItemRef *itemRef)
+{
+    BEGIN_SECAPI
+               KCThrowParamErrIf_(length!=0 && data==NULL);
+        Item item(itemClass, attrList, length, data);
+               if (initialAccess)
+                       item->setAccess(Access::required(initialAccess));
+
+        Keychain keychain = nil;
+        try
+        {
+            keychain = Keychain::optional(keychainRef);
+            if ( !keychain->exists() )
+            {
+                MacOSError::throwMe(errSecNoSuchKeychain);     // Might be deleted or not available at this time.
+            }
+        }
+        catch(...)
+        {
+            keychain = globals().storageManager.defaultKeychainUI(item);
+        }
+
+        keychain->add(item);
+        if (itemRef)
+               *itemRef = item->handle();
+       END_SECAPI
+}
+
+
+OSStatus
+SecKeychainItemModifyContent(SecKeychainItemRef itemRef, const SecKeychainAttributeList *attrList, UInt32 length, const void *data)
+{
+    BEGIN_SECAPI
+               Item item = ItemImpl::required(itemRef);
+               item->modifyContent(attrList, length, data);
+       END_SECAPI
+}
+
+
+OSStatus
+SecKeychainItemCopyContent(SecKeychainItemRef itemRef, SecItemClass *itemClass, SecKeychainAttributeList *attrList, UInt32 *length, void **outData)
+{
+#if !SECTRUST_OSX
+       BEGIN_SECAPI
+               Item item = ItemImpl::required(itemRef);
+               item->getContent(itemClass, attrList, length, outData);
+       END_SECAPI
+#else
+       OSStatus __secapiresult;
+       bool isCertificate = false;
+       SecKeychainItemRef itemImplRef;
+       if (itemRef && CFGetTypeID(itemRef) == SecCertificateGetTypeID()) {
+               // TODO: determine whether we need to actually look up the cert in a keychain here
+               itemImplRef = (SecKeychainItemRef) SecCertificateCopyKeychainItem((SecCertificateRef)itemRef);
+               if (!itemImplRef) {
+                       itemImplRef = (SecKeychainItemRef) SecCertificateCreateItemImplInstance((SecCertificateRef)itemRef);
+               }
+               isCertificate = true;
+       }
+       else {
+               itemImplRef = (SecKeychainItemRef)((itemRef) ? CFRetain(itemRef) : NULL);
+       }
+
+       try
+       {
+               Item item = ItemImpl::required(itemImplRef);
+               item->getContent(itemClass, attrList, (isCertificate) ? NULL : length, (isCertificate) ? NULL : outData);
+               __secapiresult=0;
+       }
+       catch (const MacOSError &err) { __secapiresult=err.osStatus(); }
+       catch (const CommonError &err) { __secapiresult=SecKeychainErrFromOSStatus(err.osStatus()); }
+       catch (const std::bad_alloc &) { __secapiresult=errSecAllocate; }
+       catch (...) { __secapiresult=errSecInternalComponent; }
+
+       if (isCertificate && outData && *outData == NULL) {
+               // copy the data here
+               __secapiresult = errSecAllocate;
+               CFDataRef dataRef = SecCertificateCopyData((SecCertificateRef)itemRef);
+               if (dataRef) {
+                       CFIndex dataLen = CFDataGetLength(dataRef);
+                       const UInt8 *bytePtr = CFDataGetBytePtr(dataRef);
+                       if ((bytePtr != NULL) && (dataLen > 0)) {
+                               *outData = malloc(dataLen);
+                               memcpy(*outData, bytePtr, dataLen);
+                               *length = (UInt32)dataLen;
+                               __secapiresult = errSecSuccess;
+                       }
+                       CFRelease(dataRef);
+               }
+       }
+       if (itemImplRef) {
+               CFRelease(itemImplRef);
+       }
+       return __secapiresult;
+#endif
+}
+
+
+OSStatus
+SecKeychainItemFreeContent(SecKeychainAttributeList *attrList, void *data)
+{
+       BEGIN_SECAPI
+               ItemImpl::freeContent(attrList, data);
+       END_SECAPI
+}
+
+
+OSStatus
+SecKeychainItemModifyAttributesAndData(SecKeychainItemRef itemRef, const SecKeychainAttributeList *attrList, UInt32 length, const void *data)
+{
+    BEGIN_SECAPI
+               Item item = ItemImpl::required(itemRef);
+               item->modifyAttributesAndData(attrList, length, data);
+       END_SECAPI
+}
+
+
+OSStatus
+SecKeychainItemCopyAttributesAndData(SecKeychainItemRef itemRef, SecKeychainAttributeInfo *info, SecItemClass *itemClass, SecKeychainAttributeList **attrList, UInt32 *length, void **outData)
+{
+#if !SECTRUST_OSX
+       BEGIN_SECAPI
+               Item item = ItemImpl::required(itemRef);
+               item->getAttributesAndData(info, itemClass, attrList, length, outData);
+       END_SECAPI
+#else
+       // if the item is a SecCertificateRef, must convert to an ItemImpl-based instance
+       // TODO: determine whether we need to actually look up the cert in a keychain here
+       OSStatus __secapiresult;
+       SecKeychainItemRef itemImplRef;
+       if (itemRef && CFGetTypeID(itemRef) == SecCertificateGetTypeID()) {
+               itemImplRef = (SecKeychainItemRef) SecCertificateCopyKeychainItem((SecCertificateRef)itemRef);
+               if (!itemImplRef) {
+                       itemImplRef = (SecKeychainItemRef) SecCertificateCreateItemImplInstance((SecCertificateRef)itemRef);
+               }
+       }
+       else {
+               itemImplRef = (SecKeychainItemRef)((itemRef) ? CFRetain(itemRef) : NULL);
+       }
+
+       try
+       {
+               Item item = ItemImpl::required(itemImplRef);
+               item->getAttributesAndData(info, itemClass, attrList, length, outData);
+               __secapiresult=0;
+       }
+       catch (const MacOSError &err) { __secapiresult=err.osStatus(); }
+       catch (const CommonError &err) { __secapiresult=SecKeychainErrFromOSStatus(err.osStatus()); }
+       catch (const std::bad_alloc &) { __secapiresult=errSecAllocate; }
+       catch (...) { __secapiresult=errSecInternalComponent; }
+
+       if (itemImplRef) {
+               CFRelease(itemImplRef);
+       }
+       return __secapiresult;
+#endif
+}
+
+
+OSStatus
+SecKeychainItemFreeAttributesAndData(SecKeychainAttributeList *attrList, void *data)
+{
+       BEGIN_SECAPI
+               ItemImpl::freeAttributesAndData(attrList, data);
+       END_SECAPI
+}
+
+
+OSStatus
+SecKeychainItemDelete(SecKeychainItemRef itemRef)
+{
+#if !SECTRUST_OSX
+    BEGIN_SECAPI
+               Item item = ItemImpl::required( itemRef );
+               Keychain keychain = item->keychain();
+               // item must be persistent.
+               KCThrowIf_( !keychain, errSecInvalidItemRef );
+
+               /*
+                * Before deleting the item, delete any existing Extended Attributes.
+                */
+               OSStatus ortn;
+               CFArrayRef attrNames = NULL;
+               ortn = SecKeychainItemCopyAllExtendedAttributes(itemRef, &attrNames, NULL);
+               if(ortn == errSecSuccess) {
+                       CFIndex numAttrs = CFArrayGetCount(attrNames);
+                       for(CFIndex dex=0; dex<numAttrs; dex++) {
+                               CFStringRef attrName = (CFStringRef)CFArrayGetValueAtIndex(attrNames, dex);
+                               /* setting value to NULL ==> delete */
+                               SecKeychainItemSetExtendedAttribute(itemRef, attrName, NULL);
+                       }
+               }
+
+               /* now delete the item */
+        keychain->deleteItem( item );
+       END_SECAPI
+#else
+       // if the item is a SecCertificateRef, must convert to an ItemImpl-based instance
+       // TODO: determine whether we need to actually look up the cert in a keychain here
+       OSStatus __secapiresult;
+       SecKeychainItemRef itemImplRef;
+       if (itemRef && CFGetTypeID(itemRef) == SecCertificateGetTypeID()) {
+               itemImplRef = (SecKeychainItemRef) SecCertificateCopyKeychainItem((SecCertificateRef)itemRef);
+               if (!itemImplRef) {
+                       itemImplRef = (SecKeychainItemRef) SecCertificateCreateItemImplInstance((SecCertificateRef)itemRef);
+               }
+       }
+       else {
+               itemImplRef = (SecKeychainItemRef)((itemRef) ? CFRetain(itemRef) : NULL);
+       }
+
+       try
+       {
+               Item item = ItemImpl::required( itemImplRef );
+               Keychain keychain = item->keychain();
+               // item must be persistent.
+               KCThrowIf_( !keychain, errSecInvalidItemRef );
+
+               /*
+                * Before deleting the item, delete any existing Extended Attributes.
+                */
+               OSStatus ortn;
+               CFArrayRef attrNames = NULL;
+               ortn = SecKeychainItemCopyAllExtendedAttributes(itemImplRef, &attrNames, NULL);
+               if(ortn == errSecSuccess) {
+                       CFIndex numAttrs = CFArrayGetCount(attrNames);
+                       for(CFIndex dex=0; dex<numAttrs; dex++) {
+                               CFStringRef attrName = (CFStringRef)CFArrayGetValueAtIndex(attrNames, dex);
+                               /* setting value to NULL ==> delete */
+                               SecKeychainItemSetExtendedAttribute(itemImplRef, attrName, NULL);
+                       }
+               }
+
+               /* now delete the item */
+        keychain->deleteItem( item );
+               __secapiresult=0;
+       }
+       catch (const MacOSError &err) { __secapiresult=err.osStatus(); }
+       catch (const CommonError &err) { __secapiresult=SecKeychainErrFromOSStatus(err.osStatus()); }
+       catch (const std::bad_alloc &) { __secapiresult=errSecAllocate; }
+       catch (...) { __secapiresult=errSecInternalComponent; }
+
+       if (itemImplRef) {
+               CFRelease(itemImplRef);
+       }
+       return __secapiresult;
+#endif
+}
+
+
+OSStatus
+SecKeychainItemCopyKeychain(SecKeychainItemRef itemRef, SecKeychainRef* keychainRef)
+{
+#if !SECTRUST_OSX
+       BEGIN_SECAPI
+               // make sure this item has a keychain
+               Keychain kc = ItemImpl::required(itemRef)->keychain ();
+               if (kc == NULL)
+               {
+                       MacOSError::throwMe(errSecNoSuchKeychain);
+               }
+
+               Required(keychainRef) = kc->handle();
+       END_SECAPI
+#else
+       // if the item is a SecCertificateRef, must convert to an ItemImpl-based instance
+       // TODO: determine whether we need to actually look up the cert in a keychain here
+       OSStatus __secapiresult;
+       SecKeychainItemRef itemImplRef;
+       if (itemRef && CFGetTypeID(itemRef) == SecCertificateGetTypeID()) {
+               itemImplRef = (SecKeychainItemRef) SecCertificateCopyKeychainItem((SecCertificateRef)itemRef);
+               if (!itemImplRef) {
+                       itemImplRef = (SecKeychainItemRef) SecCertificateCreateItemImplInstance((SecCertificateRef)itemRef);
+               }
+       }
+       else {
+               itemImplRef = (SecKeychainItemRef)((itemRef) ? CFRetain(itemRef) : NULL);
+       }
+
+       try
+       {
+               // make sure this item has a keychain
+               Keychain kc = ItemImpl::required(itemImplRef)->keychain();
+               if (kc == NULL)
+               {
+                       MacOSError::throwMe(errSecNoSuchKeychain);
+               }
+
+               Required(keychainRef) = kc->handle();
+               __secapiresult=0;
+       }
+       catch (const MacOSError &err) { __secapiresult=err.osStatus(); }
+       catch (const CommonError &err) { __secapiresult=SecKeychainErrFromOSStatus(err.osStatus()); }
+       catch (const std::bad_alloc &) { __secapiresult=errSecAllocate; }
+       catch (...) { __secapiresult=errSecInternalComponent; }
+
+       if (itemImplRef) {
+               CFRelease(itemImplRef);
+       }
+       return __secapiresult;
+#endif
+}
+
+
+OSStatus
+SecKeychainItemCreateCopy(SecKeychainItemRef itemRef, SecKeychainRef destKeychainRef,
+       SecAccessRef initialAccess, SecKeychainItemRef *itemCopy)
+{
+#if SECTRUST_OSX
+       // bridge code for certificate items
+       if (itemRef && CFGetTypeID(itemRef) == SecCertificateGetTypeID()) {
+               return SecCertificateAddToKeychain((SecCertificateRef)itemRef, destKeychainRef);
+       }
+#endif
+
+       BEGIN_SECAPI
+               Item copy = ItemImpl::required(itemRef)->copyTo(Keychain::optional(destKeychainRef), Access::optional(initialAccess));
+               if (itemCopy)
+                       *itemCopy = copy->handle();
+       END_SECAPI
+}
+
+
+OSStatus
+SecKeychainItemGetUniqueRecordID(SecKeychainItemRef itemRef, const CSSM_DB_UNIQUE_RECORD **uniqueRecordID)
+{
+       BEGIN_SECAPI
+               Required(uniqueRecordID) = ItemImpl::required(itemRef)->dbUniqueRecord();
+       END_SECAPI
+}
+
+
+OSStatus
+SecKeychainItemGetDLDBHandle(SecKeychainItemRef itemRef, CSSM_DL_DB_HANDLE* dldbHandle)
+{
+       BEGIN_SECAPI
+               *dldbHandle = ItemImpl::required(itemRef)->keychain()->database()->handle();
+       END_SECAPI
+}
+
+#if 0
+static
+OSStatus SecAccessCreateFromObject(CFTypeRef sourceRef,
+       SecAccessRef *accessRef)
+{
+       BEGIN_SECAPI
+       Required(accessRef);    // preflight
+       SecPointer<Access> access = new Access(*aclBearer(sourceRef));
+       *accessRef = access->handle();
+       END_SECAPI
+}
+
+
+/*!
+ */
+static
+OSStatus SecAccessModifyObject(SecAccessRef accessRef, CFTypeRef sourceRef)
+{
+       BEGIN_SECAPI
+       Access::required(accessRef)->setAccess(*aclBearer(sourceRef), true);
+       END_SECAPI
+}
+#endif
+
+OSStatus
+SecKeychainItemCopyAccess(SecKeychainItemRef itemRef, SecAccessRef* accessRef)
+{
+    BEGIN_SECAPI
+
+       Required(accessRef);    // preflight
+       SecPointer<Access> access = new Access(*aclBearer(reinterpret_cast<CFTypeRef>(itemRef)));
+       *accessRef = access->handle();
+
+    END_SECAPI
+}
+
+
+OSStatus
+SecKeychainItemSetAccess(SecKeychainItemRef itemRef, SecAccessRef accessRef)
+{
+    BEGIN_SECAPI
+
+       Access::required(accessRef)->setAccess(*aclBearer(reinterpret_cast<CFTypeRef>(itemRef)), true);
+
+       ItemImpl::required(itemRef)->postItemEvent (kSecUpdateEvent);
+
+    END_SECAPI
+}
+
+/*  Sets an item's data for legacy "KC" CoreServices APIs.
+    Note this version sets the data, but doesn't update the item
+    as the KC behavior dictates.
+*/
+OSStatus SecKeychainItemSetData(SecKeychainItemRef itemRef, UInt32 length, const void* data)
+{
+    BEGIN_SECAPI
+               ItemImpl::required(itemRef)->setData(length, data);
+       END_SECAPI
+}
+
+/*  Gets an item's data for legacy "KC" CoreServices APIs.
+    Note this version doesn't take a SecItemClass parameter.
+*/
+OSStatus SecKeychainItemGetData(SecKeychainItemRef itemRef, UInt32 maxLength, void* data, UInt32* actualLength)
+{
+    BEGIN_SECAPI
+               /* 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.  */
+               if (!((data && maxLength) || actualLength))
+                       MacOSError::throwMe(errSecParam);
+
+        CssmDataContainer aData;
+        ItemImpl::required(itemRef)->getData(aData);
+        if (actualLength)
+            *actualLength = (UInt32)aData.length();
+
+               if (data)
+               {
+                       // Make sure the buffer is big enough
+                       if (aData.length() > maxLength)
+                               MacOSError::throwMe(errSecBufferTooSmall);
+                       memcpy(data, aData.data(), aData.length());
+               }
+       END_SECAPI
+}
+
+/*  Update a keychain item for legacy "KC" CoreServices APIs.
+    The "KC" API's do a 'set attribute', then an 'update'.
+*/
+OSStatus SecKeychainItemUpdate(SecKeychainItemRef itemRef)
+{
+    BEGIN_SECAPI
+        ItemImpl::required(itemRef)->update();
+       END_SECAPI
+}
+
+/* Add a 'floating' keychain item without UI for legacy "KC" CoreServices APIs.
+*/
+OSStatus SecKeychainItemAddNoUI(SecKeychainRef keychainRef, SecKeychainItemRef itemRef)
+{
+    BEGIN_SECAPI
+        Item item = ItemImpl::required(itemRef);
+        Keychain::optional(keychainRef)->add(item);
+       END_SECAPI
+}
+
+/* Add a 'floating' keychain item to the default keychain with possible UI for legacy "KC" Carbon APIs.
+*/
+OSStatus SecKeychainItemAdd(SecKeychainItemRef itemRef)
+{
+    BEGIN_SECAPI
+        Item item = ItemImpl::required(itemRef);
+        Keychain defaultKeychain = globals().storageManager.defaultKeychainUI(item);
+        defaultKeychain->add(item);
+       END_SECAPI
+}
+
+/* Creates a floating keychain item for legacy "KC" CoreServices APIs
+*/
+OSStatus SecKeychainItemCreateNew(SecItemClass itemClass, OSType itemCreator, UInt32 length, const void* data, SecKeychainItemRef* itemRef)
+{
+    BEGIN_SECAPI
+        RequiredParam(itemRef) = Item(itemClass, itemCreator, length, data, false)->handle();
+       END_SECAPI
+}
+
+/* Gets an individual attribute for legacy "KC" CoreServices APIs
+*/
+OSStatus SecKeychainItemGetAttribute(SecKeychainItemRef itemRef, SecKeychainAttribute* attribute, UInt32* actualLength)
+{
+    BEGIN_SECAPI
+        ItemImpl::required(itemRef)->getAttribute(RequiredParam(attribute), actualLength);
+       END_SECAPI
+}
+
+/* Sets an individual attribute for legacy "KC" CoreServices APIs
+*/
+OSStatus SecKeychainItemSetAttribute(SecKeychainItemRef itemRef, SecKeychainAttribute* attribute)
+{
+    BEGIN_SECAPI
+        ItemImpl::required(itemRef)->setAttribute(RequiredParam(attribute));
+       END_SECAPI
+}
+
+/*  Finds a keychain item for legacy "KC" CoreServices APIs.
+    Note: This version doesn't take a SecItemClass because
+            SecKeychainSearchCreateFromAttributes() requires it.
+    @@@ This should move to SecKeychainSearch.cpp
+*/
+OSStatus SecKeychainItemFindFirst(SecKeychainRef keychainRef, const SecKeychainAttributeList *attrList, SecKeychainSearchRef *searchRef, SecKeychainItemRef *itemRef)
+{
+    BEGIN_SECAPI
+        KCCursor cursor;
+        if (keychainRef)
+            cursor = KeychainImpl::required(keychainRef)->createCursor(attrList);
+        else
+            cursor = globals().storageManager.createCursor(attrList);
+
+        Item item;
+        if (!cursor->next(item))
+            return errSecItemNotFound;
+
+        *itemRef=item->handle();
+        if (searchRef)
+            *searchRef=cursor->handle();
+       END_SECAPI
+}
+
+#if SECTRUST_OSX
+static OSStatus SecKeychainItemCreatePersistentReferenceFromCertificate(SecCertificateRef certRef,
+    CFDataRef *persistentItemRef)
+{
+       if (!certRef || !persistentItemRef)
+               return errSecParam;
+
+       OSStatus __secapiresult;
+       CFErrorRef errorRef = NULL;
+       CFDataRef serialData = SecCertificateCopySerialNumber(certRef, &errorRef);
+       if (errorRef) {
+               CFIndex err = CFErrorGetCode(errorRef);
+               CFRelease(errorRef);
+               if (serialData) { CFRelease(serialData); }
+               return (OSStatus)err;
+       }
+       CFDataRef issuerData = SecCertificateCopyNormalizedIssuerContent(certRef, &errorRef);
+       if (errorRef) {
+               CFIndex err = CFErrorGetCode(errorRef);
+               CFRelease(errorRef);
+               if (serialData) { CFRelease(serialData); }
+               if (issuerData) { CFRelease(issuerData); }
+               return (OSStatus)err;
+       }
+
+       try {
+               // look up ItemImpl cert in keychain by normalized issuer and serial number
+               StorageManager::KeychainList keychains;
+               globals().storageManager.optionalSearchList(NULL, keychains);
+               KCCursor cursor(Certificate::cursorForIssuerAndSN_CF(keychains, issuerData, serialData));
+               Item item;
+               if (!cursor->next(item)) {
+                       MacOSError::throwMe(errSecItemNotFound);
+               }
+               item->copyPersistentReference(*persistentItemRef, false);
+               __secapiresult = errSecSuccess;
+       }
+       catch (const MacOSError &err) { __secapiresult=err.osStatus(); }
+       catch (const CommonError &err) { __secapiresult=SecKeychainErrFromOSStatus(err.osStatus()); }
+       catch (const std::bad_alloc &) { __secapiresult=errSecAllocate; }
+       catch (...) { __secapiresult=errSecInternalComponent; }
+
+       if (serialData)
+               CFRelease(serialData);
+       if (issuerData)
+               CFRelease(issuerData);
+
+       return __secapiresult;
+}
+#endif
+
+OSStatus SecKeychainItemCreatePersistentReference(SecKeychainItemRef itemRef, CFDataRef *persistentItemRef)
+{
+#if !SECTRUST_OSX
+       BEGIN_SECAPI
+
+       KCThrowParamErrIf_(!itemRef || !persistentItemRef);
+       Item item;
+       bool isIdentityRef = (CFGetTypeID(itemRef) == SecIdentityGetTypeID()) ? true : false;
+       bool isCertificateRef = (CFGetTypeID(itemRef) == SecCertificateGetTypeID()) ? true : false;
+       if (isIdentityRef) {
+               SecPointer<Certificate> certificatePtr(Identity::required((SecIdentityRef)itemRef)->certificate());
+               SecCertificateRef certificateRef = certificatePtr->handle(false);
+               item = ItemImpl::required((SecKeychainItemRef)certificateRef);
+               item->copyPersistentReference(*persistentItemRef, true);
+       }
+       else if (isCertificateRef) {
+               item = ItemImpl::required(itemRef);
+               item->copyPersistentReference(*persistentItemRef, false);
+       }
+       else {
+               item = ItemImpl::required(itemRef);
+               item->copyPersistentReference(*persistentItemRef, false);
+       }
+
+       END_SECAPI
+#else
+    /* We're in the unified world, where SecCertificateRef is not a SecKeychainItemRef. */
+    if (!itemRef || !persistentItemRef) {
+        return errSecParam;
+    }
+    bool isIdentityRef = (CFGetTypeID(itemRef) == SecIdentityGetTypeID()) ? true : false;
+    bool isCertificateRef = (CFGetTypeID(itemRef) == SecCertificateGetTypeID()) ? true : false;
+    SecCertificateRef certRef = NULL;
+    if (isIdentityRef) {
+        SecIdentityCopyCertificate((SecIdentityRef)itemRef, &certRef);
+    }
+    else if (isCertificateRef) {
+        certRef = (SecCertificateRef) CFRetain(itemRef);
+    }
+    if (certRef) {
+        OSStatus status = SecKeychainItemCreatePersistentReferenceFromCertificate(certRef, persistentItemRef);
+        CFRelease(certRef);
+        return status;
+    }
+    // otherwise, not a certificate, so proceed as usual for keychain item
+
+    BEGIN_SECAPI
+    Item item = ItemImpl::required(itemRef);
+    item->copyPersistentReference(*persistentItemRef, false);
+    END_SECAPI
+
+#endif
+}
+
+OSStatus SecKeychainItemCopyFromPersistentReference(CFDataRef persistentItemRef, SecKeychainItemRef *itemRef)
+{
+    BEGIN_SECAPI
+
+    KCThrowParamErrIf_(!persistentItemRef || !itemRef);
+    CFTypeRef result = NULL;
+    bool isIdentityRef = false;
+    Item item = ItemImpl::makeFromPersistentReference(persistentItemRef, &isIdentityRef);
+    if (isIdentityRef) {
+        // item was stored as an identity, attempt to reconstitute it
+        SecPointer<Certificate> certificatePtr(static_cast<Certificate *>(item.get()));
+        StorageManager::KeychainList keychains;
+        globals().storageManager.optionalSearchList(NULL, keychains);
+        SecPointer<Identity> identityPtr(new Identity(keychains, certificatePtr));
+        result = identityPtr->handle();
+        KCThrowIf_( !result, errSecItemNotFound );
+    }
+    if (!result) {
+        result = item->handle();
+    }
+    *itemRef = (SecKeychainItemRef) result;
+
+#if SECTRUST_OSX
+    /* see if we should convert outgoing item to a unified SecCertificateRef */
+    SecItemClass tmpItemClass = Schema::itemClassFor(item->recordType());
+    if (tmpItemClass == kSecCertificateItemClass) {
+        SecPointer<Certificate> certificate(static_cast<Certificate *>(&*item));
+        CssmData certData = certificate->data();
+        CFDataRef data = NULL;
+        if (certData.Data && certData.Length) {
+            data = CFDataCreate(NULL, certData.Data, certData.Length);
+        }
+        if (!data) {
+            *itemRef = NULL;
+            if (certData.Data && !certData.Length) {
+                syslog(LOG_ERR, "WARNING: SecKeychainItemCopyFromPersistentReference skipped a zero-length certificate (data=0x%lX)",
+                       (uintptr_t)certData.Data);
+                return errSecDataNotAvailable;
+            }
+            else {
+                syslog(LOG_ERR, "WARNING: SecKeychainItemCopyFromPersistentReference failed to retrieve certificate data (length=%ld, data=0x%lX)",
+                       (long)certData.Length, (uintptr_t)certData.Data);
+                return errSecInternal;
+            }
+        }
+        SecKeychainItemRef tmpRef = *itemRef;
+        *itemRef = (SecKeychainItemRef) SecCertificateCreateWithKeychainItem(NULL, data, tmpRef);
+        if (data)
+            CFRelease(data);
+        if (tmpRef)
+            CFRelease(tmpRef);
+    }
+#endif
+
+       END_SECAPI
+}
+
+OSStatus SecKeychainItemCopyRecordIdentifier(SecKeychainItemRef itemRef, CFDataRef *recordIdentifier)
+{
+    BEGIN_SECAPI
+               CSSM_DATA data;
+               RequiredParam (recordIdentifier);
+               Item item = ItemImpl::required(itemRef);
+               item->copyRecordIdentifier (data);
+               *recordIdentifier = ::CFDataCreate(kCFAllocatorDefault, (UInt8*) data.Data, data.Length);
+               free (data.Data);
+       END_SECAPI
+}
+
+OSStatus
+SecKeychainItemCopyFromRecordIdentifier(SecKeychainRef keychainRef,
+                                                                               SecKeychainItemRef *itemRef,
+                                                                               CFDataRef recordIdentifier)
+{
+       BEGIN_SECAPI
+               // make a local Keychain reference
+               RequiredParam (keychainRef);
+               Keychain keychain = KeychainImpl::optional (keychainRef);
+               RequiredParam (itemRef);
+               RequiredParam (recordIdentifier);
+
+               Db db(keychain->database());
+
+               // make a raw database call to get the data
+               CSSM_DL_DB_HANDLE dbHandle = db.handle ();
+               CSSM_DB_UNIQUE_RECORD uniqueRecord;
+
+               // according to source, we should be able to reconsitute the uniqueRecord
+               // from the data we earlier retained
+
+               // prepare the record id
+               memset (&uniqueRecord, 0, sizeof (uniqueRecord));
+               uniqueRecord.RecordIdentifier.Data = (uint8*) CFDataGetBytePtr (recordIdentifier);
+               uniqueRecord.RecordIdentifier.Length = CFDataGetLength (recordIdentifier);
+
+               // convert this unique id to a CSSM_DB_UNIQUE_RECORD that works for the CSP/DL
+               CSSM_DB_UNIQUE_RECORD_PTR outputUniqueRecordPtr;
+               CSSM_RETURN result;
+               result = CSSM_DL_PassThrough (dbHandle, CSSM_APPLECSPDL_DB_CONVERT_RECORD_IDENTIFIER, &uniqueRecord, (void**) &outputUniqueRecordPtr);
+               KCThrowIf_(result != 0, errSecItemNotFound);
+
+               // from this, get the record type
+               CSSM_DB_RECORD_ATTRIBUTE_DATA attributeData;
+               memset (&attributeData, 0, sizeof (attributeData));
+
+               result = CSSM_DL_DataGetFromUniqueRecordId (dbHandle, outputUniqueRecordPtr, &attributeData, NULL);
+               KCThrowIf_(result != 0, errSecItemNotFound);
+               CSSM_DB_RECORDTYPE recordType = attributeData.DataRecordType;
+
+               // make the unique record item -- precursor to creation of a SecKeychainItemRef
+               DbUniqueRecord unique(db);
+               CSSM_DB_UNIQUE_RECORD_PTR *uniquePtr = unique;
+               *uniquePtr = outputUniqueRecordPtr;
+
+               unique->activate ();
+               Item item = keychain->item (recordType, unique);
+               if (itemRef)
+               {
+                       *itemRef = item->handle();
+               }
+       END_SECAPI
+}
+
+OSStatus SecKeychainItemCreateFromEncryptedContent(SecItemClass itemClass,
+               UInt32 length, const void *data, SecKeychainRef keychainRef,
+               SecAccessRef initialAccess, SecKeychainItemRef *itemRef, CFDataRef *localID)
+{
+    BEGIN_SECAPI
+               KCThrowParamErrIf_(length!=0 && data==NULL);
+
+               RequiredParam (localID);
+               RequiredParam (keychainRef);
+
+        Item item(itemClass, (uint32) 0, length, data, true);
+               if (initialAccess)
+                       item->setAccess(Access::required(initialAccess));
+
+        Keychain keychain = Keychain::optional(keychainRef);
+               if (!keychain->exists())
+               {
+                       MacOSError::throwMe(errSecNoSuchKeychain);      // Might be deleted or not available at this time.
+               }
+
+               item->doNotEncrypt ();
+               try
+               {
+                       keychain->add(item);
+               }
+               catch (const CommonError &err)
+               {
+                       if (err.osStatus () == errSecNoSuchClass)
+                       {
+                               // the only time this should happen is if the item is a certificate (for keychain syncing)
+                               if (itemClass == CSSM_DL_DB_RECORD_X509_CERTIFICATE)
+                               {
+                                       // create the certificate relation
+                                       Db db(keychain->database());
+
+                                       db->createRelation(CSSM_DL_DB_RECORD_X509_CERTIFICATE,
+                                               "CSSM_DL_DB_RECORD_X509_CERTIFICATE",
+                                               Schema::X509CertificateSchemaAttributeCount,
+                                               Schema::X509CertificateSchemaAttributeList,
+                                               Schema::X509CertificateSchemaIndexCount,
+                                               Schema::X509CertificateSchemaIndexList);
+                                       keychain->keychainSchema()->didCreateRelation(
+                                               CSSM_DL_DB_RECORD_X509_CERTIFICATE,
+                                               "CSSM_DL_DB_RECORD_X509_CERTIFICATE",
+                                               Schema::X509CertificateSchemaAttributeCount,
+                                               Schema::X509CertificateSchemaAttributeList,
+                                               Schema::X509CertificateSchemaIndexCount,
+                                               Schema::X509CertificateSchemaIndexList);
+
+                                       // add the item again
+                                       keychain->add(item);
+                               }
+                       }
+                       else
+                       {
+                               throw;
+                       }
+               }
+
+        if (itemRef)
+               *itemRef = item->handle();
+
+               CSSM_DATA recordID;
+               item->copyRecordIdentifier (recordID);
+
+               *localID = CFDataCreate(kCFAllocatorDefault, (UInt8*) recordID.Data, recordID.Length);
+               free (recordID.Data);
+       END_SECAPI
+}
+
+OSStatus SecKeychainItemCopyAttributesAndEncryptedData(SecKeychainItemRef itemRef, SecKeychainAttributeInfo *info,
+                                                                                                          SecItemClass *itemClass, SecKeychainAttributeList **attrList,
+                                                                                                          UInt32 *length, void **outData)
+{
+       BEGIN_SECAPI
+               Item item = ItemImpl::required(itemRef);
+               item->doNotEncrypt ();
+               item->getAttributesAndData(info, itemClass, attrList, length, outData);
+       END_SECAPI
+}
+
+OSStatus SecKeychainItemModifyEncryptedData(SecKeychainItemRef itemRef, UInt32 length, const void *data)
+{
+    BEGIN_SECAPI
+               Item item = ItemImpl::required(itemRef);
+               item->doNotEncrypt ();
+               item->modifyAttributesAndData(NULL, length, data);
+       END_SECAPI
+}