/* * Copyright (c) 2000-2004 Apple Computer, 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 #include #include #include #include #include // @@@ Remove this when SecKeychainItemFindFirst moves to SecKeychainSearch #include #include #include #include "SecBridge.h" #include "KCExceptions.h" #include "Access.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. // RefPointer 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 secdebug("kcitem", "SecKeychainItemGetTypeID()"); 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 secdebug("kcitem", "SecKeychainItemCreateFromContent(%lu, %p, %lu, %p, %p, %p)", itemClass, attrList, length, data, keychainRef, initialAccess); 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 secdebug("kcitem", "SecKeychainItemModifyContent(%p, %p, %lu, %p)", itemRef, attrList, length, data); Item item = ItemImpl::required(itemRef); item->modifyContent(attrList, length, data); END_SECAPI } OSStatus SecKeychainItemCopyContent(SecKeychainItemRef itemRef, SecItemClass *itemClass, SecKeychainAttributeList *attrList, UInt32 *length, void **outData) { BEGIN_SECAPI secdebug("kcitem", "SecKeychainItemCopyContent(%p, %p, %p, %p, %p)", itemRef, itemClass, attrList, length, outData); Item item = ItemImpl::required(itemRef); item->getContent(itemClass, attrList, length, outData); END_SECAPI } OSStatus SecKeychainItemFreeContent(SecKeychainAttributeList *attrList, void *data) { BEGIN_SECAPI secdebug("kcitem", "SecKeychainItemFreeContent(%p, %p)", attrList, data); ItemImpl::freeContent(attrList, data); END_SECAPI } OSStatus SecKeychainItemModifyAttributesAndData(SecKeychainItemRef itemRef, const SecKeychainAttributeList *attrList, UInt32 length, const void *data) { BEGIN_SECAPI secdebug("kcitem", "SecKeychainItemModifyAttributesAndData(%p, %p, %lu, %p)", itemRef, attrList, length, data); 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) { BEGIN_SECAPI secdebug("kcitem", "SecKeychainItemCopyAttributesAndData(%p, %p, %p, %p, %p, %p)", itemRef, info, itemClass, attrList, length, outData); Item item = ItemImpl::required(itemRef); item->getAttributesAndData(info, itemClass, attrList, length, outData); END_SECAPI } OSStatus SecKeychainItemFreeAttributesAndData(SecKeychainAttributeList *attrList, void *data) { BEGIN_SECAPI secdebug("kcitem", "SecKeychainItemFreeAttributesAndData(%p, %p)", attrList, data); ItemImpl::freeAttributesAndData(attrList, data); END_SECAPI } OSStatus SecKeychainItemDelete(SecKeychainItemRef itemRef) { BEGIN_SECAPI secdebug("kcitem", "SecKeychainItemFreeAttributesAndData(%p)", itemRef); Item item = ItemImpl::required( itemRef ); Keychain keychain = item->keychain(); KCThrowIf_( !keychain, errSecInvalidItemRef ); keychain->deleteItem( item ); // item must be persistent. END_SECAPI } OSStatus SecKeychainItemCopyKeychain(SecKeychainItemRef itemRef, SecKeychainRef* keychainRef) { BEGIN_SECAPI secdebug("kcitem", "SecKeychainItemCopyKeychain(%p, %p)", itemRef, keychainRef); // 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 } OSStatus SecKeychainItemCreateCopy(SecKeychainItemRef itemRef, SecKeychainRef destKeychainRef, SecAccessRef initialAccess, SecKeychainItemRef *itemCopy) { BEGIN_SECAPI secdebug("kcitem", "SecKeychainItemCreateCopy(%p, %p, %p, %p)", itemRef, destKeychainRef, initialAccess, itemCopy); 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 secdebug("kcitem", "SecKeychainItemGetUniqueRecordID(%p, %p)", itemRef, uniqueRecordID); Required(uniqueRecordID) = ItemImpl::required(itemRef)->dbUniqueRecord(); END_SECAPI } OSStatus SecKeychainItemGetDLDBHandle(SecKeychainItemRef itemRef, CSSM_DL_DB_HANDLE* dldbHandle) { BEGIN_SECAPI secdebug("kcitem", "SecKeychainItemGetDLDBHandle(%p, %p)", itemRef, dldbHandle); *dldbHandle = ItemImpl::required(itemRef)->keychain()->database()->handle(); END_SECAPI } OSStatus SecAccessCreateFromObject(CFTypeRef sourceRef, SecAccessRef *accessRef) { BEGIN_SECAPI secdebug("kcitem", "SecAccessCreateFromObject(%p, %p)", sourceRef, accessRef); Required(accessRef); // preflight SecPointer access = new Access(*aclBearer(sourceRef)); *accessRef = access->handle(); END_SECAPI } /*! */ OSStatus SecAccessModifyObject(SecAccessRef accessRef, CFTypeRef sourceRef) { BEGIN_SECAPI secdebug("kcitem", "SecAccessModifyObject(%p, %p)", accessRef, sourceRef); Access::required(accessRef)->setAccess(*aclBearer(sourceRef), true); END_SECAPI } OSStatus SecKeychainItemCopyAccess(SecKeychainItemRef itemRef, SecAccessRef* accessRef) { BEGIN_SECAPI secdebug("kcitem", "SecKeychainItemCopyAccess(%p, %p)", itemRef, accessRef); Required(accessRef); // preflight SecPointer access = new Access(*aclBearer(reinterpret_cast(itemRef))); *accessRef = access->handle(); END_SECAPI } OSStatus SecKeychainItemSetAccess(SecKeychainItemRef itemRef, SecAccessRef accessRef) { BEGIN_SECAPI secdebug("kcitem", "SecKeychainItemSetAccess(%p, %p)", itemRef, accessRef); Access::required(accessRef)->setAccess(*aclBearer(reinterpret_cast(itemRef)), true); 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(paramErr); CssmDataContainer aData; ItemImpl::required(itemRef)->getData(aData); if (actualLength) *actualLength = aData.length(); if (data) { // Make sure the buffer is big enough if (aData.length() > maxLength) MacOSError::throwMe(errKCBufferTooSmall); 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 errKCItemNotFound; *itemRef=item->handle(); if (searchRef) *searchRef=cursor->handle(); END_SECAPI } OSStatus SecKeychainItemCreatePersistentReference(SecKeychainItemRef itemRef, CFDataRef *persistentItemRef) { BEGIN_SECAPI KCThrowParamErrIf_(!itemRef || !persistentItemRef); Item item = ItemImpl::required(itemRef); DLDbIdentifier dlDbIdentifier = item->keychain()->dlDbIdentifier(); DLDbIdentifier newDlDbIdentifier(dlDbIdentifier.ssuid(), DLDbListCFPref::AbbreviatedPath(item->keychain()->name()).c_str(), dlDbIdentifier.dbLocation()); PrimaryKey primaryKey = item->primaryKey(); KCThrowIf_( !dlDbIdentifier || !primaryKey, errSecItemNotFound ); // item not in any keychain? NameValueDictionary dict; NameValueDictionary::MakeNameValueDictionaryFromDLDbIdentifier(newDlDbIdentifier, dict); CssmData* pKey = primaryKey; dict.Insert (new NameValuePair(ITEM_KEY, *pKey)); // flatten the NameValueDictionary CssmData dictData; dict.Export(dictData); *persistentItemRef = ::CFDataCreate(kCFAllocatorDefault, dictData.Data, dictData.Length); free (dictData.Data); END_SECAPI } OSStatus SecKeychainItemCopyFromPersistentReference(CFDataRef persistentItemRef, SecKeychainItemRef *itemRef) { BEGIN_SECAPI KCThrowParamErrIf_(!persistentItemRef || !itemRef); // make a NameValueDictionary from the data we received CssmData dictData((void*)::CFDataGetBytePtr(persistentItemRef), ::CFDataGetLength(persistentItemRef)); NameValueDictionary dict(dictData); Keychain keychain; Item item; // make sure we have a database identifier if (dict.FindByName(SSUID_KEY) != 0) { DLDbIdentifier dlDbIdentifier = NameValueDictionary::MakeDLDbIdentifierFromNameValueDictionary(dict); DLDbIdentifier newDlDbIdentifier(dlDbIdentifier.ssuid(), DLDbListCFPref::ExpandTildesInPath(dlDbIdentifier.dbName()).c_str(), dlDbIdentifier.dbLocation()); keychain = globals().storageManager.keychain(newDlDbIdentifier); const NameValuePair* aDictItem = dict.FindByName(ITEM_KEY); if (aDictItem && keychain) { PrimaryKey primaryKey(aDictItem->Value()); item = keychain->item(primaryKey); } // We can safely release the global API lock now since keychain and item // are CFRetained and will be until they go out of scope. } KCThrowIf_( !item, errSecItemNotFound ); *itemRef = item->handle(); 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 secdebug("kcitem", "SecKeychainItemCreateFromEncryptedContent(%lu, %lu, %p, %p, %p)", itemClass, length, data, keychainRef, initialAccess); 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, sizeof (localID)); END_SECAPI } OSStatus SecKeychainItemCopyAttributesAndEncryptedData(SecKeychainItemRef itemRef, SecKeychainAttributeInfo *info, SecItemClass *itemClass, SecKeychainAttributeList **attrList, UInt32 *length, void **outData) { BEGIN_SECAPI secdebug("kcitem", "SecKeychainItemCopyAttributesAndEncryptedData(%p, %p, %p, %p, %p, %p)", itemRef, info, itemClass, attrList, length, outData); 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 secdebug("kcitem", "SecKeychainItemModifyAttributesAndData(%p, %lu, %p)", itemRef, length, data); Item item = ItemImpl::required(itemRef); item->doNotEncrypt (); item->modifyAttributesAndData(NULL, length, data); END_SECAPI }