--- /dev/null
+/*
+ * Copyright (c) 2006-2014 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 "SecBridge.h"
+#include "SecInternal.h"
+#include <CoreFoundation/CoreFoundation.h>
+#include <security_utilities/cfutilities.h>
+#include <Security/SecBase.h>
+#include <Security/SecKeychainItem.h>
+#include <Security/SecCertificate.h>
+#include <sys/param.h>
+#include "cssmdatetime.h"
+#include "SecItem.h"
+#include "SecItemPriv.h"
+#include "SecIdentitySearchPriv.h"
+#include "SecKeychainPriv.h"
+#include "SecCertificatePriv.h"
+#include "SecCertificatePrivP.h"
+#include "TrustAdditions.h"
+
+#include <AssertMacros.h>
+#include <syslog.h>
+
+#include <Security/SecTrustedApplication.h>
+#include <Security/SecTrustedApplicationPriv.h>
+#include <Security/SecCode.h>
+#include <Security/SecCodePriv.h>
+#include <Security/SecRequirement.h>
+
+const uint8_t kUUIDStringLength = 36;
+
+OSStatus SecItemAdd_osx(CFDictionaryRef attributes, CFTypeRef *result);
+OSStatus SecItemCopyMatching_osx(CFDictionaryRef query, CFTypeRef *result);
+OSStatus SecItemUpdate_osx(CFDictionaryRef query, CFDictionaryRef attributesToUpdate);
+OSStatus SecItemDelete_osx(CFDictionaryRef query);
+
+extern "C" {
+OSStatus SecItemAdd_ios(CFDictionaryRef attributes, CFTypeRef *result);
+OSStatus SecItemCopyMatching_ios(CFDictionaryRef query, CFTypeRef *result);
+OSStatus SecItemUpdate_ios(CFDictionaryRef query, CFDictionaryRef attributesToUpdate);
+OSStatus SecItemDelete_ios(CFDictionaryRef query);
+
+CFTypeRef SecItemCreateFromAttributeDictionary(CFDictionaryRef refAttributes);
+CFTypeRef SecItemCopyMergedResults(CFDictionaryRef query, CFTypeRef result_osx, CFTypeRef result_ios);
+OSStatus SecItemValidateAppleApplicationGroupAccess(CFStringRef group);
+CFDictionaryRef SecItemCopyTranslatedAttributes(CFDictionaryRef inOSXDict, CFTypeRef itemClass,
+ bool iOSOut, bool pruneMatch, bool pruneSync, bool pruneReturn, bool pruneData, bool pruneAccess);
+}
+
+static Boolean SecItemSynchronizable(CFDictionaryRef query);
+
+static void secitemlog(int priority, const char *format, ...)
+{
+#ifndef NDEBUG
+ // log everything
+#else
+ if (priority < LOG_NOTICE) // log warnings and errors
+#endif
+ {
+ va_list list;
+ va_start(list, format);
+ vsyslog(priority, format, list);
+ va_end(list);
+ }
+}
+
+static void secitemshow(CFTypeRef obj, const char *context)
+{
+#ifndef NDEBUG
+ CFStringRef desc = CFCopyDescription(obj);
+ if (!desc) return;
+
+ CFIndex length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(desc), kCFStringEncodingUTF8) + 1;
+ char* buffer = (char*) malloc(length);
+ if (buffer) {
+ Boolean converted = CFStringGetCString(desc, buffer, length, kCFStringEncodingUTF8);
+ if (converted) {
+ const char *prefix = (context) ? context : "";
+ const char *separator = (context) ? " " : "";
+ secitemlog(LOG_NOTICE, "%s%s%s", prefix, separator, buffer);
+ }
+ free(buffer);
+ }
+ CFRelease(desc);
+#endif
+}
+
+
+#define CFDataGetBytePtrVoid CFDataGetBytePtr
+
+#pragma mark SecItem private utility functions
+
+/******************************************************************************/
+
+struct ProtocolAttributeInfo {
+ const CFTypeRef *protocolValue;
+ SecProtocolType protocolType;
+};
+
+static ProtocolAttributeInfo gProtocolTypes[] = {
+ { &kSecAttrProtocolFTP, kSecProtocolTypeFTP },
+ { &kSecAttrProtocolFTPAccount, kSecProtocolTypeFTPAccount },
+ { &kSecAttrProtocolHTTP, kSecProtocolTypeHTTP },
+ { &kSecAttrProtocolIRC, kSecProtocolTypeIRC },
+ { &kSecAttrProtocolNNTP, kSecProtocolTypeNNTP },
+ { &kSecAttrProtocolPOP3, kSecProtocolTypePOP3 },
+ { &kSecAttrProtocolSMTP, kSecProtocolTypeSMTP },
+ { &kSecAttrProtocolSOCKS, kSecProtocolTypeSOCKS },
+ { &kSecAttrProtocolIMAP, kSecProtocolTypeIMAP },
+ { &kSecAttrProtocolLDAP, kSecProtocolTypeLDAP },
+ { &kSecAttrProtocolAppleTalk, kSecProtocolTypeAppleTalk },
+ { &kSecAttrProtocolAFP, kSecProtocolTypeAFP },
+ { &kSecAttrProtocolTelnet, kSecProtocolTypeTelnet },
+ { &kSecAttrProtocolSSH, kSecProtocolTypeSSH },
+ { &kSecAttrProtocolFTPS, kSecProtocolTypeFTPS },
+ { &kSecAttrProtocolHTTPS, kSecProtocolTypeHTTPS },
+ { &kSecAttrProtocolHTTPProxy, kSecProtocolTypeHTTPProxy },
+ { &kSecAttrProtocolHTTPSProxy, kSecProtocolTypeHTTPSProxy },
+ { &kSecAttrProtocolFTPProxy, kSecProtocolTypeFTPProxy },
+ { &kSecAttrProtocolSMB, kSecProtocolTypeSMB },
+ { &kSecAttrProtocolRTSP, kSecProtocolTypeRTSP },
+ { &kSecAttrProtocolRTSPProxy, kSecProtocolTypeRTSPProxy },
+ { &kSecAttrProtocolDAAP, kSecProtocolTypeDAAP },
+ { &kSecAttrProtocolEPPC, kSecProtocolTypeEPPC },
+ { &kSecAttrProtocolIPP, kSecProtocolTypeIPP },
+ { &kSecAttrProtocolNNTPS, kSecProtocolTypeNNTPS },
+ { &kSecAttrProtocolLDAPS, kSecProtocolTypeLDAPS },
+ { &kSecAttrProtocolTelnetS, kSecProtocolTypeTelnetS },
+ { &kSecAttrProtocolIMAPS, kSecProtocolTypeIMAPS },
+ { &kSecAttrProtocolIRCS, kSecProtocolTypeIRCS },
+ { &kSecAttrProtocolPOP3S, kSecProtocolTypePOP3S }
+};
+
+static const int kNumberOfProtocolTypes = sizeof(gProtocolTypes) / sizeof(ProtocolAttributeInfo);
+
+/*
+ * _SecProtocolTypeForSecAttrProtocol converts a SecAttrProtocol to a SecProtocolType.
+ */
+static SecProtocolType
+_SecProtocolTypeForSecAttrProtocol(
+ CFTypeRef protocol)
+{
+ SecProtocolType result = kSecProtocolTypeAny;
+
+ if (protocol != NULL) {
+ CFIndex count;
+ for (count=0; count<kNumberOfProtocolTypes; count++) {
+ if (CFEqual(protocol, *(gProtocolTypes[count].protocolValue))) {
+ result = gProtocolTypes[count].protocolType;
+ break;
+ }
+ }
+ }
+
+ return result;
+}
+
+/*
+ * _SecAttrProtocolForSecProtocolType converts a SecProtocolType to a SecAttrProtocol.
+ */
+static CFTypeRef
+_SecAttrProtocolForSecProtocolType(
+ SecProtocolType protocolType)
+{
+ CFTypeRef result = NULL;
+ CFIndex count;
+ for (count=0; count<kNumberOfProtocolTypes; count++) {
+ if (gProtocolTypes[count].protocolType == protocolType) {
+ result = *(gProtocolTypes[count].protocolValue);
+ break;
+ }
+ }
+
+ return result;
+}
+
+
+/******************************************************************************/
+
+struct AuthenticationAttributeInfo {
+ const CFTypeRef *authValue;
+ SecAuthenticationType authType;
+};
+
+static AuthenticationAttributeInfo gAuthTypes[] = {
+ { &kSecAttrAuthenticationTypeNTLM, kSecAuthenticationTypeNTLM },
+ { &kSecAttrAuthenticationTypeMSN, kSecAuthenticationTypeMSN },
+ { &kSecAttrAuthenticationTypeDPA, kSecAuthenticationTypeDPA },
+ { &kSecAttrAuthenticationTypeRPA, kSecAuthenticationTypeRPA },
+ { &kSecAttrAuthenticationTypeHTTPBasic, kSecAuthenticationTypeHTTPBasic },
+ { &kSecAttrAuthenticationTypeHTTPDigest, kSecAuthenticationTypeHTTPDigest },
+ { &kSecAttrAuthenticationTypeHTMLForm, kSecAuthenticationTypeHTMLForm },
+ { &kSecAttrAuthenticationTypeDefault, kSecAuthenticationTypeDefault }
+};
+
+static const int kNumberOfAuthenticationTypes = sizeof(gAuthTypes) / sizeof(AuthenticationAttributeInfo);
+
+/*
+ * _SecAuthenticationTypeForSecAttrAuthenticationType converts a
+ * SecAttrAuthenticationType to a SecAuthenticationType.
+ */
+static SecAuthenticationType
+_SecAuthenticationTypeForSecAttrAuthenticationType(
+ CFTypeRef authenticationType)
+{
+ SecAuthenticationType result = kSecAuthenticationTypeAny;
+
+ if (authenticationType != NULL) {
+ CFIndex count;
+ for (count=0; count<kNumberOfAuthenticationTypes; count++) {
+ if (CFEqual(authenticationType, *(gAuthTypes[count].authValue))) {
+ result = gAuthTypes[count].authType;
+ break;
+ }
+ }
+ }
+
+ return result;
+}
+
+/*
+ * _SecAttrAuthenticationTypeForSecAuthenticationType converts a SecAuthenticationType
+ * to a SecAttrAuthenticationType.
+ */
+static CFTypeRef
+_SecAttrAuthenticationTypeForSecAuthenticationType(
+ SecAuthenticationType authenticationType)
+{
+ CFTypeRef result = NULL;
+ CFIndex count;
+ for (count=0; count<kNumberOfAuthenticationTypes; count++) {
+ if (gAuthTypes[count].authType == authenticationType) {
+ result = *(gAuthTypes[count].authValue);
+ break;
+ }
+ }
+
+ return result;
+}
+
+
+/******************************************************************************/
+
+struct KeyAlgorithmInfo {
+ const CFTypeRef *keyType;
+ UInt32 keyValue;
+};
+
+static KeyAlgorithmInfo gKeyTypes[] = {
+ { &kSecAttrKeyTypeRSA, CSSM_ALGID_RSA },
+ { &kSecAttrKeyTypeDSA, CSSM_ALGID_DSA },
+ { &kSecAttrKeyTypeAES, CSSM_ALGID_AES },
+ { &kSecAttrKeyTypeDES, CSSM_ALGID_DES },
+ { &kSecAttrKeyType3DES, CSSM_ALGID_3DES },
+ { &kSecAttrKeyTypeRC4, CSSM_ALGID_RC4 },
+ { &kSecAttrKeyTypeRC2, CSSM_ALGID_RC2 },
+ { &kSecAttrKeyTypeCAST, CSSM_ALGID_CAST },
+ { &kSecAttrKeyTypeECDSA, CSSM_ALGID_ECDSA },
+ { &kSecAttrKeyTypeEC, CSSM_ALGID_ECDSA }
+};
+
+static const int kNumberOfKeyTypes = sizeof(gKeyTypes) / sizeof (KeyAlgorithmInfo);
+
+
+static UInt32 _SecAlgorithmTypeFromSecAttrKeyType(
+ CFTypeRef keyTypeRef)
+{
+ UInt32 keyAlgValue = 0;
+ if (CFStringGetTypeID() != CFGetTypeID(keyTypeRef))
+ return keyAlgValue;
+
+ int ix;
+ for (ix=0; ix<kNumberOfKeyTypes; ix++) {
+ if (CFEqual(keyTypeRef, *(gKeyTypes[ix].keyType))) {
+ keyAlgValue = gKeyTypes[ix].keyValue;
+ return keyAlgValue;
+ }
+ }
+
+ //%%%TODO try to convert the input string to a number here
+
+ return keyAlgValue;
+}
+
+
+enum ItemRepresentation
+{
+ kStringRepresentation,
+ kDataRepresentation,
+ kNumberRepresentation,
+ kBooleanRepresentation,
+ kDateRepresentation
+};
+
+
+struct InternalAttributeListInfo
+{
+ UInt32 oldItemType;
+ const CFTypeRef *newItemType;
+ ItemRepresentation itemRepresentation;
+};
+
+
+static InternalAttributeListInfo gGenericPasswordAttributes[] =
+{
+ { kSecCreationDateItemAttr, &kSecAttrCreationDate, kDateRepresentation },
+ { kSecModDateItemAttr, &kSecAttrModificationDate, kDateRepresentation },
+ { kSecDescriptionItemAttr, &kSecAttrDescription, kStringRepresentation },
+ { kSecCommentItemAttr, &kSecAttrComment, kStringRepresentation },
+ { kSecCreatorItemAttr, &kSecAttrCreator, kNumberRepresentation }, // UInt32, a.k.a. FourCharCode
+ { kSecTypeItemAttr, &kSecAttrType, kNumberRepresentation }, // UInt32, a.k.a. FourCharCode
+ { kSecLabelItemAttr, &kSecAttrLabel, kStringRepresentation },
+ { kSecInvisibleItemAttr, &kSecAttrIsInvisible, kBooleanRepresentation },
+ { kSecNegativeItemAttr, &kSecAttrIsNegative, kBooleanRepresentation },
+ { kSecAccountItemAttr, &kSecAttrAccount, kStringRepresentation },
+ { kSecServiceItemAttr, &kSecAttrService, kStringRepresentation },
+ { kSecGenericItemAttr, &kSecAttrGeneric, kDataRepresentation }
+};
+
+static const int kNumberOfGenericPasswordAttributes = sizeof(gGenericPasswordAttributes) / sizeof (InternalAttributeListInfo);
+
+#if 0
+static InternalAttributeListInfo gInternetPasswordAttributes[] =
+{
+ { kSecCreationDateItemAttr, &kSecAttrCreationDate, kDateRepresentation },
+ { kSecModDateItemAttr, &kSecAttrModificationDate, kDateRepresentation },
+ { kSecDescriptionItemAttr, &kSecAttrDescription, kStringRepresentation },
+ { kSecCommentItemAttr, &kSecAttrComment, kStringRepresentation },
+ { kSecCreatorItemAttr, &kSecAttrCreator, kNumberRepresentation }, // UInt32, a.k.a. FourCharCode
+ { kSecTypeItemAttr, &kSecAttrType, kNumberRepresentation }, // UInt32, a.k.a. FourCharCode
+ { kSecLabelItemAttr, &kSecAttrLabel, kStringRepresentation },
+ { kSecInvisibleItemAttr, &kSecAttrIsInvisible, kBooleanRepresentation },
+ { kSecNegativeItemAttr, &kSecAttrIsNegative, kBooleanRepresentation },
+ { kSecAccountItemAttr, &kSecAttrAccount, kStringRepresentation },
+ { kSecSecurityDomainItemAttr, &kSecAttrSecurityDomain, kStringRepresentation },
+ { kSecServerItemAttr, &kSecAttrServer, kStringRepresentation },
+ { kSecAuthenticationTypeItemAttr, &kSecAttrAuthenticationType, kStringRepresentation }, // maps from UInt32 value to string constant
+ { kSecPortItemAttr, &kSecAttrPort, kNumberRepresentation },
+ { kSecPathItemAttr, &kSecAttrPath, kStringRepresentation }
+};
+
+static const int kNumberOfInternetPasswordAttributes = sizeof(gInternetPasswordAttributes) / sizeof (InternalAttributeListInfo);
+#endif
+
+static InternalAttributeListInfo gCertificateAttributes[] =
+{
+ { kSecLabelItemAttr, &kSecAttrLabel, kStringRepresentation },
+ { kSecSubjectItemAttr, &kSecAttrSubject, kDataRepresentation },
+ { kSecIssuerItemAttr, &kSecAttrIssuer, kDataRepresentation },
+ { kSecSerialNumberItemAttr, &kSecAttrSerialNumber, kDataRepresentation },
+ { kSecPublicKeyHashItemAttr, &kSecAttrPublicKeyHash, kDataRepresentation },
+ { kSecSubjectKeyIdentifierItemAttr, &kSecAttrSubjectKeyID, kDataRepresentation },
+ { kSecCertTypeItemAttr, &kSecAttrCertificateType, kDataRepresentation },
+ { kSecCertEncodingItemAttr, &kSecAttrCertificateEncoding, kDataRepresentation }
+};
+
+static const int kNumberOfCertificateAttributes = sizeof(gCertificateAttributes) / sizeof(InternalAttributeListInfo);
+
+
+static InternalAttributeListInfo gKeyAttributes[] =
+{
+ { kSecKeyKeyClass, &kSecAttrKeyClass, kStringRepresentation }, // key class maps from UInt32 value to string constant
+ { kSecKeyPrintName, &kSecAttrLabel, kStringRepresentation }, // note that "print name" maps to the user-visible label
+// { kSecKeyAlias, /* not yet exposed by SecItem */, kDataRepresentation },
+ { kSecKeyPermanent, &kSecAttrIsPermanent, kBooleanRepresentation },
+// { kSecKeyPrivate, /* not yet exposed by SecItem */, kBooleanRepresentation },
+// { kSecKeyModifiable, /* not yet exposed by SecItem */, kBooleanRepresentation },
+ { kSecKeyLabel, &kSecAttrApplicationLabel, kDataRepresentation }, // this contains the hash of the key (or the public key hash, if asymmetric) as a CFData. Legacy keys may contain a UUID as a CFString
+ { kSecKeyApplicationTag, &kSecAttrApplicationTag, kDataRepresentation },
+// { kSecKeyKeyCreator, /* not yet exposed by SecItem */, kStringRepresentation }, // this is the GUID of the CSP that owns this key
+ { kSecKeyKeyType, &kSecAttrKeyType, kStringRepresentation }, // algorithm type is given as a string constant (e.g. kSecAttrKeyTypeAES)
+ { kSecKeyKeySizeInBits, &kSecAttrKeySizeInBits, kNumberRepresentation },
+ { kSecKeyEffectiveKeySize, &kSecAttrEffectiveKeySize, kNumberRepresentation },
+// { kSecKeyStartDate, /* not yet exposed by SecItem */, kDateRepresentation },
+// { kSecKeyEndDate, /* not yet exposed by SecItem */, kDateRepresentation },
+// { kSecKeySensitive, /* not yet exposed by SecItem */, kBooleanRepresentation },
+// { kSecKeyAlwaysSensitive, /* not yet exposed by SecItem */, kBooleanRepresentation },
+// { kSecKeyExtractable, /* not yet exposed by SecItem */, kBooleanRepresentation },
+// { kSecKeyNeverExtractable, /* not yet exposed by SecItem */, kBooleanRepresentation },
+ { kSecKeyEncrypt, &kSecAttrCanEncrypt, kBooleanRepresentation },
+ { kSecKeyDecrypt, &kSecAttrCanDecrypt, kBooleanRepresentation },
+ { kSecKeyDerive, &kSecAttrCanDerive, kBooleanRepresentation },
+ { kSecKeySign, &kSecAttrCanSign, kBooleanRepresentation },
+ { kSecKeyVerify, &kSecAttrCanVerify, kBooleanRepresentation },
+// { kSecKeySignRecover, /* not yet exposed by SecItem */, kBooleanRepresentation },
+// { kSecKeyVerifyRecover, /* not yet exposed by SecItem */, kBooleanRepresentation },
+ { kSecKeyWrap, &kSecAttrCanWrap, kBooleanRepresentation },
+ { kSecKeyUnwrap, &kSecAttrCanUnwrap, kBooleanRepresentation }
+};
+
+static const int kNumberOfKeyAttributes = sizeof(gKeyAttributes) / sizeof(InternalAttributeListInfo);
+
+
+static void* CloneDataByType(ItemRepresentation type, CFTypeRef value, UInt32& length)
+{
+ switch (type)
+ {
+ case kStringRepresentation:
+ {
+ if (CFStringGetTypeID() != CFGetTypeID(value)) {
+ length = 0;
+ return NULL;
+ }
+ CFIndex maxLength = CFStringGetMaximumSizeForEncoding(CFStringGetLength((CFStringRef) value), kCFStringEncodingUTF8) + 1;
+ char* buffer = (char*) malloc(maxLength);
+ Boolean converted = CFStringGetCString((CFStringRef) value, buffer, maxLength, kCFStringEncodingUTF8);
+ if (converted) {
+ length = (UInt32)strlen(buffer);
+ }
+ else {
+ length = 0;
+ free(buffer);
+ buffer = NULL;
+ }
+ return buffer;
+ }
+
+ case kDataRepresentation:
+ {
+ if (CFStringGetTypeID() == CFGetTypeID(value)) {
+ // We may have a string here, since the key label may be a GUID for the symmetric keys
+ CFIndex maxLength = CFStringGetMaximumSizeForEncoding(CFStringGetLength((CFStringRef) value), kCFStringEncodingUTF8) + 1;
+ char* buffer = (char*) malloc(maxLength);
+ Boolean converted = CFStringGetCString((CFStringRef) value, buffer, maxLength, kCFStringEncodingUTF8);
+ if (converted) {
+ length = (UInt32)strlen(buffer);
+ }
+ else {
+ length = 0;
+ free(buffer);
+ buffer = NULL;
+ }
+ return buffer;
+ }
+
+ if (CFDataGetTypeID() != CFGetTypeID(value)) {
+ length = 0;
+ return NULL;
+ }
+ length = (UInt32)CFDataGetLength((CFDataRef) value);
+ uint8_t* buffer = (uint8_t*) malloc(length);
+ CFDataGetBytes((CFDataRef) value, CFRangeMake(0, length), buffer);
+ return buffer;
+ }
+
+ case kNumberRepresentation:
+ {
+ if (CFNumberGetTypeID() != CFGetTypeID(value)) {
+ length = 0;
+ return NULL;
+ }
+ uint32_t* buffer = (uint32_t*) malloc(sizeof(uint32_t));
+ Boolean converted = CFNumberGetValue((CFNumberRef) value, kCFNumberSInt32Type, buffer);
+ if (converted) {
+ length = sizeof(uint32_t);
+ }
+ else {
+ length = 0;
+ free(buffer);
+ buffer = NULL;
+ }
+ return buffer;
+ }
+
+ case kBooleanRepresentation:
+ {
+ if (CFBooleanGetTypeID() != CFGetTypeID(value)) {
+ length = 0;
+ return NULL;
+ }
+ uint32_t* buffer = (uint32_t*) malloc(sizeof(uint32_t));
+ *buffer = (CFEqual(kCFBooleanTrue, value)) ? 1 : 0;
+ length = sizeof(uint32_t);
+ return buffer;
+ }
+
+ case kDateRepresentation:
+ {
+ if (CFDateGetTypeID() != CFGetTypeID(value)) {
+ length = 0;
+ return NULL;
+ }
+ char* buffer = (char*) calloc(1, 32); // max length of a CSSM date string
+ CSSMDateTimeUtils::CFDateToCssmDate((CFDateRef) value, buffer);
+ length = (UInt32)strlen(buffer);
+ return buffer;
+ }
+
+ default:
+ {
+ length = 0;
+ return NULL;
+ }
+ }
+}
+
+
+static OSStatus
+_ConvertNewFormatToOldFormat(
+ CFAllocatorRef allocator,
+ const InternalAttributeListInfo* info,
+ int infoNumItems,
+ CFDictionaryRef dictionaryRef,
+ SecKeychainAttributeList* &attrList
+ )
+{
+ // get the keychain attributes array from the data item
+ // here's the problem. On the one hand, we have a dictionary that is purported to contain
+ // attributes for our type. On the other hand, the dictionary may contain items we don't support,
+ // and we therefore don't know how many attributes we will have unless we count them first
+
+ // setup the return
+ attrList = (SecKeychainAttributeList*) calloc(1, sizeof(SecKeychainAttributeList));
+
+ // make storage to extract the dictionary items
+ CFIndex itemsInDictionary = CFDictionaryGetCount(dictionaryRef);
+ CFTypeRef keys[itemsInDictionary];
+ CFTypeRef values[itemsInDictionary];
+
+ CFTypeRef *keysPtr = keys;
+ CFTypeRef *valuesPtr = values;
+
+ CFDictionaryGetKeysAndValues(dictionaryRef, keys, values);
+
+ // count the number of items we are interested in
+ CFIndex count = 0;
+ CFIndex i;
+
+ // since this is one of those nasty order n^2 loops, we cache as much stuff as possible so that
+ // we don't pay the price for this twice
+ SecKeychainAttrType tags[itemsInDictionary];
+ ItemRepresentation types[itemsInDictionary];
+
+ for (i = 0; i < itemsInDictionary; ++i)
+ {
+ CFTypeRef key = keysPtr[i];
+
+ int j;
+ for (j = 0; j < infoNumItems; ++j)
+ {
+ if (CFEqual(*(info[j].newItemType), key))
+ {
+ tags[i] = info[j].oldItemType;
+ types[i] = info[j].itemRepresentation;
+ count += 1;
+ break;
+ }
+ }
+
+ if (j >= infoNumItems)
+ {
+ // if we got here, we aren't interested in this item.
+ valuesPtr[i] = NULL;
+ }
+ }
+
+ // now we can make the result array
+ attrList->count = (UInt32)count;
+ attrList->attr = (SecKeychainAttribute*) malloc(sizeof(SecKeychainAttribute) * count);
+
+ // fill out the array
+ int resultPointer = 0;
+ for (i = 0; i < itemsInDictionary; ++i)
+ {
+ if (values[i] != NULL)
+ {
+ attrList->attr[resultPointer].tag = tags[i];
+
+ // we have to clone the data pointer. The caller will need to make sure to throw these away
+ // with _FreeAttrList when it is done...
+ attrList->attr[resultPointer].data = CloneDataByType(types[i], valuesPtr[i], attrList->attr[resultPointer].length);
+ resultPointer += 1;
+ }
+ }
+
+ return errSecSuccess;
+}
+
+
+
+static OSStatus
+_ConvertOldFormatToNewFormat(
+ CFAllocatorRef allocator,
+ const InternalAttributeListInfo* info,
+ int infoNumItems,
+ SecKeychainItemRef itemRef,
+ CFMutableDictionaryRef& dictionaryRef)
+{
+ SecKeychainAttributeList list;
+ list.count = infoNumItems;
+ list.attr = (SecKeychainAttribute*) calloc(infoNumItems, sizeof(SecKeychainAttribute));
+
+ // fill out the array. We only need to fill in the tags, since calloc zeros what it returns
+ int i;
+ for (i = 0; i < infoNumItems; ++i)
+ {
+ list.attr[i].tag = info[i].oldItemType;
+ }
+
+ OSStatus result = SecKeychainItemCopyContent(itemRef, NULL, &list, NULL, NULL);
+ if (result != errSecSuccess)
+ {
+ dictionaryRef = NULL;
+ free(list.attr);
+ return result;
+ }
+
+ // create the dictionary
+ dictionaryRef = CFDictionaryCreateMutable(allocator, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+
+ // add the pairs
+ for (i = 0; i < infoNumItems; ++i)
+ {
+ if (list.attr[i].data == NULL)
+ continue;
+
+ switch (info[i].itemRepresentation)
+ {
+ case kStringRepresentation:
+ {
+ CFStringRef stringRef;
+ if (info[i].oldItemType == kSecKeyKeyClass) {
+ // special case: kSecKeyKeyClass is a UInt32 value that maps to a CFStringRef constant
+ uint32_t keyRecordValue = *((uint32_t*)list.attr[i].data);
+ bool retainString = true;
+ switch (keyRecordValue) {
+ case CSSM_DL_DB_RECORD_PUBLIC_KEY :
+ stringRef = (CFStringRef) kSecAttrKeyClassPublic;
+ break;
+ case CSSM_DL_DB_RECORD_PRIVATE_KEY:
+ stringRef = (CFStringRef) kSecAttrKeyClassPrivate;
+ break;
+ case CSSM_DL_DB_RECORD_SYMMETRIC_KEY:
+ stringRef = (CFStringRef) kSecAttrKeyClassSymmetric;
+ break;
+ default:
+ stringRef = CFStringCreateWithFormat(allocator, NULL, CFSTR("%d"), keyRecordValue);
+ break;
+ }
+ if (stringRef) {
+ if (retainString) CFRetain(stringRef);
+ CFDictionaryAddValue(dictionaryRef, *(info[i].newItemType), stringRef);
+ CFRelease(stringRef);
+ }
+ }
+ else if (info[i].oldItemType == kSecKeyKeyType) {
+ // special case: kSecKeyKeyType is a UInt32 value that maps to a CFStringRef constant
+ uint32_t keyAlgValue = *((uint32_t*)list.attr[i].data);
+ bool retainString = true;
+ switch (keyAlgValue) {
+ case CSSM_ALGID_RSA :
+ stringRef = (CFStringRef) kSecAttrKeyTypeRSA;
+ break;
+ case CSSM_ALGID_DSA :
+ stringRef = (CFStringRef) kSecAttrKeyTypeDSA;
+ break;
+ case CSSM_ALGID_AES :
+ stringRef = (CFStringRef) kSecAttrKeyTypeAES;
+ break;
+ case CSSM_ALGID_DES :
+ stringRef = (CFStringRef) kSecAttrKeyTypeDES;
+ break;
+ case CSSM_ALGID_3DES :
+ stringRef = (CFStringRef) kSecAttrKeyType3DES;
+ break;
+ case CSSM_ALGID_RC4 :
+ stringRef = (CFStringRef) kSecAttrKeyTypeRC4;
+ break;
+ case CSSM_ALGID_RC2 :
+ stringRef = (CFStringRef) kSecAttrKeyTypeRC2;
+ break;
+ case CSSM_ALGID_CAST :
+ stringRef = (CFStringRef) kSecAttrKeyTypeCAST;
+ break;
+ case CSSM_ALGID_ECDSA :
+ stringRef = (CFStringRef) kSecAttrKeyTypeEC;
+ break;
+ default :
+ stringRef = CFStringCreateWithFormat(allocator, NULL, CFSTR("%d"), keyAlgValue);
+ retainString = false;
+ break;
+ }
+ if (stringRef) {
+ if (retainString) CFRetain(stringRef);
+ CFDictionaryAddValue(dictionaryRef, *(info[i].newItemType), stringRef);
+ CFRelease(stringRef);
+ }
+ }
+ else {
+ // normal case: attribute contains a string
+ stringRef = CFStringCreateWithBytes(allocator, (UInt8*)list.attr[i].data, list.attr[i].length, kCFStringEncodingUTF8, FALSE);
+ if (stringRef == NULL)
+ stringRef = (CFStringRef) CFRetain(kCFNull);
+ CFDictionaryAddValue(dictionaryRef, *(info[i].newItemType), stringRef);
+ CFRelease(stringRef);
+ }
+ }
+ break;
+
+ case kDataRepresentation:
+ {
+ if ((info[i].oldItemType == kSecKeyLabel) && (list.attr[i].length == kUUIDStringLength)) {
+ // It's possible that there could be a string here because the key label may have a UUID
+ CFStringRef stringRef = CFStringCreateWithBytes(allocator, (UInt8*)list.attr[i].data, list.attr[i].length, kCFStringEncodingUTF8, FALSE);
+ if (stringRef == NULL)
+ stringRef = (CFStringRef) CFRetain(kCFNull);
+ CFDictionaryAddValue(dictionaryRef, *(info[i].newItemType), stringRef);
+ CFRelease(stringRef);
+ break;
+ }
+ CFDataRef dataRef = CFDataCreate(allocator, (UInt8*) list.attr[i].data, list.attr[i].length);
+ if (dataRef == NULL)
+ dataRef = (CFDataRef) CFRetain(kCFNull);
+ CFDictionaryAddValue(dictionaryRef, *(info[i].newItemType), dataRef);
+ CFRelease(dataRef);
+ }
+ break;
+
+ case kNumberRepresentation:
+ {
+ CFNumberRef numberRef = CFNumberCreate(allocator, kCFNumberSInt32Type, list.attr[i].data);
+ if (numberRef == NULL)
+ numberRef = (CFNumberRef) CFRetain(kCFNull);
+ CFDictionaryAddValue(dictionaryRef, *(info[i].newItemType), numberRef);
+ CFRelease(numberRef);
+ }
+ break;
+
+ case kBooleanRepresentation:
+ {
+ uint32_t value = *((uint32_t*)list.attr[i].data);
+ CFBooleanRef boolRef = (value) ? kCFBooleanTrue : kCFBooleanFalse;
+ CFDictionaryAddValue(dictionaryRef, *(info[i].newItemType), boolRef);
+ }
+ break;
+
+ case kDateRepresentation:
+ {
+ CFDateRef dateRef = NULL;
+ CSSMDateTimeUtils::CssmDateStringToCFDate((const char *)list.attr[i].data, list.attr[i].length, &dateRef);
+ if (dateRef == NULL)
+ dateRef = (CFDateRef) CFRetain(kCFNull);
+ CFDictionaryAddValue(dictionaryRef, *(info[i].newItemType), dateRef);
+ CFRelease(dateRef);
+ }
+ break;
+ }
+ }
+
+ // cleanup
+ SecKeychainItemFreeContent(&list, NULL);
+ free(list.attr);
+
+ return result;
+}
+
+
+
+//
+/*
+ * _CreateAttributesDictionaryFromGenericPasswordItem creates a CFDictionaryRef using the
+ * attributes of item.
+ */
+static OSStatus
+_CreateAttributesDictionaryFromGenericPasswordItem(
+ CFAllocatorRef allocator,
+ SecKeychainItemRef item,
+ CFDictionaryRef *dictionary)
+{
+ // do the basic allocations
+ CFMutableDictionaryRef dict = NULL;
+ OSStatus result = _ConvertOldFormatToNewFormat(allocator, gGenericPasswordAttributes, kNumberOfGenericPasswordAttributes, item, dict);
+ if (result == errSecSuccess) // did we complete OK
+ {
+ CFDictionaryAddValue(dict, kSecClass, kSecClassGenericPassword);
+ }
+
+ *dictionary = dict;
+
+ return result;
+}
+
+
+
+/*
+ * _CreateAttributesDictionaryFromCertificateItem creates a CFDictionaryRef using the
+ * attributes of item.
+ */
+static OSStatus
+_CreateAttributesDictionaryFromCertificateItem(
+ CFAllocatorRef allocator,
+ SecKeychainItemRef item,
+ CFDictionaryRef *dictionary)
+{
+ // do the basic allocations
+ CFMutableDictionaryRef dict = NULL;
+ OSStatus result = _ConvertOldFormatToNewFormat(allocator, gCertificateAttributes, kNumberOfCertificateAttributes, item, dict);
+ if (result == errSecSuccess) // did we complete OK
+ {
+ CFDictionaryAddValue(dict, kSecClass, kSecClassCertificate);
+ }
+
+ *dictionary = dict;
+
+ return errSecSuccess;
+}
+
+/*
+ * _CreateAttributesDictionaryFromKeyItem creates a CFDictionaryRef using the
+ * attributes of item.
+ */
+static OSStatus
+_CreateAttributesDictionaryFromKeyItem(
+ CFAllocatorRef allocator,
+ SecKeychainItemRef item,
+ CFDictionaryRef *dictionary)
+{
+#if 0
+//%%%FIXME this ought to work, but the call to SecKeychainCopyContent in _ConvertOldFormatToNewFormat fails.
+// Need to rewrite _ConvertOldFormatToNewFormat so that it uses SecKeychainAttributeInfoForItemID and
+// SecKeychainItemCopyAttributesAndData to get the attributes, rather than SecKeychainCopyContent.
+
+ if (status) {
+ goto error_exit; // unable to get the attribute info (i.e. database schema)
+ }
+
+ status = SecKeychainItemCopyAttributesAndData(item, info, &itemClass, &attrList, NULL, NULL);
+
+ // do the basic allocations
+ CFMutableDictionaryRef dict = NULL;
+ OSStatus result = _ConvertOldFormatToNewFormat(allocator, gKeyAttributes, kNumberOfKeyAttributes, item, dict);
+ if (result == errSecSuccess) // did we complete OK
+ {
+ CFDictionaryAddValue(dict, kSecClass, kSecClassKey);
+ }
+
+ *dictionary = dict;
+
+ return errSecSuccess;
+#endif
+
+ CFMutableDictionaryRef dict = CFDictionaryCreateMutable(allocator, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+ unsigned int ix;
+ SecItemClass itemClass = 0;
+ UInt32 itemID;
+ SecKeychainAttributeList *attrList = NULL;
+ SecKeychainAttributeInfo *info = NULL;
+ SecKeychainRef keychain = NULL;
+
+ OSStatus status = SecKeychainItemCopyAttributesAndData(item, NULL, &itemClass, NULL, NULL, NULL);
+ if (status) {
+ goto error_exit; // item must have an itemClass
+ }
+
+ switch (itemClass)
+ {
+ case kSecInternetPasswordItemClass:
+ itemID = CSSM_DL_DB_RECORD_INTERNET_PASSWORD;
+ break;
+ case kSecGenericPasswordItemClass:
+ itemID = CSSM_DL_DB_RECORD_GENERIC_PASSWORD;
+ break;
+ case 'ashp': /* kSecAppleSharePasswordItemClass */
+ itemID = CSSM_DL_DB_RECORD_APPLESHARE_PASSWORD;
+ break;
+ default:
+ itemID = itemClass;
+ break;
+ }
+
+ status = SecKeychainItemCopyKeychain(item, &keychain);
+ if (status) {
+ goto error_exit; // item must have a keychain, so we can get the attribute info for it
+ }
+
+ status = SecKeychainAttributeInfoForItemID(keychain, itemID, &info);
+ if (status) {
+ goto error_exit; // unable to get the attribute info (i.e. database schema)
+ }
+
+ status = SecKeychainItemCopyAttributesAndData(item, info, &itemClass, &attrList, NULL, NULL);
+ if (status) {
+ goto error_exit; // unable to get the attribute info (i.e. database schema)
+ }
+
+ for (ix = 0; ix < info->count; ++ix)
+ {
+ SecKeychainAttribute *attribute = &attrList->attr[ix];
+ if (!attribute->length && !attribute->data)
+ continue;
+
+ UInt32 j, count = kNumberOfKeyAttributes;
+ InternalAttributeListInfo *intInfo = NULL;
+ for (j=0; j<count; j++) {
+ if (gKeyAttributes[j].oldItemType == info->tag[ix]) {
+ intInfo = &gKeyAttributes[j];
+ break;
+ }
+ }
+ if (!intInfo)
+ continue;
+
+ switch (intInfo->itemRepresentation)
+ {
+ case kStringRepresentation:
+ {
+ CFStringRef stringRef;
+ if (intInfo->oldItemType == kSecKeyKeyClass) {
+ // special case: kSecKeyKeyClass is a UInt32 value that maps to a CFStringRef constant
+ UInt32 keyRecordValue = *((UInt32*)attribute->data);
+ bool retainString = true;
+ switch (keyRecordValue) {
+ case CSSM_DL_DB_RECORD_PUBLIC_KEY :
+ stringRef = (CFStringRef) kSecAttrKeyClassPublic;
+ break;
+ case CSSM_DL_DB_RECORD_PRIVATE_KEY:
+ stringRef = (CFStringRef) kSecAttrKeyClassPrivate;
+ break;
+ case CSSM_DL_DB_RECORD_SYMMETRIC_KEY:
+ stringRef = (CFStringRef) kSecAttrKeyClassSymmetric;
+ break;
+ default:
+ stringRef = CFStringCreateWithFormat(allocator, NULL, CFSTR("%u"), (unsigned int)keyRecordValue);
+ break;
+ }
+ if (stringRef) {
+ if (retainString) CFRetain(stringRef);
+ CFDictionaryAddValue(dict, *(intInfo->newItemType), stringRef);
+ CFRelease(stringRef);
+ }
+ }
+ else if (intInfo->oldItemType == kSecKeyKeyType) {
+ // special case: kSecKeyKeyType is a UInt32 value that maps to a CFStringRef constant
+ UInt32 keyAlgValue = *((UInt32*)attribute->data);
+ bool retainString = true;
+ switch (keyAlgValue) {
+ case CSSM_ALGID_RSA :
+ stringRef = (CFStringRef) kSecAttrKeyTypeRSA;
+ break;
+ case CSSM_ALGID_DSA :
+ stringRef = (CFStringRef) kSecAttrKeyTypeDSA;
+ break;
+ case CSSM_ALGID_AES :
+ stringRef = (CFStringRef) kSecAttrKeyTypeAES;
+ break;
+ case CSSM_ALGID_DES :
+ stringRef = (CFStringRef) kSecAttrKeyTypeDES;
+ break;
+ case CSSM_ALGID_3DES :
+ stringRef = (CFStringRef) kSecAttrKeyType3DES;
+ break;
+ case CSSM_ALGID_RC4 :
+ stringRef = (CFStringRef) kSecAttrKeyTypeRC4;
+ break;
+ case CSSM_ALGID_RC2 :
+ stringRef = (CFStringRef) kSecAttrKeyTypeRC2;
+ break;
+ case CSSM_ALGID_CAST :
+ stringRef = (CFStringRef) kSecAttrKeyTypeCAST;
+ break;
+ case CSSM_ALGID_ECDSA :
+ stringRef = (CFStringRef) kSecAttrKeyTypeEC;
+ break;
+ default :
+ stringRef = CFStringCreateWithFormat(allocator, NULL, CFSTR("%u"), (unsigned int)keyAlgValue);
+ retainString = false;
+ break;
+ }
+ if (stringRef) {
+ if (retainString) CFRetain(stringRef);
+ CFDictionaryAddValue(dict, *(intInfo->newItemType), stringRef);
+ CFRelease(stringRef);
+ }
+ }
+ else {
+ // normal case: attribute contains a string
+ stringRef = CFStringCreateWithBytes(allocator, (UInt8*)attribute->data, attribute->length, kCFStringEncodingUTF8, FALSE);
+ if (stringRef == NULL)
+ stringRef = (CFStringRef) CFRetain(kCFNull);
+ CFDictionaryAddValue(dict, *(intInfo->newItemType), stringRef);
+ CFRelease(stringRef);
+ }
+ }
+ break;
+
+ case kDataRepresentation:
+ {
+ if ((intInfo->oldItemType == kSecKeyLabel) && (attribute->length == kUUIDStringLength)) {
+ // It's possible that there could be a string here because the key label may have a UUID
+ CFStringRef stringRef = CFStringCreateWithBytes(allocator, (UInt8*)attribute->data, attribute->length, kCFStringEncodingUTF8, FALSE);
+ if (stringRef == NULL)
+ stringRef = (CFStringRef) CFRetain(kCFNull);
+ CFDictionaryAddValue(dict, *(intInfo->newItemType), stringRef);
+ CFRelease(stringRef);
+ break;
+ }
+
+ CFDataRef dataRef = CFDataCreate(allocator, (UInt8*)attribute->data, attribute->length);
+ if (dataRef == NULL)
+ dataRef = (CFDataRef) CFRetain(kCFNull);
+ CFDictionaryAddValue(dict, *(intInfo->newItemType), dataRef);
+ CFRelease(dataRef);
+ }
+ break;
+
+ case kNumberRepresentation:
+ {
+ CFNumberRef numberRef = CFNumberCreate(allocator, kCFNumberSInt32Type, attribute->data);
+ if (numberRef == NULL)
+ numberRef = (CFNumberRef) CFRetain(kCFNull);
+ CFDictionaryAddValue(dict, *(intInfo->newItemType), numberRef);
+ CFRelease(numberRef);
+ }
+ break;
+
+ case kBooleanRepresentation:
+ {
+ UInt32 value = *((UInt32*)attribute->data);
+ CFBooleanRef boolRef = (value) ? kCFBooleanTrue : kCFBooleanFalse;
+ CFDictionaryAddValue(dict, *(intInfo->newItemType), boolRef);
+ }
+ break;
+
+ case kDateRepresentation:
+ {
+ //%%% FIXME need to convert from a CSSM date string to a CFDateRef here
+ CFDateRef dateRef = NULL;
+ if (dateRef == NULL)
+ dateRef = (CFDateRef) CFRetain(kCFNull);
+ CFDictionaryAddValue(dict, *(intInfo->newItemType), dateRef);
+ CFRelease(dateRef);
+ }
+ break;
+ }
+ }
+
+ CFDictionaryAddValue(dict, kSecClass, kSecClassKey);
+
+error_exit:
+
+ if (attrList)
+ SecKeychainItemFreeAttributesAndData(attrList, NULL);
+
+ if (info)
+ SecKeychainFreeAttributeInfo(info);
+
+ if (keychain)
+ CFRelease(keychain);
+
+ *dictionary = dict;
+
+ return status;
+}
+
+
+/*
+ * _CreateAttributesDictionaryFromInternetPasswordItem creates a CFDictionaryRef using the
+ * attributes of item.
+ */
+static OSStatus
+_CreateAttributesDictionaryFromInternetPasswordItem(
+ CFAllocatorRef allocator,
+ SecKeychainItemRef item,
+ CFDictionaryRef *dictionary)
+{
+ OSStatus status;
+ SecKeychainAttribute attr[] = {
+ { kSecServerItemAttr, 0, NULL }, /* [0] server */
+ { kSecSecurityDomainItemAttr, 0, NULL }, /* [1] securityDomain */
+ { kSecAccountItemAttr, 0, NULL }, /* [2] account */
+ { kSecPathItemAttr, 0, NULL }, /* [3] path */
+ { kSecPortItemAttr, 0, NULL }, /* [4] port */
+ { kSecProtocolItemAttr, 0, NULL }, /* [5] protocol */
+ { kSecAuthenticationTypeItemAttr, 0, NULL }, /* [6] authenticationType */
+ { kSecCommentItemAttr, 0, NULL }, /* [7] comment */
+ { kSecDescriptionItemAttr, 0, NULL }, /* [8] description */
+ { kSecLabelItemAttr, 0, NULL }, /* [9] label */
+ { kSecCreationDateItemAttr, 0, NULL }, /* [10] creation date */
+ { kSecModDateItemAttr, 0, NULL }, /* [11] modification date */
+ { kSecCreatorItemAttr, 0, NULL }, /* [12] creator */
+ { kSecTypeItemAttr, 0, NULL }, /* [13] type */
+ { kSecInvisibleItemAttr, 0, NULL }, /* [14] invisible */
+ { kSecNegativeItemAttr, 0, NULL }, /* [15] negative */
+ };
+ SecKeychainAttributeList attrList = { sizeof(attr) / sizeof(SecKeychainAttribute), attr };
+ CFIndex numValues;
+ CFIndex index;
+ CFTypeRef keys[(sizeof(attr) / sizeof(SecKeychainAttribute)) + 2];
+ CFTypeRef values[(sizeof(attr) / sizeof(SecKeychainAttribute)) + 2];
+
+ *dictionary = NULL;
+
+ // copy the item's attributes
+ status = SecKeychainItemCopyContent(item, NULL, &attrList, NULL, NULL);
+ require_noerr(status, SecKeychainItemCopyContent_failed);
+
+ numValues = 0;
+
+ // add kSecClass
+ keys[numValues] = kSecClass;
+ values[numValues] = kSecClassInternetPassword;
+ ++numValues;
+
+ // add kSecAttrServer
+ if ( attrList.attr[0].length > 0 ) {
+ keys[numValues] = kSecAttrServer;
+ values[numValues] = CFStringCreateWithBytes(allocator, (UInt8 *)attrList.attr[0].data, attrList.attr[0].length, kCFStringEncodingUTF8, FALSE);
+ if ( values[numValues] != NULL ) {
+ ++numValues;
+ }
+ }
+
+ // add kSecAttrSecurityDomain
+ if ( attrList.attr[1].length > 0 ) {
+ keys[numValues] = kSecAttrSecurityDomain;
+ values[numValues] = CFStringCreateWithBytes(allocator, (UInt8 *)attrList.attr[1].data, attrList.attr[1].length, kCFStringEncodingUTF8, FALSE);
+ if ( values[numValues] != NULL ) {
+ ++numValues;
+ }
+ }
+
+ // add kSecAttrAccount
+ if ( attrList.attr[2].length > 0 ) {
+ keys[numValues] = kSecAttrAccount;
+ values[numValues] = CFStringCreateWithBytes(allocator, (UInt8 *)attrList.attr[2].data, attrList.attr[2].length, kCFStringEncodingUTF8, FALSE);
+ if ( values[numValues] != NULL ) {
+ ++numValues;
+ }
+ }
+
+ // add kSecAttrPath
+ if ( attrList.attr[3].length > 0 ) {
+ keys[numValues] = kSecAttrPath;
+ values[numValues] = CFStringCreateWithBytes(allocator, (UInt8 *)attrList.attr[3].data, attrList.attr[3].length, kCFStringEncodingUTF8, FALSE);
+ if ( values[numValues] != NULL ) {
+ ++numValues;
+ }
+ }
+
+ // add kSecAttrPort
+ if ( attrList.attr[4].length > 0 ) {
+ keys[numValues] = kSecAttrPort;
+ values[numValues] = CFNumberCreate(allocator, kCFNumberSInt32Type, attrList.attr[4].data);
+ if ( values[numValues] != NULL ) {
+ ++numValues;
+ }
+ }
+
+ // add kSecAttrProtocol
+ if ( attrList.attr[5].length > 0 ) {
+ keys[numValues] = kSecAttrProtocol;
+ values[numValues] = _SecAttrProtocolForSecProtocolType(*(SecProtocolType*)attrList.attr[5].data);
+ if ( values[numValues] != NULL ) {
+ CFRetain(values[numValues]);
+ ++numValues;
+ }
+ }
+
+ // add kSecAttrAuthenticationType
+ if ( attrList.attr[6].length > 0 ) {
+ keys[numValues] = kSecAttrAuthenticationType;
+ values[numValues] = _SecAttrAuthenticationTypeForSecAuthenticationType(*(SecProtocolType*)attrList.attr[6].data);
+ if ( values[numValues] != NULL ) {
+ CFRetain(values[numValues]);
+ ++numValues;
+ }
+ }
+
+ // add kSecAttrComment
+ if ( attrList.attr[7].length > 0 ) {
+ keys[numValues] = kSecAttrComment;
+ values[numValues] = CFStringCreateWithBytes(allocator, (UInt8 *)attrList.attr[7].data, attrList.attr[7].length, kCFStringEncodingUTF8, FALSE);
+ if ( values[numValues] != NULL ) {
+ ++numValues;
+ }
+ }
+
+ // add kSecAttrDescription
+ if ( attrList.attr[8].length > 0 ) {
+ keys[numValues] = kSecAttrDescription;
+ values[numValues] = CFStringCreateWithBytes(allocator, (UInt8 *)attrList.attr[8].data, attrList.attr[8].length, kCFStringEncodingUTF8, FALSE);
+ if ( values[numValues] != NULL ) {
+ ++numValues;
+ }
+ }
+
+ // add kSecAttrLabel
+ if ( attrList.attr[9].length > 0 ) {
+ keys[numValues] = kSecAttrLabel;
+ values[numValues] = CFStringCreateWithBytes(allocator, (UInt8 *)attrList.attr[9].data, attrList.attr[9].length, kCFStringEncodingUTF8, FALSE);
+ if ( values[numValues] != NULL ) {
+ ++numValues;
+ }
+ }
+
+ // add kSecAttrCreationDate
+ if ( attrList.attr[10].length > 0 ) {
+ CFDateRef creationDate = NULL;
+ CSSMDateTimeUtils::CssmDateStringToCFDate((const char *)attrList.attr[10].data, attrList.attr[10].length, &creationDate);
+ keys[numValues] = kSecAttrCreationDate;
+ values[numValues] = creationDate;
+ if ( values[numValues] != NULL ) {
+ ++numValues;
+ }
+ }
+
+ // add kSecAttrModificationDate
+ if ( attrList.attr[11].length > 0 ) {
+ CFDateRef modDate = NULL;
+ CSSMDateTimeUtils::CssmDateStringToCFDate((const char *)attrList.attr[11].data, attrList.attr[11].length, &modDate);
+ keys[numValues] = kSecAttrModificationDate;
+ values[numValues] = modDate;
+ if ( values[numValues] != NULL ) {
+ ++numValues;
+ }
+ }
+
+ // add kSecCreatorItemAttr
+ if ( attrList.attr[12].length > 0 ) {
+ CFNumberRef numberRef = CFNumberCreate(allocator, kCFNumberSInt32Type, attrList.attr[12].data);
+ keys[numValues] = kSecAttrCreator;
+ values[numValues] = numberRef;
+ if ( values[numValues] != NULL ) {
+ CFRetain(values[numValues]);
+ ++numValues;
+ }
+ }
+
+ // add kSecTypeItemAttr
+ if ( attrList.attr[13].length > 0 ) {
+ CFNumberRef numberRef = CFNumberCreate(allocator, kCFNumberSInt32Type, attrList.attr[13].data);
+ keys[numValues] = kSecAttrType;
+ values[numValues] = numberRef;
+ if ( values[numValues] != NULL ) {
+ CFRetain(values[numValues]);
+ ++numValues;
+ }
+ }
+
+ // add kSecInvisibleItemAttr
+ if ( attrList.attr[14].length > 0 ) {
+ uint32_t value = *((uint32_t*)attrList.attr[14].data);
+ CFBooleanRef boolRef = (value) ? kCFBooleanTrue : kCFBooleanFalse;
+ keys[numValues] = kSecAttrIsInvisible;
+ values[numValues] = boolRef;
+ if ( values[numValues] != NULL ) {
+ CFRetain(values[numValues]);
+ ++numValues;
+ }
+ }
+
+ // add kSecNegativeItemAttr
+ if ( attrList.attr[15].length > 0 ) {
+ uint32_t value = *((uint32_t*)attrList.attr[15].data);
+ CFBooleanRef boolRef = (value) ? kCFBooleanTrue : kCFBooleanFalse;
+ keys[numValues] = kSecAttrIsNegative;
+ values[numValues] = boolRef;
+ if ( values[numValues] != NULL ) {
+ CFRetain(values[numValues]);
+ ++numValues;
+ }
+ }
+
+ // create the dictionary
+ *dictionary = CFDictionaryCreate(allocator, keys, values, numValues, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+
+ // release the values added to the dictionary
+ for ( index = 0; index < numValues; ++index )
+ {
+ CFRelease(values[index]);
+ }
+
+ // and free the attributes
+ (void) SecKeychainItemFreeContent(&attrList, NULL);
+
+SecKeychainItemCopyContent_failed:
+
+ return ( status );
+}
+
+
+/*
+ * _CreateAttributesDictionaryFromItem creates a CFDictionaryRef using the
+ * attributes of the specified item class and item.
+ */
+static OSStatus
+_CreateAttributesDictionaryFromItem(
+ CFAllocatorRef allocator,
+ SecItemClass itemClass,
+ SecKeychainItemRef item,
+ CFDictionaryRef *dictionary)
+{
+ switch (itemClass)
+ {
+ case kSecInternetPasswordItemClass:
+ return _CreateAttributesDictionaryFromInternetPasswordItem(allocator, item, dictionary);
+
+ case kSecGenericPasswordItemClass:
+ return _CreateAttributesDictionaryFromGenericPasswordItem(allocator, item, dictionary);
+
+ case kSecCertificateItemClass:
+ return _CreateAttributesDictionaryFromCertificateItem(allocator, item, dictionary);
+
+ case kSecPublicKeyItemClass:
+ case kSecPrivateKeyItemClass:
+ case kSecSymmetricKeyItemClass:
+ return _CreateAttributesDictionaryFromKeyItem(allocator, item, dictionary);
+
+ default:
+ *dictionary = NULL;
+ break;
+ }
+ return errSecParam;
+}
+
+
+/*
+ * _FreeAttrList frees the memory allocated for the SecKeychainAttributeList
+ * by the _CreateSecKeychainAttributeListFromDictionary function.
+ */
+static void
+_FreeAttrList(
+ SecKeychainAttributeList *attrListPtr)
+{
+ UInt32 index;
+
+ if ( attrListPtr != NULL ) {
+ if ( attrListPtr->attr != NULL ) {
+ // free any attribute data
+ for ( index = 0; index < attrListPtr->count; ++index ) {
+ free(attrListPtr->attr[index].data);
+ }
+ // free the attribute array
+ free(attrListPtr->attr);
+ }
+ // free the attribute list
+ free(attrListPtr);
+ }
+}
+
+/*
+ * _CFDataCreateAttribute initializes the SecKeychainAttribute pointed to by
+ * attr using the data and tag parameters.
+ *
+ * The memory for the SecKeychainAttribute's data field is allocated with malloc
+ * and must be released by the caller (this is normally done by calling _FreeAttrList).
+ */
+static OSStatus
+_CFDataCreateAttribute(
+ CFDataRef data,
+ SecKeychainAttrType tag,
+ SecKeychainAttributePtr attr)
+{
+ OSStatus status = errSecSuccess;
+ CFRange range;
+
+ // set the attribute tag
+ attr->tag = tag;
+
+ // determine the attribute length
+ attr->length = (UInt32) CFDataGetLength(data);
+ range = CFRangeMake(0, (CFIndex)attr->length);
+
+ // allocate memory for the attribute bytes
+ attr->data = malloc(attr->length);
+ require_action(attr->data != NULL, malloc_failed, status = errSecBufferTooSmall);
+
+ // get the attribute bytes
+ CFDataGetBytes(data, range, (UInt8 *)attr->data);
+
+malloc_failed:
+
+ return ( status );
+}
+
+/*
+ * _CFStringCreateAttribute initializes the SecKeychainAttribute pointed to by
+ * attr using the string and tag parameters.
+ *
+ * The memory for the SecKeychainAttribute's data field is allocated with malloc
+ * and must be released by the caller (this is normally done by calling _FreeAttrList).
+ */
+static OSStatus
+_CFStringCreateAttribute(
+ CFStringRef string,
+ SecKeychainAttrType tag,
+ SecKeychainAttributePtr attr)
+{
+ OSStatus status = errSecSuccess;
+ CFRange range;
+
+ // set the attribute tag
+ attr->tag = tag;
+
+ // determine the attribute length
+ range = CFRangeMake(0, CFStringGetLength(string));
+ CFStringGetBytes(string, range, kCFStringEncodingUTF8, 0, FALSE, NULL, 0, (CFIndex *)&attr->length);
+
+ // allocate memory for the attribute bytes
+ attr->data = malloc(attr->length);
+ require_action(attr->data != NULL, malloc_failed, status = errSecBufferTooSmall);
+
+ // get the attribute bytes
+ CFStringGetBytes(string, range, kCFStringEncodingUTF8, 0, FALSE, (UInt8 *)attr->data, attr->length, NULL);
+
+malloc_failed:
+
+ return ( status );
+}
+
+
+/*
+ * _CreateSecKeychainGenericPasswordAttributeListFromDictionary creates a SecKeychainAttributeList
+ * from the attribute key/values in attrDictionary.
+ *
+ * If this function returns errSecSuccess, the pointer to the SecKeychainAttributeList
+ * must be freed by the caller with _FreeAttrList()
+ */
+static OSStatus
+_CreateSecKeychainGenericPasswordAttributeListFromDictionary(
+ CFDictionaryRef attrDictionary,
+ SecKeychainAttributeList **attrList)
+{
+ return _ConvertNewFormatToOldFormat(NULL, gGenericPasswordAttributes, kNumberOfGenericPasswordAttributes, attrDictionary, *attrList);
+}
+
+
+/*
+ * _CreateSecKeychainCertificateAttributeListFromDictionary creates a SecKeychainAttributeList
+ * from the attribute key/values in attrDictionary.
+ *
+ * If this function returns errSecSuccess, the pointer to the SecKeychainAttributeList
+ * must be freed by the caller with _FreeAttrList()
+ */
+static OSStatus
+_CreateSecKeychainCertificateAttributeListFromDictionary(
+ CFDictionaryRef attrDictionary,
+ SecKeychainAttributeList **attrList)
+{
+ return _ConvertNewFormatToOldFormat(NULL, gCertificateAttributes, kNumberOfCertificateAttributes, attrDictionary, *attrList);
+}
+
+
+/*
+ * _CreateSecKeychainKeyAttributeListFromDictionary creates a SecKeychainAttributeList
+ * from the attribute key/values in attrDictionary.
+ *
+ * If this function returns errSecSuccess, the pointer to the SecKeychainAttributeList
+ * must be freed by the caller with _FreeAttrList()
+ */
+static OSStatus
+_CreateSecKeychainKeyAttributeListFromDictionary(
+ CFDictionaryRef attrDictionary,
+ SecKeychainAttributeList **attrList)
+{
+#if 0
+ //%%%FIXME this function should work for key attributes, but currently doesn't; need to debug
+ return _ConvertNewFormatToOldFormat(NULL, gKeyAttributes, kNumberOfKeyAttributes, attrDictionary, *attrList);
+#else
+ // explicitly build attribute list for supported key attributes
+ // NOTE: this code supports only MaxSecKeyAttributes (15) attributes
+ const int MaxSecKeyAttributes = 15;
+
+ OSStatus status;
+ CFTypeRef value;
+ SecKeychainAttributeList *attrListPtr;
+
+ attrListPtr = (SecKeychainAttributeList*)calloc(1, sizeof(SecKeychainAttributeList));
+ require_action(attrListPtr != NULL, calloc_attrListPtr_failed, status = errSecBufferTooSmall);
+
+ attrListPtr->attr = (SecKeychainAttribute*)calloc(MaxSecKeyAttributes, sizeof(SecKeychainAttribute));
+ require_action(attrListPtr->attr != NULL, malloc_attrPtr_failed, status = errSecBufferTooSmall);
+
+ // [0] get the kSecKeyKeyClass value
+ if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrKeyClass, (const void **)&value) && value) {
+ UInt32 keyRecordValue = 0;
+ if (CFEqual(kSecAttrKeyClassPublic, value))
+ keyRecordValue = CSSM_DL_DB_RECORD_PUBLIC_KEY;
+ else if (CFEqual(kSecAttrKeyClassPrivate, value))
+ keyRecordValue = CSSM_DL_DB_RECORD_PRIVATE_KEY;
+ else if (CFEqual(kSecAttrKeyClassSymmetric, value))
+ keyRecordValue = CSSM_DL_DB_RECORD_SYMMETRIC_KEY;
+
+ // only use this attribute if we recognize the value!
+ if (keyRecordValue != 0) {
+ attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32));
+ require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_number_failed, status = errSecBufferTooSmall);
+
+ attrListPtr->attr[attrListPtr->count].tag = kSecKeyKeyClass;
+ attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32);
+ *((UInt32*)attrListPtr->attr[attrListPtr->count].data) = keyRecordValue;
+
+ ++attrListPtr->count;
+ }
+ }
+
+ // [1] get the kSecKeyPrintName string
+ if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrLabel, (const void **)&value) && value) {
+ status = _CFStringCreateAttribute((CFStringRef)value, kSecKeyPrintName, &attrListPtr->attr[attrListPtr->count]);
+ require_noerr_quiet(status, CFStringCreateAttribute_failed);
+
+ ++attrListPtr->count;
+ }
+
+ // [2] get the kSecKeyPermanent boolean
+ if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrIsPermanent, (const void **)&value) && value) {
+ attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32));
+ require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_number_failed, status = errSecBufferTooSmall);
+
+ attrListPtr->attr[attrListPtr->count].tag = kSecKeyPermanent;
+ attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32);
+ *((UInt32*)attrListPtr->attr[attrListPtr->count].data) = (CFEqual(kCFBooleanTrue, value)) ? 1 : 0;
+
+ ++attrListPtr->count;
+ }
+
+ // [3] get the kSecKeyLabel string
+ if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrApplicationLabel, (const void **)&value) && value) {
+ if (CFStringGetTypeID() == CFGetTypeID(value))
+ status = _CFStringCreateAttribute((CFStringRef)value, kSecKeyLabel, &attrListPtr->attr[attrListPtr->count]);
+ else if (CFDataGetTypeID() == CFGetTypeID(value))
+ status = _CFDataCreateAttribute((CFDataRef)value, kSecKeyLabel, &attrListPtr->attr[attrListPtr->count]);
+ else
+ status = errSecParam;
+
+ require_noerr_quiet(status, CFStringCreateAttribute_failed);
+
+ ++attrListPtr->count;
+ }
+
+ // [4] get the kSecKeyApplicationTag data
+ if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrApplicationTag, (const void **)&value) && value) {
+ if (CFStringGetTypeID() == CFGetTypeID(value))
+ status = _CFStringCreateAttribute((CFStringRef)value, kSecKeyApplicationTag, &attrListPtr->attr[attrListPtr->count]);
+ else if (CFDataGetTypeID() == CFGetTypeID(value))
+ status = _CFDataCreateAttribute((CFDataRef)value, kSecKeyApplicationTag, &attrListPtr->attr[attrListPtr->count]);
+ else
+ status = errSecParam;
+
+ require_noerr_quiet(status, CFDataCreateAttribute_failed);
+ ++attrListPtr->count;
+ }
+
+ // [5] get the kSecKeyKeyType number
+ if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrKeyType, (const void **)&value) && value) {
+ UInt32 keyAlgValue = _SecAlgorithmTypeFromSecAttrKeyType(kSecAttrKeyType);
+ if (keyAlgValue != 0) {
+ attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32));
+ require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_number_failed, status = errSecBufferTooSmall);
+
+ attrListPtr->attr[attrListPtr->count].tag = kSecKeyKeyType;
+ attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32);
+ *((UInt32*)attrListPtr->attr[attrListPtr->count].data) = keyAlgValue;
+
+ ++attrListPtr->count;
+ }
+ }
+
+ // [6] get the kSecKeyKeySizeInBits number
+ if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrKeySizeInBits, (const void **)&value) && value) {
+ if (CFNumberGetTypeID() == CFGetTypeID(value)) {
+ attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32));
+ require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_number_failed, status = errSecBufferTooSmall);
+
+ attrListPtr->attr[attrListPtr->count].tag = kSecKeyKeySizeInBits;
+ attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32);
+ CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, attrListPtr->attr[attrListPtr->count].data);
+
+ ++attrListPtr->count;
+ }
+ }
+
+ // [7] get the kSecKeyEffectiveKeySize number
+ if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrEffectiveKeySize, (const void **)&value) && value) {
+ if (CFNumberGetTypeID() == CFGetTypeID(value)) {
+ attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32));
+ require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_number_failed, status = errSecBufferTooSmall);
+
+ attrListPtr->attr[attrListPtr->count].tag = kSecKeyEffectiveKeySize;
+ attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32);
+ CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, attrListPtr->attr[attrListPtr->count].data);
+
+ ++attrListPtr->count;
+ }
+ }
+
+ // [8] get the kSecKeyEncrypt boolean
+ if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrCanEncrypt, (const void **)&value) && value) {
+ if (CFBooleanGetTypeID() == CFGetTypeID(value)) {
+ attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32));
+ require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_number_failed, status = errSecBufferTooSmall);
+
+ attrListPtr->attr[attrListPtr->count].tag = kSecKeyEncrypt;
+ attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32);
+ *((UInt32*)attrListPtr->attr[attrListPtr->count].data) = (CFEqual(kCFBooleanTrue, value)) ? 1 : 0;
+
+ ++attrListPtr->count;
+ }
+ }
+
+ // [9] get the kSecKeyDecrypt boolean
+ if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrCanDecrypt, (const void **)&value) && value) {
+ if (CFBooleanGetTypeID() == CFGetTypeID(value)) {
+ attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32));
+ require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_number_failed, status = errSecBufferTooSmall);
+
+ attrListPtr->attr[attrListPtr->count].tag = kSecKeyDecrypt;
+ attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32);
+ *((UInt32*)attrListPtr->attr[attrListPtr->count].data) = (CFEqual(kCFBooleanTrue, value)) ? 1 : 0;
+
+ ++attrListPtr->count;
+ }
+ }
+
+ // [10] get the kSecKeyDerive boolean
+ if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrCanDerive, (const void **)&value) && value) {
+ if (CFBooleanGetTypeID() == CFGetTypeID(value)) {
+ attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32));
+ require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_number_failed, status = errSecBufferTooSmall);
+
+ attrListPtr->attr[attrListPtr->count].tag = kSecKeyDerive;
+ attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32);
+ *((UInt32*)attrListPtr->attr[attrListPtr->count].data) = (CFEqual(kCFBooleanTrue, value)) ? 1 : 0;
+
+ ++attrListPtr->count;
+ }
+ }
+
+ // [11] get the kSecKeySign boolean
+ if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrCanSign, (const void **)&value) && value) {
+ if (CFBooleanGetTypeID() == CFGetTypeID(value)) {
+ attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32));
+ require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_number_failed, status = errSecBufferTooSmall);
+
+ attrListPtr->attr[attrListPtr->count].tag = kSecKeySign;
+ attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32);
+ *((UInt32*)attrListPtr->attr[attrListPtr->count].data) = (CFEqual(kCFBooleanTrue, value)) ? 1 : 0;
+
+ ++attrListPtr->count;
+ }
+ }
+
+ // [12] get the kSecKeyVerify boolean
+ if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrCanVerify, (const void **)&value) && value) {
+ if (CFBooleanGetTypeID() == CFGetTypeID(value)) {
+ attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32));
+ require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_number_failed, status = errSecBufferTooSmall);
+
+ attrListPtr->attr[attrListPtr->count].tag = kSecKeyVerify;
+ attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32);
+ *((UInt32*)attrListPtr->attr[attrListPtr->count].data) = (CFEqual(kCFBooleanTrue, value)) ? 1 : 0;
+
+ ++attrListPtr->count;
+ }
+ }
+
+ // [13] get the kSecKeyWrap boolean
+ if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrCanWrap, (const void **)&value) && value) {
+ if (CFBooleanGetTypeID() == CFGetTypeID(value)) {
+ attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32));
+ require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_number_failed, status = errSecBufferTooSmall);
+
+ attrListPtr->attr[attrListPtr->count].tag = kSecKeyWrap;
+ attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32);
+ *((UInt32*)attrListPtr->attr[attrListPtr->count].data) = (CFEqual(kCFBooleanTrue, value)) ? 1 : 0;
+
+ ++attrListPtr->count;
+ }
+ }
+
+ // [14] get the kSecKeyUnwrap boolean
+ if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrCanUnwrap, (const void **)&value) && value) {
+ if (CFBooleanGetTypeID() == CFGetTypeID(value)) {
+ attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32));
+ require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_number_failed, status = errSecBufferTooSmall);
+
+ attrListPtr->attr[attrListPtr->count].tag = kSecKeyUnwrap;
+ attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32);
+ *((UInt32*)attrListPtr->attr[attrListPtr->count].data) = (CFEqual(kCFBooleanTrue, value)) ? 1 : 0;
+
+ ++attrListPtr->count;
+ }
+ }
+
+ // return the pointer to the attrList
+ *attrList = attrListPtr;
+
+ return ( errSecSuccess );
+
+ /***************/
+
+malloc_number_failed:
+CFDataCreateAttribute_failed:
+CFStringCreateAttribute_failed:
+malloc_attrPtr_failed:
+
+ // free any attributes
+ _FreeAttrList(attrListPtr);
+
+calloc_attrListPtr_failed:
+
+ return ( errSecBufferTooSmall );
+
+#endif
+}
+
+static CFTypeRef copyNumber(CFTypeRef obj)
+{
+ if (!obj)
+ return NULL;
+
+ CFTypeID tid = CFGetTypeID(obj);
+ if (tid == CFNumberGetTypeID())
+ {
+ CFRetain(obj);
+ return obj;
+ }
+
+ if (tid == CFBooleanGetTypeID())
+ {
+ SInt32 value = CFBooleanGetValue((CFBooleanRef)obj);
+ return CFNumberCreate(0, kCFNumberSInt32Type, &value);
+ }
+
+ if (tid == CFStringGetTypeID())
+ {
+ SInt32 value = CFStringGetIntValue((CFStringRef)obj);
+ CFStringRef t = CFStringCreateWithFormat(0, 0, CFSTR("%ld"), (long) value);
+ /* If a string converted to an int isn't equal to the int printed as
+ a string, return a NULL instead. */
+ if (!CFEqual(t, obj))
+ {
+ CFRelease(t);
+ return NULL;
+ }
+ CFRelease(t);
+ return CFNumberCreate(0, kCFNumberSInt32Type, &value);
+ }
+ return NULL;
+}
+
+/*
+ * _CreateSecKeychainInternetPasswordAttributeListFromDictionary creates a SecKeychainAttributeList
+ * from the attribute key/values in attrDictionary.
+ *
+ * If this function returns errSecSuccess, the pointer to the SecKeychainAttributeList
+ * must be freed by the caller with _FreeAttrList()
+ */
+static OSStatus
+_CreateSecKeychainInternetPasswordAttributeListFromDictionary(
+ CFDictionaryRef attrDictionary,
+ SecKeychainAttributeList **attrList)
+{
+ // explicitly build attribute list for supported key attributes
+ // NOTE: this code supports only MaxSecKeychainAttributes (14) attributes
+ const int MaxSecKeychainAttributes = 14;
+
+ OSStatus status;
+ CFTypeRef value;
+ SecKeychainAttributeList *attrListPtr;
+
+ attrListPtr = (SecKeychainAttributeList*)calloc(1, sizeof(SecKeychainAttributeList));
+ require_action(attrListPtr != NULL, calloc_attrListPtr_failed, status = errSecBufferTooSmall);
+
+ attrListPtr->attr = (SecKeychainAttribute*)calloc(MaxSecKeychainAttributes, sizeof(SecKeychainAttribute));
+ require_action(attrListPtr->attr != NULL, malloc_attrPtr_failed, status = errSecBufferTooSmall);
+
+
+ // [0] get the serverName string
+ if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrServer, (const void **)&value) ) {
+ status = _CFStringCreateAttribute((CFStringRef)value, kSecServerItemAttr, &attrListPtr->attr[attrListPtr->count]);
+ require_noerr_quiet(status, CFStringCreateAttribute_failed);
+
+ ++attrListPtr->count;
+ }
+
+ // [1] get the securityDomain string
+ if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrSecurityDomain, (const void **)&value) ) {
+ status = _CFStringCreateAttribute((CFStringRef)value, kSecSecurityDomainItemAttr, &attrListPtr->attr[attrListPtr->count]);
+ require_noerr_quiet(status, CFStringCreateAttribute_failed);
+
+ ++attrListPtr->count;
+ }
+
+ // [2] get the accountName string
+ if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrAccount, (const void **)&value) ) {
+ status = _CFStringCreateAttribute((CFStringRef)value, kSecAccountItemAttr, &attrListPtr->attr[attrListPtr->count]);
+ require_noerr_quiet(status, CFStringCreateAttribute_failed);
+
+ ++attrListPtr->count;
+ }
+
+ // [3] get the path string
+ if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrPath, (const void **)&value) ) {
+ status = _CFStringCreateAttribute((CFStringRef)value, kSecPathItemAttr, &attrListPtr->attr[attrListPtr->count]);
+ require_noerr_quiet(status, CFStringCreateAttribute_failed);
+
+ ++attrListPtr->count;
+ }
+
+ // [4] get the port number
+ if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrPort, (const void **)&value) ) {
+ attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt16));
+ require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_port_failed, status = errSecBufferTooSmall);
+
+ CFTypeRef num = copyNumber(value);
+ require_action(num != NULL, CFStringCreateAttribute_failed, status = errSecParam);
+ attrListPtr->attr[attrListPtr->count].tag = kSecPortItemAttr;
+ attrListPtr->attr[attrListPtr->count].length = sizeof(UInt16);
+ CFNumberGetValue((CFNumberRef)num, kCFNumberSInt16Type, attrListPtr->attr[attrListPtr->count].data);
+ CFRelease(num);
+
+ ++attrListPtr->count;
+ }
+
+ // [5] get the protocol
+ if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrProtocol, (const void **)&value) ) {
+ attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(SecProtocolType));
+ require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_protocol_failed, status = errSecBufferTooSmall);
+
+ attrListPtr->attr[attrListPtr->count].tag = kSecProtocolItemAttr;
+ attrListPtr->attr[attrListPtr->count].length = sizeof(SecProtocolType);
+ *(SecProtocolType *)(attrListPtr->attr[attrListPtr->count].data) = _SecProtocolTypeForSecAttrProtocol(value);
+
+ ++attrListPtr->count;
+ }
+
+ // [6] get the authenticationType
+ if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrAuthenticationType, (const void **)&value) ) {
+ attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(SecAuthenticationType));
+ require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_authenticationType_failed, status = errSecBufferTooSmall);
+
+ attrListPtr->attr[attrListPtr->count].tag = kSecAuthenticationTypeItemAttr;
+ attrListPtr->attr[attrListPtr->count].length = sizeof(SecAuthenticationType);
+ *(SecAuthenticationType *)(attrListPtr->attr[attrListPtr->count].data) = _SecAuthenticationTypeForSecAttrAuthenticationType(value);
+
+ ++attrListPtr->count;
+ }
+
+ // [7] get the comment string
+ if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrComment, (const void **)&value) ) {
+ status = _CFStringCreateAttribute((CFStringRef)value, kSecCommentItemAttr, &attrListPtr->attr[attrListPtr->count]);
+ require_noerr_quiet(status, CFStringCreateAttribute_failed);
+
+ ++attrListPtr->count;
+ }
+
+ // [8] get the description string
+ if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrDescription, (const void **)&value) ) {
+ status = _CFStringCreateAttribute((CFStringRef)value, kSecDescriptionItemAttr, &attrListPtr->attr[attrListPtr->count]);
+ require_noerr_quiet(status, CFStringCreateAttribute_failed);
+
+ ++attrListPtr->count;
+ }
+
+ // [9] get the label string
+ if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrLabel, (const void **)&value) ) {
+ status = _CFStringCreateAttribute((CFStringRef)value, kSecLabelItemAttr, &attrListPtr->attr[attrListPtr->count]);
+ require_noerr_quiet(status, CFStringCreateAttribute_failed);
+
+ ++attrListPtr->count;
+ }
+
+ // [10] get the creator code
+ if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrCreator, (const void **)&value) ) {
+ attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32));
+ require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_port_failed, status = errSecBufferTooSmall);
+
+ CFTypeRef num = copyNumber(value);
+ require_action(num != NULL, CFStringCreateAttribute_failed, status = errSecParam);
+ attrListPtr->attr[attrListPtr->count].tag = kSecCreatorItemAttr;
+ attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32);
+ CFNumberGetValue((CFNumberRef)num, kCFNumberSInt32Type, attrListPtr->attr[attrListPtr->count].data);
+ CFRelease(num);
+
+ ++attrListPtr->count;
+ }
+
+ // [11] get the type code
+ if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrType, (const void **)&value) ) {
+ attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32));
+ require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_port_failed, status = errSecBufferTooSmall);
+
+ CFTypeRef num = copyNumber(value);
+ require_action(num != NULL, CFStringCreateAttribute_failed, status = errSecParam);
+ attrListPtr->attr[attrListPtr->count].tag = kSecTypeItemAttr;
+ attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32);
+ CFNumberGetValue((CFNumberRef)num, kCFNumberSInt32Type, attrListPtr->attr[attrListPtr->count].data);
+ CFRelease(num);
+
+ ++attrListPtr->count;
+ }
+
+ // [12] get the invisible flag
+ if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrIsInvisible, (const void **)&value) ) {
+ attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32));
+ require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_port_failed, status = errSecBufferTooSmall);
+
+ attrListPtr->attr[attrListPtr->count].tag = kSecInvisibleItemAttr;
+ attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32);
+ *(UInt32 *)(attrListPtr->attr[attrListPtr->count].data) = (CFBooleanGetValue((CFBooleanRef)value)) ? 1 : 0;
+
+ ++attrListPtr->count;
+ }
+
+ // [13] get the negative flag
+ if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrIsNegative, (const void **)&value) ) {
+ attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32));
+ require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_port_failed, status = errSecBufferTooSmall);
+
+ attrListPtr->attr[attrListPtr->count].tag = kSecNegativeItemAttr;
+ attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32);
+ *(UInt32 *)(attrListPtr->attr[attrListPtr->count].data) = (CFBooleanGetValue((CFBooleanRef)value)) ? 1 : 0;
+
+ ++attrListPtr->count;
+ }
+
+ // return the pointer to the attrList
+ *attrList = attrListPtr;
+
+ return ( errSecSuccess );
+
+ /***************/
+
+malloc_authenticationType_failed:
+malloc_protocol_failed:
+malloc_port_failed:
+CFStringCreateAttribute_failed:
+malloc_attrPtr_failed:
+
+ // free any attributes
+ _FreeAttrList(attrListPtr);
+
+calloc_attrListPtr_failed:
+
+ return ( errSecBufferTooSmall );
+}
+
+
+/*
+ * _CreateSecKeychainAttributeListFromDictionary creates a SecKeychainAttributeList
+ * from the attribute key/values in attrDictionary for the specified item class.
+ *
+ * If this function returns errSecSuccess, the pointer to the SecKeychainAttributeList
+ * must be freed by the caller with _FreeAttrList()
+ */
+static OSStatus
+_CreateSecKeychainAttributeListFromDictionary(
+ CFDictionaryRef attrDictionary,
+ SecItemClass itemClass,
+ SecKeychainAttributeList **attrList)
+{
+ switch (itemClass)
+ {
+ case kSecInternetPasswordItemClass:
+ return _CreateSecKeychainInternetPasswordAttributeListFromDictionary(attrDictionary, attrList);
+
+ case kSecGenericPasswordItemClass:
+ return _CreateSecKeychainGenericPasswordAttributeListFromDictionary(attrDictionary, attrList);
+
+ case kSecCertificateItemClass:
+ return _CreateSecKeychainCertificateAttributeListFromDictionary(attrDictionary, attrList);
+
+ case kSecPublicKeyItemClass:
+ case kSecPrivateKeyItemClass:
+ case kSecSymmetricKeyItemClass:
+ return _CreateSecKeychainKeyAttributeListFromDictionary(attrDictionary, attrList);
+
+ default:
+ break;
+ }
+ return errSecParam;
+}
+
+
+/*
+ * _AppNameFromSecTrustedApplication attempts to pull the name of the
+ * application/tool from the SecTrustedApplicationRef.
+ */
+static CFStringRef
+_AppNameFromSecTrustedApplication(
+ CFAllocatorRef alloc,
+ SecTrustedApplicationRef appRef)
+{
+ CFStringRef result;
+ OSStatus status;
+ CFDataRef appDataRef;
+
+ result = NULL;
+
+ // get the data for item's application/tool
+ status = SecTrustedApplicationCopyData(appRef, &appDataRef);
+ if ( status == errSecSuccess ) {
+ CFStringRef path;
+
+ // convert it to a CFString potentially containing the path
+ path = CFStringCreateWithCString(NULL, (char *)CFDataGetBytePtrVoid(appDataRef), kCFStringEncodingUTF8);
+ if ( path != NULL ) {
+ // the path has to start with a "/" and cannot contain "://"
+ if ( CFStringHasPrefix(path, CFSTR("/")) && (CFStringFind(path, CFSTR("://"), 0).location == kCFNotFound) ) {
+ CFRange nameRange, compRg;
+
+ nameRange = CFRangeMake(0, CFStringGetLength(path));
+
+ // remove the trailing slashes (if any)
+ while ( (nameRange.length > 0) && (CFStringGetCharacterAtIndex(path, nameRange.length - 1) == '/') ) {
+ nameRange.length --;
+ }
+
+ if ( nameRange.length > 0 ) {
+ // find last slash and adjust nameRange to be everything after it
+ if ( CFStringFindWithOptions(path, CFSTR("/"), nameRange, kCFCompareBackwards, &compRg) ) {
+ nameRange.length = nameRange.location + nameRange.length - (compRg.location + 1);
+ nameRange.location = compRg.location + 1;
+ }
+
+ result = CFStringCreateWithSubstring(alloc, path, nameRange);
+ }
+ }
+ CFRelease(path);
+ }
+ CFRelease(appDataRef);
+ }
+
+ return ( result );
+}
+
+/* (This function really belongs in SecIdentity.cpp!)
+ *
+ * Returns the public key item corresponding to the identity, if it exists in
+ * the same keychain as the private key. Note that the public key might not
+ * exist in the same keychain (e.g. if the identity was imported via PKCS12),
+ * in which case it will not be found.
+ */
+static OSStatus
+_SecIdentityCopyPublicKey(
+ SecIdentityRef identityRef,
+ SecKeyRef *publicKeyRef)
+{
+ OSStatus status;
+ UInt32 count;
+ SecKeychainAttribute attr = { kSecKeyLabel, 0, NULL };
+ SecKeychainAttributeList attrList = { 1, &attr };
+ SecKeychainAttributeList *keyAttrList = NULL;
+ SecKeychainAttributeInfo *info = NULL;
+ SecKeychainSearchRef search = NULL;
+ SecKeychainRef keychain = NULL;
+ SecKeychainItemRef privateKey = NULL;
+ SecKeychainItemRef publicKey = NULL;
+
+ status = SecIdentityCopyPrivateKey(identityRef, (SecKeyRef *)&privateKey);
+ if (status) {
+ goto error_exit; // identity must have a private key
+ }
+ status = SecKeychainItemCopyKeychain(privateKey, &keychain);
+ if (status) {
+ goto error_exit; // private key must have a keychain, so we can get the attribute info for it
+ }
+ status = SecKeychainAttributeInfoForItemID(keychain, kSecPrivateKeyItemClass, &info);
+ if (status) {
+ goto error_exit; // unable to get the attribute info (i.e. database schema) for private keys
+ }
+ status = SecKeychainItemCopyAttributesAndData(privateKey, info, NULL, &keyAttrList, NULL, NULL);
+ if (status) {
+ goto error_exit; // unable to get the key label attribute for the private key
+ }
+
+ // use the found kSecKeyLabel attribute from the private key in a separate attribute list for searching
+ for (count = 0; count < keyAttrList->count; count++) {
+ if (keyAttrList->attr[count].tag == kSecKeyLabel) {
+ attr.length = keyAttrList->attr[count].length;
+ attr.data = keyAttrList->attr[count].data;
+ break;
+ }
+ }
+ if (!attr.length || !attr.data) {
+ status = errSecNoSuchAttr;
+ goto error_exit; // the private key didn't have the hash of the public key in its kSecKeyLabel
+ }
+ status = SecKeychainSearchCreateFromAttributes(keychain, kSecPublicKeyItemClass, &attrList, &search);
+ if (status) {
+ goto error_exit; // unable to create the search reference
+ }
+ status = SecKeychainSearchCopyNext(search, &publicKey);
+ if (status) {
+ goto error_exit; // unable to find the public key
+ }
+
+ if (publicKeyRef)
+ *publicKeyRef = (SecKeyRef)publicKey;
+ else
+ CFRelease(publicKey);
+
+error_exit:
+ if (status != errSecSuccess) {
+ if (publicKeyRef)
+ *publicKeyRef = NULL;
+ if (publicKey)
+ CFRelease(publicKey);
+ }
+ if (search)
+ CFRelease(search);
+
+ if (keyAttrList)
+ SecKeychainItemFreeAttributesAndData(keyAttrList, NULL);
+
+ if (info)
+ SecKeychainFreeAttributeInfo(info);
+
+ if (keychain)
+ CFRelease(keychain);
+
+ if (privateKey)
+ CFRelease(privateKey);
+
+ return status;
+}
+
+
+/*
+ * Deletes a keychain item if the current application/tool is the only application/tool
+ * with decrypt access to that keychain item. If more than one application/tool
+ * has decrypt access to the keychain item, the item is left on the keychain.
+ *
+ * TBD: If more than one app/tool has access to the keychain item, we should remove
+ * the current app/tool's decrypt access. There's no easy way to do that with
+ * current keychain APIs without bringing up the security UI.
+ */
+static OSStatus
+_SafeSecKeychainItemDelete(
+ SecKeychainItemRef itemRef)
+{
+ OSStatus status;
+ SecAccessRef access = NULL;
+ CFArrayRef aclList = NULL;
+ SecACLRef acl = NULL;
+ CFArrayRef appList = NULL;
+ CFStringRef description = NULL;
+ CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR promptSelector;
+ CFIndex count = 0;
+ SecTrustedApplicationRef currentAppRef = NULL;
+ CFStringRef itemAppName = NULL, currentAppName = NULL;
+
+ SecItemClass itemClass = (SecItemClass)0;
+ status = SecKeychainItemCopyAttributesAndData(itemRef, NULL, &itemClass, NULL, NULL, NULL);
+ if (!(itemClass == kSecInternetPasswordItemClass || itemClass == kSecGenericPasswordItemClass)) {
+ // only perform the access control safety check on deletion of password credentials;
+ // if the item is of some other type, delete it normally.
+ return SecKeychainItemDelete(itemRef);
+ }
+
+ // skip access control checking for web form passwords: <rdar://10957301>
+ // This permits Safari to manage the removal of all web form passwords,
+ // regardless of whether they are shared by multiple applications.
+ if (itemClass == kSecInternetPasswordItemClass) {
+ UInt32 tags[1] = { kSecAuthenticationTypeItemAttr };
+ SecKeychainAttributeInfo attrInfo = { 1, tags, NULL };
+ SecKeychainAttributeList *attrs = NULL;
+ status = SecKeychainItemCopyAttributesAndData(itemRef, &attrInfo, NULL, &attrs, NULL, NULL);
+ if (!status && attrs) {
+ bool webFormPassword = (attrs->attr[0].length == 4 && (!memcmp(attrs->attr[0].data, "form", 4)));
+ SecKeychainItemFreeAttributesAndData(attrs, NULL);
+ if (webFormPassword) {
+ return SecKeychainItemDelete(itemRef);
+ }
+ }
+ }
+
+ // copy the access of the keychain item
+ status = SecKeychainItemCopyAccess(itemRef, &access);
+ require_noerr(status, finish);
+ require_quiet(access != NULL, finish);
+
+ // copy the decrypt access control lists -- this is what has access to the keychain item
+ status = SecAccessCopySelectedACLList(access, CSSM_ACL_AUTHORIZATION_DECRYPT, &aclList);
+ require_noerr(status, finish);
+ require_quiet(aclList != NULL, finish);
+
+ // get the access control list
+ acl = (SecACLRef)CFArrayGetValueAtIndex(aclList, 0);
+ require_quiet(acl != NULL, finish);
+
+ // copy the application list, description, and CSSM prompt selector for a given access control list entry
+ status = SecACLCopySimpleContents(acl, &appList, &description, &promptSelector);
+ require_noerr(status, finish);
+ require_quiet(appList != NULL, finish);
+
+ // does only a single application/tool have decrypt access to this item?
+ count = CFArrayGetCount(appList);
+ if ( count == 1 ) {
+ // get SecTrustedApplicationRef for item's application/tool
+ SecTrustedApplicationRef itemAppRef = (SecTrustedApplicationRef)CFArrayGetValueAtIndex(appList, 0);
+ require_quiet(itemAppRef != NULL, finish);
+
+ // copy the name out
+ itemAppName = _AppNameFromSecTrustedApplication(CFGetAllocator(itemRef), itemAppRef);
+ if (itemAppName == NULL) {
+ /*
+ * If there is no app name, it's probably because it's not an appname
+ * in the ACE but an entitlement/info.plist based rule instead;
+ * just let the caller have it. */
+ count--;
+ goto finish;
+ }
+
+ // create SecTrustedApplicationRef for current application/tool
+ status = SecTrustedApplicationCreateFromPath(NULL, ¤tAppRef);
+ require_noerr(status, finish);
+ require_quiet(currentAppRef != NULL, finish);
+
+ // copy the name out
+ currentAppName = _AppNameFromSecTrustedApplication(CFGetAllocator(itemRef), currentAppRef);
+ require_quiet(currentAppName != NULL, finish);
+
+ // compare the names to see if we own the decrypt access
+ if ( CFStringCompare(currentAppName, itemAppName, 0) == kCFCompareEqualTo ) {
+ // decrement the count to zero, which will remove the item below
+ --count;
+ }
+ }
+
+finish:
+
+ CFReleaseSafe(currentAppName);
+ CFReleaseSafe(itemAppName);
+ CFReleaseSafe(currentAppRef);
+ CFReleaseSafe(description);
+ CFReleaseSafe(appList);
+ CFReleaseSafe(aclList);
+ CFReleaseSafe(access);
+
+ if ((count == 0) || (status == errSecVerifyFailed)) {
+ // no "owners" remain in the ACL list (or unable to get ACL)
+ status = SecKeychainItemDelete(itemRef);
+ } else {
+ // caller is not the "owner" of the item
+ status = errSecInvalidOwnerEdit;
+ }
+
+ return status;
+}
+
+static OSStatus
+_UpdateKeychainItem(CFTypeRef item, CFDictionaryRef changedAttributes)
+{
+ // This function updates a single keychain item, which may be specified as
+ // a reference, persistent reference or attribute dictionary, with the
+ // attributes provided.
+
+ OSStatus status = errSecSuccess;
+ if (!item) {
+ return errSecParam;
+ }
+
+ SecItemClass itemClass;
+ SecAccessRef access = NULL;
+ SecKeychainAttributeList *changeAttrList = NULL;
+ SecKeychainItemRef itemToUpdate = NULL;
+ CFDataRef theData = NULL;
+ CFTypeID itemType = CFGetTypeID(item);
+
+ // validate input item (must be convertible to a SecKeychainItemRef)
+ if (SecKeychainItemGetTypeID() == itemType ||
+ SecCertificateGetTypeID() == itemType ||
+ SecKeyGetTypeID() == itemType) {
+ // item is already a reference, retain it
+ itemToUpdate = (SecKeychainItemRef) CFRetain(item);
+ }
+ else if (CFDataGetTypeID() == itemType) {
+ // item is a persistent reference, must convert it
+ status = SecKeychainItemCopyFromPersistentReference((CFDataRef)item, &itemToUpdate);
+ }
+ else if (CFDictionaryGetTypeID() == itemType) {
+ // item is a dictionary
+ CFTypeRef value = NULL;
+ if (CFDictionaryGetValueIfPresent((CFDictionaryRef)item, kSecValueRef, &value)) {
+ // kSecValueRef value is a SecKeychainItemRef, retain it
+ itemToUpdate = (SecKeychainItemRef) CFRetain(value);
+ }
+ else if (CFDictionaryGetValueIfPresent((CFDictionaryRef)item, kSecValuePersistentRef, &value)) {
+ // kSecValuePersistentRef value is a persistent reference, must convert it
+ status = SecKeychainItemCopyFromPersistentReference((CFDataRef)value, &itemToUpdate);
+ }
+ }
+ else if (SecIdentityGetTypeID() == itemType) {
+ // item is a certificate + private key; since we can't really change the
+ // certificate's attributes, assume we want to update the private key
+ status = SecIdentityCopyPrivateKey((SecIdentityRef)item, (SecKeyRef*)&itemToUpdate);
+ }
+ require_action(itemToUpdate != NULL, update_failed, status = errSecInvalidItemRef);
+ require_noerr(status, update_failed);
+
+ status = SecKeychainItemCopyContent(itemToUpdate, &itemClass, NULL, NULL, NULL);
+ require_noerr(status, update_failed);
+
+ // build changeAttrList from changedAttributes dictionary
+ switch (itemClass)
+ {
+ case kSecInternetPasswordItemClass:
+ {
+ status = _CreateSecKeychainInternetPasswordAttributeListFromDictionary(changedAttributes, &changeAttrList);
+ require_noerr(status, update_failed);
+ }
+ break;
+
+ case kSecGenericPasswordItemClass:
+ {
+ status = _CreateSecKeychainGenericPasswordAttributeListFromDictionary(changedAttributes, &changeAttrList);
+ require_noerr(status, update_failed);
+ }
+ break;
+
+ case kSecCertificateItemClass:
+ {
+ status = _CreateSecKeychainCertificateAttributeListFromDictionary(changedAttributes, &changeAttrList);
+ require_noerr(status, update_failed);
+ }
+ break;
+
+ case kSecPublicKeyItemClass:
+ case kSecPrivateKeyItemClass:
+ case kSecSymmetricKeyItemClass:
+ {
+ status = _CreateSecKeychainKeyAttributeListFromDictionary(changedAttributes, &changeAttrList);
+ require_noerr(status, update_failed);
+ }
+ }
+
+ // get the password
+ // (if the caller is not updating the password, this value will be NULL)
+ theData = (CFDataRef)CFDictionaryGetValue(changedAttributes, kSecValueData);
+ if (theData != NULL) {
+ require_action(CFDataGetTypeID() == CFGetTypeID(theData), update_failed, status = errSecParam);
+ }
+ // update item
+ status = SecKeychainItemModifyContent(itemToUpdate,
+ (changeAttrList->count == 0) ? NULL : changeAttrList,
+ (theData != NULL) ? (UInt32)CFDataGetLength(theData) : 0,
+ (theData != NULL) ? CFDataGetBytePtrVoid(theData) : NULL);
+
+ // one more thing... update access?
+ if (CFDictionaryGetValueIfPresent(changedAttributes, kSecAttrAccess, (const void **)&access)) {
+ status = SecKeychainItemSetAccess(itemToUpdate, access);
+ }
+
+update_failed:
+ if (itemToUpdate)
+ CFRelease(itemToUpdate);
+ _FreeAttrList(changeAttrList);
+ return status;
+}
+
+static OSStatus
+_DeleteKeychainItem(CFTypeRef item)
+{
+ // This function deletes a single keychain item, which may be specified as
+ // a reference, persistent reference or attribute dictionary. It will not
+ // delete non-keychain items or aggregate items (such as a SecIdentityRef);
+ // it is assumed that the caller will pass identity components separately.
+
+ OSStatus status = errSecSuccess;
+ if (!item) {
+ return errSecParam;
+ }
+
+ SecKeychainItemRef itemToDelete = NULL;
+ CFTypeID itemType = CFGetTypeID(item);
+ if (SecKeychainItemGetTypeID() == itemType ||
+ SecCertificateGetTypeID() == itemType ||
+ SecKeyGetTypeID() == itemType) {
+ // item is already a reference, retain it
+ itemToDelete = (SecKeychainItemRef) CFRetain(item);
+ }
+ else if (CFDataGetTypeID() == itemType) {
+ // item is a persistent reference, must convert it
+ status = SecKeychainItemCopyFromPersistentReference((CFDataRef)item, &itemToDelete);
+ }
+ else if (CFDictionaryGetTypeID() == itemType) {
+ // item is a dictionary
+ CFTypeRef value = NULL;
+ if (CFDictionaryGetValueIfPresent((CFDictionaryRef)item, kSecValueRef, &value)) {
+ // kSecValueRef value is a SecKeychainItemRef, retain it
+ itemToDelete = (SecKeychainItemRef) CFRetain(value);
+ }
+ else if (CFDictionaryGetValueIfPresent((CFDictionaryRef)item, kSecValuePersistentRef, &value)) {
+ // kSecValuePersistentRef value is a persistent reference, must convert it
+ status = SecKeychainItemCopyFromPersistentReference((CFDataRef)value, &itemToDelete);
+ }
+ }
+
+ if (itemToDelete) {
+ if (!status) {
+ status = _SafeSecKeychainItemDelete(itemToDelete);
+ }
+ CFRelease(itemToDelete);
+ }
+
+ return status;
+}
+
+static OSStatus
+_DeleteIdentity(SecIdentityRef identity)
+{
+ OSStatus status, result = errSecSuccess;
+ SecKeyRef privateKey = NULL;
+ SecCertificateRef certificate = NULL;
+
+ status = SecIdentityCopyPrivateKey(identity, &privateKey);
+ if (!status) {
+ SecKeyRef publicKey = NULL;
+ status = _SecIdentityCopyPublicKey(identity, &publicKey);
+ if (!status) {
+ status = _DeleteKeychainItem(publicKey);
+ CFRelease(publicKey);
+ }
+ status = _DeleteKeychainItem(privateKey);
+ }
+
+ if (privateKey) CFRelease(privateKey);
+ if (status) result = status;
+
+ status = SecIdentityCopyCertificate(identity, &certificate);
+ if (!status) {
+ status = _DeleteKeychainItem(certificate);
+ }
+
+ if (certificate) CFRelease(certificate);
+ if (status) result = status;
+
+ return result;
+}
+
+static OSStatus
+_UpdateAggregateStatus(OSStatus newStatus, OSStatus curStatus, OSStatus baseStatus)
+{
+ // This function is used when atomically processing multiple items,
+ // where an overall error result must be returned for the entire operation.
+ // When newStatus is something other than errSecSuccess, we want to keep the "most
+ // interesting" status (which usually will be newStatus, unless curStatus is
+ // already set; in that case, newStatus can trump curStatus only by being
+ // something different than baseStatus.)
+
+ OSStatus result = curStatus;
+
+ if (newStatus != errSecSuccess) {
+ result = newStatus;
+ if (curStatus != errSecSuccess) {
+ result = (newStatus != baseStatus) ? newStatus : curStatus;
+ }
+ }
+ return result;
+}
+
+static void
+_AddDictValueToOtherDict(const void *key, const void *value, void *context)
+{
+ // CFDictionaryApplierFunction
+ // This function just takes the given key/value pair,
+ // and adds it to another dictionary supplied in the context argument.
+
+ CFMutableDictionaryRef dict = *((CFMutableDictionaryRef*) context);
+ if (key && value) {
+ CFDictionaryAddValue(dict, key, value);
+ }
+}
+
+static CFStringCompareFlags
+_StringCompareFlagsFromQuery(CFDictionaryRef query)
+{
+ CFTypeRef value;
+ CFStringCompareFlags flags = 0;
+ if (!query) return flags;
+
+ if (CFDictionaryGetValueIfPresent(query, kSecMatchSubjectStartsWith, (const void **)&value) ||
+ CFDictionaryGetValueIfPresent(query, kSecMatchSubjectEndsWith, (const void **)&value))
+ flags |= kCFCompareAnchored;
+
+ if (CFDictionaryGetValueIfPresent(query, kSecMatchSubjectEndsWith, (const void **)&value))
+ flags |= kCFCompareBackwards;
+
+ if (CFDictionaryGetValueIfPresent(query, kSecMatchCaseInsensitive, (const void **)&value) && CFEqual(kCFBooleanTrue, value))
+ flags |= kCFCompareCaseInsensitive;
+
+ if (CFDictionaryGetValueIfPresent(query, kSecMatchDiacriticInsensitive, (const void **)&value) && CFEqual(kCFBooleanTrue, value))
+ flags |= kCFCompareDiacriticInsensitive;
+
+ if (CFDictionaryGetValueIfPresent(query, kSecMatchWidthInsensitive, (const void **)&value) && CFEqual(kCFBooleanTrue, value))
+ flags |= kCFCompareWidthInsensitive;
+
+ return flags;
+}
+
+static uint32
+_CssmKeyUsageFromQuery(CFDictionaryRef query)
+{
+ CFTypeRef value;
+ uint32 keyUsage = 0;
+ if (!query) return keyUsage;
+
+ if (CFDictionaryGetValueIfPresent(query, kSecAttrCanEncrypt, (const void **)&value) && CFEqual(kCFBooleanTrue, value))
+ keyUsage |= CSSM_KEYUSE_ENCRYPT;
+
+ if (CFDictionaryGetValueIfPresent(query, kSecAttrCanDecrypt, (const void **)&value) && CFEqual(kCFBooleanTrue, value))
+ keyUsage |= CSSM_KEYUSE_DECRYPT;
+
+ if (CFDictionaryGetValueIfPresent(query, kSecAttrCanSign, (const void **)&value) && CFEqual(kCFBooleanTrue, value))
+ keyUsage |= CSSM_KEYUSE_SIGN;
+
+ if (CFDictionaryGetValueIfPresent(query, kSecAttrCanVerify, (const void **)&value) && CFEqual(kCFBooleanTrue, value))
+ keyUsage |= CSSM_KEYUSE_VERIFY;
+
+ if (CFDictionaryGetValueIfPresent(query, kSecAttrCanWrap, (const void **)&value) && CFEqual(kCFBooleanTrue, value))
+ keyUsage |= CSSM_KEYUSE_WRAP;
+
+ if (CFDictionaryGetValueIfPresent(query, kSecAttrCanUnwrap, (const void **)&value) && CFEqual(kCFBooleanTrue, value))
+ keyUsage |= CSSM_KEYUSE_UNWRAP;
+
+ if (CFDictionaryGetValueIfPresent(query, kSecAttrCanDerive, (const void **)&value) && CFEqual(kCFBooleanTrue, value))
+ keyUsage |= CSSM_KEYUSE_DERIVE;
+
+ return keyUsage;
+}
+
+static SecItemClass
+_ConvertItemClass(const void* item, const void* keyClass, Boolean *isIdentity)
+{
+ SecItemClass itemClass = (SecItemClass) 0;
+ if (isIdentity) *isIdentity = false;
+
+ if (CFEqual(item, kSecClassGenericPassword)) {
+ itemClass = kSecGenericPasswordItemClass;
+ }
+ else if (CFEqual(item, kSecClassInternetPassword)) {
+ itemClass = kSecInternetPasswordItemClass;
+ }
+ else if (CFEqual(item, kSecClassCertificate)) {
+ itemClass = kSecCertificateItemClass;
+ }
+ else if (CFEqual(item, kSecClassIdentity)) {
+ // will perform a certificate lookup
+ itemClass = kSecCertificateItemClass;
+ if (isIdentity) *isIdentity = true;
+ }
+ else if (CFEqual(item, kSecClassKey)) {
+ // examine second parameter to determine type of key
+ if (!keyClass || CFEqual(keyClass, kSecAttrKeyClassSymmetric)) {
+ itemClass = kSecSymmetricKeyItemClass;
+ }
+ else if (keyClass && CFEqual(keyClass, kSecAttrKeyClassPublic)) {
+ itemClass = kSecPublicKeyItemClass;
+ }
+ else if (keyClass && CFEqual(keyClass, kSecAttrKeyClassPrivate)) {
+ itemClass = kSecPrivateKeyItemClass;
+ }
+ }
+
+ return itemClass;
+}
+
+static SecItemClass
+_ItemClassFromItemList(CFArrayRef itemList)
+{
+ // Given a list of items (standard or persistent references),
+ // determine whether they all have the same item class. Returns
+ // the item class, or 0 if multiple classes in list.
+ SecItemClass result = 0;
+ CFIndex index, count = (itemList) ? CFArrayGetCount(itemList) : 0;
+ for (index=0; index < count; index++) {
+ CFTypeRef item = (CFTypeRef) CFArrayGetValueAtIndex(itemList, index);
+ if (item) {
+ SecKeychainItemRef itemRef = NULL;
+ OSStatus status;
+ if (CFGetTypeID(item) == CFDataGetTypeID()) {
+ // persistent reference, resolve first
+ status = SecKeychainItemCopyFromPersistentReference((CFDataRef)item, &itemRef);
+ }
+ else {
+ itemRef = (SecKeychainItemRef) CFRetain(item);
+ }
+ if (itemRef) {
+ SecItemClass itemClass = 0;
+ CFTypeID itemTypeID = CFGetTypeID(itemRef);
+ if (itemTypeID == SecIdentityGetTypeID() || itemTypeID == SecCertificateGetTypeID()) {
+ // Identities and certificates have the same underlying item class
+ itemClass = kSecCertificateItemClass;
+ }
+ else if (itemTypeID == SecKeychainItemGetTypeID()) {
+ // Reference to item in a keychain
+ status = SecKeychainItemCopyAttributesAndData(itemRef, NULL, &itemClass, NULL, NULL, NULL);
+ }
+ else if (itemTypeID == SecKeyGetTypeID()) {
+ // SecKey that isn't stored in a keychain
+ // %%% will need to change this code when SecKey is no longer CSSM-based %%%
+ const CSSM_KEY *cssmKey;
+ status = SecKeyGetCSSMKey((SecKeyRef)itemRef, &cssmKey);
+ if (status == errSecSuccess) {
+ if (cssmKey->KeyHeader.KeyClass == CSSM_KEYCLASS_PUBLIC_KEY)
+ itemClass = kSecPublicKeyItemClass;
+ else if (cssmKey->KeyHeader.KeyClass == CSSM_KEYCLASS_PRIVATE_KEY)
+ itemClass = kSecPrivateKeyItemClass;
+ else
+ itemClass = kSecSymmetricKeyItemClass;
+ }
+ }
+ CFRelease(itemRef);
+ if (itemClass != 0) {
+ if (result != 0 && result != itemClass) {
+ return 0; // different item classes in list; bail out
+ }
+ result = itemClass;
+ }
+ }
+ }
+ }
+ return result;
+}
+
+// SecItemParams contains a validated set of input parameters, as well as a
+// search reference and attribute list built from those parameters. It is
+// designed to be allocated with _CreateSecItemParamsFromDictionary, and
+// freed with _FreeSecItemParams.
+
+struct SecItemParams {
+ CFDictionaryRef query; // caller-supplied query
+ int numResultTypes; // number of result types requested
+ int maxMatches; // max number of matches to return
+ uint32 keyUsage; // key usage(s) requested
+ Boolean returningAttributes; // true if returning attributes dictionary
+ Boolean returningData; // true if returning item's data
+ Boolean returningRef; // true if returning item reference
+ Boolean returningPersistentRef; // true if returing a persistent reference
+ Boolean returnAllMatches; // true if we should return all matches
+ Boolean returnIdentity; // true if we are returning a SecIdentityRef
+ Boolean trustedOnly; // true if we only return trusted certs
+ Boolean issuerAndSNToMatch; // true if both issuer and SN were provided
+ SecItemClass itemClass; // item class for this query
+ SecPolicyRef policy; // value for kSecMatchPolicy (may be NULL)
+ SecKeychainRef keychain; // value for kSecUseKeychain (may be NULL)
+ CFArrayRef useItems; // value for kSecUseItemList (may be NULL)
+ CFArrayRef itemList; // value for kSecMatchItemList (may be NULL)
+ CFTypeRef searchList; // value for kSecMatchSearchList (may be NULL)
+ CFTypeRef matchLimit; // value for kSecMatchLimit (may be NULL)
+ CFTypeRef emailAddrToMatch; // value for kSecMatchEmailAddressIfPresent (may be NULL)
+ CFTypeRef validOnDate; // value for kSecMatchValidOnDate (may be NULL)
+ CFTypeRef keyClass; // value for kSecAttrKeyClass (may be NULL)
+ CFTypeRef service; // value for kSecAttrService (may be NULL)
+ CFTypeRef issuer; // value for kSecAttrIssuer (may be NULL)
+ CFTypeRef serialNumber; // value for kSecAttrSerialNumber (may be NULL)
+ CFTypeRef search; // search reference for this query (SecKeychainSearchRef or SecIdentitySearchRef)
+ CFTypeRef assumedKeyClass; // if no kSecAttrKeyClass provided, holds the current class we're searching for
+ CFIndex itemListIndex; // if no search reference but we have itemList, holds index of next item to return
+ SecKeychainAttributeList *attrList; // attribute list for this query
+ SecAccessRef access; // access reference (for SecItemAdd only, not used to find items)
+ CFDataRef itemData; // item data (for SecItemAdd only, not used to find items)
+ CFTypeRef itemRef; // item reference (to find, add, update or delete, depending on context)
+ SecIdentityRef identityRef; // identity reference (input as kSecValueRef)
+ CFDataRef itemPersistentRef; // item persistent reference (to find, add, update or delete, depending on context)
+ Boolean isPCSItem; // true if this query is for a Protected Cloud Storage item
+};
+
+static OSStatus
+_ValidateDictionaryEntry(CFDictionaryRef dict, CFTypeRef key, const void **value, CFTypeID expectedTypeID, CFTypeID altTypeID)
+{
+ if (!dict || !key || !value || !expectedTypeID)
+ return errSecParam;
+
+ if (!CFDictionaryGetValueIfPresent(dict, key, value)) {
+ // value was not provided for this key (not an error!)
+ *value = NULL;
+ }
+ else if (!(*value)) {
+ // provided value is NULL (also not an error!)
+ return errSecSuccess;
+ }
+ else {
+ CFTypeID actualTypeID = CFGetTypeID(*value);
+ if (!((expectedTypeID == actualTypeID) || (altTypeID && altTypeID == actualTypeID))) {
+ // provided value does not have the expected (or alternate) CF type ID
+ if ((expectedTypeID == SecKeychainItemGetTypeID()) &&
+ (actualTypeID == SecKeyGetTypeID() || actualTypeID == SecCertificateGetTypeID())) {
+ // provided value is a "floating" reference which is not yet in a keychain
+ CFRetain(*value);
+ return errSecSuccess;
+ }
+ return errSecItemInvalidValue;
+ }
+ else {
+ // provided value is OK; retain it
+ CFRetain(*value);
+ }
+ }
+ return errSecSuccess;
+}
+
+static void
+_EnsureUserDefaultKeychainIsSearched(SecItemParams *itemParams)
+{
+ OSStatus status;
+ CFArrayRef tmpList = (CFArrayRef) itemParams->searchList;
+ if (tmpList) {
+ // search list exists; make it mutable
+ itemParams->searchList = (CFArrayRef) CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, tmpList);
+ CFRelease(tmpList);
+ } else {
+ // no search list; start with default list
+ status = SecKeychainCopySearchList(&tmpList);
+ if (!status && tmpList) {
+ itemParams->searchList = (CFArrayRef) CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, tmpList);
+ CFRelease(tmpList);
+ }
+ else {
+ itemParams->searchList = (CFArrayRef) CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
+ }
+ }
+
+ SecKeychainRef userKeychain = NULL;
+ status = SecKeychainCopyDomainDefault(kSecPreferencesDomainUser, &userKeychain);
+ if (!status && userKeychain) {
+ if (!CFArrayContainsValue((CFArrayRef)itemParams->searchList,
+ CFRangeMake(0, CFArrayGetCount((CFArrayRef)itemParams->searchList)), userKeychain)) {
+ // user's default keychain isn't currently in the search list, so append it
+ CFArrayAppendValue((CFMutableArrayRef)itemParams->searchList, userKeychain);
+ }
+ CFRelease(userKeychain);
+ }
+}
+
+static void
+_EnsureUserDefaultKeychainIsTargeted(SecItemParams *itemParams)
+{
+ if (itemParams->keychain) {
+ return; // keychain is already explicitly specified, assume it's correct
+ }
+ SecKeychainRef userKeychain = NULL;
+ OSStatus status = SecKeychainCopyDomainDefault(kSecPreferencesDomainUser, &userKeychain);
+ if (!status && userKeychain) {
+ itemParams->keychain = userKeychain;
+ }
+}
+
+static void
+_FreeSecItemParams(SecItemParams *itemParams)
+{
+ if (!itemParams)
+ return;
+
+ if (itemParams->query) CFRelease(itemParams->query);
+ if (itemParams->policy) CFRelease(itemParams->policy);
+ if (itemParams->keychain) CFRelease(itemParams->keychain);
+ if (itemParams->useItems) CFRelease(itemParams->useItems);
+ if (itemParams->itemList) CFRelease(itemParams->itemList);
+ if (itemParams->searchList) CFRelease(itemParams->searchList);
+ if (itemParams->matchLimit) CFRelease(itemParams->matchLimit);
+ if (itemParams->emailAddrToMatch) CFRelease(itemParams->emailAddrToMatch);
+ if (itemParams->validOnDate) CFRelease(itemParams->validOnDate);
+ if (itemParams->keyClass) CFRelease(itemParams->keyClass);
+ if (itemParams->service) CFRelease(itemParams->service);
+ if (itemParams->issuer) CFRelease(itemParams->issuer);
+ if (itemParams->serialNumber) CFRelease(itemParams->serialNumber);
+ if (itemParams->search) CFRelease(itemParams->search);
+ if (itemParams->access) CFRelease(itemParams->access);
+ if (itemParams->itemData) CFRelease(itemParams->itemData);
+ if (itemParams->itemRef) CFRelease(itemParams->itemRef);
+ if (itemParams->identityRef) CFRelease(itemParams->identityRef);
+ if (itemParams->itemPersistentRef) CFRelease(itemParams->itemPersistentRef);
+
+ _FreeAttrList(itemParams->attrList);
+
+ free(itemParams);
+}
+
+static SecItemParams*
+_CreateSecItemParamsFromDictionary(CFDictionaryRef dict, OSStatus *error)
+{
+ OSStatus status;
+ CFTypeRef value = NULL;
+ SecItemParams *itemParams = (SecItemParams *) malloc(sizeof(SecItemParams));
+
+ require_action(itemParams != NULL, error_exit, status = errSecAllocate);
+ require_action(dict && (CFDictionaryGetTypeID() == CFGetTypeID(dict)), error_exit, status = errSecParam);
+
+ memset(itemParams, 0, sizeof(SecItemParams));
+ itemParams->query = (CFDictionaryRef) CFRetain(dict);
+
+ // validate input search parameters
+ require_noerr(status = _ValidateDictionaryEntry(dict, kSecMatchPolicy, (const void **)&itemParams->policy, SecPolicyGetTypeID(), NULL), error_exit);
+ require_noerr(status = _ValidateDictionaryEntry(dict, kSecMatchSearchList, (const void **)&itemParams->searchList, CFArrayGetTypeID(), NULL), error_exit);
+ require_noerr(status = _ValidateDictionaryEntry(dict, kSecMatchItemList, (const void **)&itemParams->itemList, CFArrayGetTypeID(), NULL), error_exit);
+ require_noerr(status = _ValidateDictionaryEntry(dict, kSecMatchEmailAddressIfPresent, (const void **)&itemParams->emailAddrToMatch, CFStringGetTypeID(), NULL), error_exit);
+ require_noerr(status = _ValidateDictionaryEntry(dict, kSecMatchValidOnDate, (const void **)&itemParams->validOnDate, CFDateGetTypeID(), CFNullGetTypeID()), error_exit);
+ require_noerr(status = _ValidateDictionaryEntry(dict, kSecMatchLimit, (const void **)&itemParams->matchLimit, CFStringGetTypeID(), CFNumberGetTypeID()), error_exit);
+
+ require_noerr(status = _ValidateDictionaryEntry(dict, kSecUseItemList, (const void **)&itemParams->useItems, CFArrayGetTypeID(), NULL), error_exit);
+ require_noerr(status = _ValidateDictionaryEntry(dict, kSecUseKeychain, (const void **)&itemParams->keychain, SecKeychainGetTypeID(), NULL), error_exit);
+
+ // validate a subset of input attributes (used to create an appropriate search reference)
+ require_noerr(status = _ValidateDictionaryEntry(dict, kSecAttrIssuer, (const void **)&itemParams->issuer, CFDataGetTypeID(), NULL), error_exit);
+ require_noerr(status = _ValidateDictionaryEntry(dict, kSecAttrSerialNumber, (const void **)&itemParams->serialNumber, CFDataGetTypeID(), NULL), error_exit);
+ require_noerr(status = _ValidateDictionaryEntry(dict, kSecAttrService, (const void **)&itemParams->service, CFStringGetTypeID(), NULL), error_exit);
+ require_noerr(status = _ValidateDictionaryEntry(dict, kSecAttrKeyClass, (const void **)&itemParams->keyClass, CFStringGetTypeID(), NULL), error_exit);
+
+ if (itemParams->service && CFStringHasPrefix((CFStringRef)itemParams->service, CFSTR("ProtectedCloudStorage"))) {
+ itemParams->isPCSItem = true;
+ if (!SecItemSynchronizable(dict)) {
+ _EnsureUserDefaultKeychainIsSearched(itemParams); // for SecItemCopyMatching, SecItemUpdate, SecItemDelete
+ _EnsureUserDefaultKeychainIsTargeted(itemParams); // for SecItemAdd
+ }
+ }
+
+ // validate the payload (password, key or certificate data), used for SecItemAdd but not for finding items
+ require_noerr(status = _ValidateDictionaryEntry(dict, kSecValueData, (const void **)&itemParams->itemData, CFDataGetTypeID(), CFStringGetTypeID()), error_exit);
+
+ // validate item references
+ require_noerr(status = _ValidateDictionaryEntry(dict, kSecValueRef, (const void **)&itemParams->itemRef, SecKeychainItemGetTypeID(), SecIdentityGetTypeID()), error_exit);
+ if (itemParams->itemRef && (CFGetTypeID(itemParams->itemRef) == SecIdentityGetTypeID())) {
+ itemParams->identityRef = (SecIdentityRef)itemParams->itemRef;
+ itemParams->itemRef = NULL;
+ SecIdentityCopyCertificate(itemParams->identityRef, (SecCertificateRef *)&itemParams->itemRef);
+ }
+ require_noerr(status = _ValidateDictionaryEntry(dict, kSecValuePersistentRef, (const void **)&itemParams->itemPersistentRef, CFDataGetTypeID(), NULL), error_exit);
+ if (itemParams->itemRef || itemParams->itemPersistentRef) {
+ // Caller is trying to add or find an item by reference.
+ // The supported method for doing that is to provide a kSecUseItemList array
+ // for SecItemAdd, or a kSecMatchItemList array for SecItemCopyMatching et al,
+ // so add the item reference to those arrays here.
+ if (itemParams->useItems) {
+ CFArrayRef tmpItems = itemParams->useItems;
+ itemParams->useItems = (CFArrayRef) CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, tmpItems);
+ CFRelease(tmpItems);
+ } else {
+ itemParams->useItems = (CFArrayRef) CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
+ }
+ if (itemParams->itemRef) CFArrayAppendValue((CFMutableArrayRef)itemParams->useItems, itemParams->itemRef);
+ if (itemParams->itemPersistentRef) CFArrayAppendValue((CFMutableArrayRef)itemParams->useItems, itemParams->itemPersistentRef);
+
+ if (itemParams->itemList) {
+ CFArrayRef tmpItems = itemParams->itemList;
+ itemParams->itemList = (CFArrayRef) CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, tmpItems);
+ CFRelease(tmpItems);
+ } else {
+ itemParams->itemList = (CFArrayRef) CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
+ }
+ if (itemParams->itemRef) CFArrayAppendValue((CFMutableArrayRef)itemParams->itemList, itemParams->itemRef);
+ if (itemParams->itemPersistentRef) CFArrayAppendValue((CFMutableArrayRef)itemParams->itemList, itemParams->itemPersistentRef);
+ }
+
+ // must have an explicit item class, unless one of the following is true:
+ // - we have an item list to add or search (kSecUseItemList)
+ // - we have an item reference or persistent reference for the thing we want to look up
+ // Note that both of these cases will set itemParams->useItems.
+ // If we have an item list to match (kSecMatchItemList), that still requires an item class,
+ // so we can perform a search and see if the results match items in the list.
+ //
+ if (!CFDictionaryGetValueIfPresent(dict, kSecClass, (const void**) &value) && !itemParams->useItems) {
+ require_action(false, error_exit, status = errSecItemClassMissing);
+ }
+ else if (value) {
+ itemParams->itemClass = _ConvertItemClass(value, itemParams->keyClass, &itemParams->returnIdentity);
+ if (itemParams->itemClass == kSecSymmetricKeyItemClass && !itemParams->keyClass) {
+ itemParams->assumedKeyClass = kSecAttrKeyClassSymmetric; // no key class specified, so start with symmetric key class; will search the others later
+ }
+ require_action(!(itemParams->itemClass == 0 && !itemParams->useItems), error_exit, status = errSecItemClassMissing);
+ }
+
+ itemParams->keyUsage = _CssmKeyUsageFromQuery(dict);
+ itemParams->trustedOnly = CFDictionaryGetValueIfPresent(dict, kSecMatchTrustedOnly, (const void **)&value) && value && CFEqual(kCFBooleanTrue, value);
+ itemParams->issuerAndSNToMatch = (itemParams->issuer != NULL && itemParams->serialNumber != NULL);
+
+ // other input attributes, used for SecItemAdd but not for finding items
+ require_noerr(status = _ValidateDictionaryEntry(dict, kSecAttrAccess, (const void **)&itemParams->access, SecAccessGetTypeID(), NULL), error_exit);
+ if (itemParams->access == NULL) {
+ // check for the old definition of kSecAttrAccess from SecItem-shim (see <rdar://7987447>)
+ require_noerr(status = _ValidateDictionaryEntry(dict, CFSTR("kSecAttrAccess"), (const void **)&itemParams->access, SecAccessGetTypeID(), NULL), error_exit);
+ }
+
+ // determine how to return the result
+ itemParams->numResultTypes = 0;
+ itemParams->returningRef = CFDictionaryGetValueIfPresent(dict, kSecReturnRef, (const void **)&value) && value && CFEqual(kCFBooleanTrue, value);
+ if (itemParams->returningRef) ++itemParams->numResultTypes;
+ itemParams->returningPersistentRef = CFDictionaryGetValueIfPresent(dict, kSecReturnPersistentRef, (const void **)&value) && value && CFEqual(kCFBooleanTrue, value);
+ if (itemParams->returningPersistentRef) ++itemParams->numResultTypes;
+ itemParams->returningAttributes = CFDictionaryGetValueIfPresent(dict, kSecReturnAttributes, (const void **)&value) && value && CFEqual(kCFBooleanTrue, value);
+ if (itemParams->returningAttributes) ++itemParams->numResultTypes;
+ itemParams->returningData = CFDictionaryGetValueIfPresent(dict, kSecReturnData, (const void **)&value) && value && CFEqual(kCFBooleanTrue, value);
+ if (itemParams->returningData) ++itemParams->numResultTypes;
+
+ // default is kSecReturnRef if no result types were specified
+ if (!itemParams->numResultTypes) {
+ itemParams->returningRef = TRUE;
+ itemParams->numResultTypes = 1;
+ }
+
+ // determine if one, some or all matches should be returned (default is kSecMatchLimitOne)
+ itemParams->maxMatches = 1;
+ itemParams->returnAllMatches = FALSE;
+ if (itemParams->matchLimit) {
+ if (CFStringGetTypeID() == CFGetTypeID(itemParams->matchLimit)) {
+ itemParams->returnAllMatches = CFEqual(kSecMatchLimitAll, itemParams->matchLimit);
+ }
+ else if (CFNumberGetTypeID() == CFGetTypeID(itemParams->matchLimit)) {
+ CFNumberGetValue((CFNumberRef)itemParams->matchLimit, kCFNumberIntType, &itemParams->maxMatches);
+ require_action(!(itemParams->maxMatches < 0), error_exit, status = errSecMatchLimitUnsupported);
+ }
+ }
+ if (itemParams->returnAllMatches) {
+ itemParams->maxMatches = INT32_MAX;
+ // if we're returning all matches, then we don't support getting passwords as data (which could require authentication for each)
+ if ((itemParams->itemClass==kSecInternetPasswordItemClass || itemParams->itemClass==kSecGenericPasswordItemClass) && itemParams->returningData)
+ status = errSecReturnDataUnsupported;
+ require_noerr(status, error_exit);
+ }
+
+ // if we already have an item list (to add or find items in), we don't need an item class, attribute list or a search reference
+ if (itemParams->useItems) {
+ if (itemParams->itemClass == 0) {
+ itemParams->itemClass = _ItemClassFromItemList(itemParams->useItems);
+ }
+ status = errSecSuccess;
+ goto error_exit; // all done here
+ }
+
+ // build a SecKeychainAttributeList from the query dictionary for the specified item class
+ require_noerr(status = _CreateSecKeychainAttributeListFromDictionary(dict, itemParams->itemClass, &itemParams->attrList), error_exit);
+
+ // create a search reference (either a SecKeychainSearchRef or a SecIdentitySearchRef)
+ if ((itemParams->itemClass == kSecCertificateItemClass) && itemParams->emailAddrToMatch) {
+ // searching for certificates by email address
+ char *nameBuf = (char*)malloc(MAXPATHLEN);
+ if (!nameBuf) {
+ status = errSecAllocate;
+ }
+ else if (CFStringGetCString((CFStringRef)itemParams->emailAddrToMatch, nameBuf, (CFIndex)MAXPATHLEN-1, kCFStringEncodingUTF8)) {
+ status = SecKeychainSearchCreateForCertificateByEmail(itemParams->searchList, (const char *)nameBuf, (SecKeychainSearchRef*)&itemParams->search);
+ }
+ else {
+ status = errSecItemInvalidValue;
+ }
+ if (nameBuf) free(nameBuf);
+ }
+ else if ((itemParams->itemClass == kSecCertificateItemClass) && itemParams->issuerAndSNToMatch) {
+ // searching for certificates by issuer and serial number
+ status = SecKeychainSearchCreateForCertificateByIssuerAndSN_CF(itemParams->searchList,
+ (CFDataRef)itemParams->issuer,
+ (CFDataRef)itemParams->serialNumber,
+ (SecKeychainSearchRef*)&itemParams->search);
+ }
+ else if (itemParams->returnIdentity && itemParams->policy) {
+ // searching for identities by policy
+ status = SecIdentitySearchCreateWithPolicy(itemParams->policy,
+ (CFStringRef)itemParams->service,
+ itemParams->keyUsage,
+ itemParams->searchList,
+ itemParams->trustedOnly,
+ (SecIdentitySearchRef*)&itemParams->search);
+ }
+ else if (itemParams->returnIdentity) {
+ // searching for identities
+ status = SecIdentitySearchCreate(itemParams->searchList,
+ itemParams->keyUsage,
+ (SecIdentitySearchRef*)&itemParams->search);
+ }
+ else {
+ // normal keychain item search
+ status = SecKeychainSearchCreateFromAttributes(itemParams->searchList,
+ itemParams->itemClass,
+ (itemParams->attrList->count == 0) ? NULL : itemParams->attrList,
+ (SecKeychainSearchRef*)&itemParams->search);
+ }
+
+error_exit:
+ if (status) {
+ _FreeSecItemParams(itemParams);
+ itemParams = NULL;
+ }
+ if (error) {
+ *error = status;
+ }
+ return itemParams;
+}
+
+
+static OSStatus
+_ImportKey(
+ SecKeyRef keyRef,
+ SecKeychainRef keychainRef,
+ SecAccessRef accessRef,
+ SecKeychainAttributeList *attrList,
+ SecKeychainItemRef *outItemRef)
+{
+ BEGIN_SECAPI
+
+ // We must specify the access, since a free-floating key won't have one yet by default
+ SecPointer<Access> access;
+ if (accessRef) {
+ access = Access::required(accessRef);
+ }
+ else {
+ CFStringRef descriptor = NULL;
+ if (attrList) {
+ for (UInt32 index=0; index < attrList->count; index++) {
+ SecKeychainAttribute attr = attrList->attr[index];
+ if (attr.tag == kSecKeyPrintName) {
+ descriptor = CFStringCreateWithBytes(NULL, (const UInt8 *)attr.data, attr.length, kCFStringEncodingUTF8, FALSE);
+ break;
+ }
+ }
+ }
+ if (descriptor == NULL) {
+ descriptor = (CFStringRef) CFRetain(CFSTR("<unknown>"));
+ }
+ access = new Access(cfString(descriptor));
+ CFRelease(descriptor);
+ }
+
+ KeyItem *key = KeyItem::required(keyRef);
+ Item item = key->importTo(Keychain::optional(keychainRef), access, attrList);
+ if (outItemRef)
+ *outItemRef = item->handle();
+
+ END_SECAPI
+}
+
+static Boolean
+_CanIgnoreLeafStatusCodes(CSSM_TP_APPLE_EVIDENCE_INFO *evidence)
+{
+ /* Check for ignorable status codes in leaf certificate's evidence */
+ Boolean result = true;
+ unsigned int i;
+ for (i=0; i < evidence->NumStatusCodes; i++) {
+ CSSM_RETURN scode = evidence->StatusCodes[i];
+ if (scode == CSSMERR_APPLETP_INVALID_CA) {
+ // the TP has rejected this CA cert because it's in the leaf position
+ result = true;
+ }
+ else if (ignorableRevocationStatusCode(scode)) {
+ result = true;
+ }
+ else {
+ result = false;
+ break;
+ }
+ }
+ return result;
+}
+
+static OSStatus
+_FilterWithPolicy(SecPolicyRef policy, CFDateRef date, SecCertificateRef cert)
+{
+ CFDictionaryRef props = NULL;
+ CFArrayRef keychains = NULL;
+ CFArrayRef anchors = NULL;
+ CFArrayRef certs = NULL;
+ CFArrayRef chain = NULL;
+ SecTrustRef trust = NULL;
+
+ SecTrustResultType trustResult;
+ CSSM_TP_APPLE_EVIDENCE_INFO *evidence = NULL;
+ Boolean needChain = false;
+ OSStatus status;
+ if (!policy || !cert) return errSecParam;
+
+ certs = CFArrayCreate(NULL, (const void **)&cert, (CFIndex)1, &kCFTypeArrayCallBacks);
+ status = SecTrustCreateWithCertificates(certs, policy, &trust);
+ if(status) goto cleanup;
+
+ /* Set evaluation date, if specified (otherwise current date is implied) */
+ if (date && (CFGetTypeID(date) == CFDateGetTypeID())) {
+ status = SecTrustSetVerifyDate(trust, date);
+ if(status) goto cleanup;
+ }
+
+ /* Check whether this is the X509 Basic policy, which means chain building */
+ props = SecPolicyCopyProperties(policy);
+ if (props) {
+ CFTypeRef oid = (CFTypeRef) CFDictionaryGetValue(props, kSecPolicyOid);
+ if (oid && CFEqual(oid, kSecPolicyAppleX509Basic)) {
+ needChain = true;
+ }
+ }
+
+ if (!needChain) {
+ /* To make the evaluation as lightweight as possible, specify an empty array
+ * of keychains which will be searched for certificates.
+ */
+ keychains = CFArrayCreate(NULL, NULL, 0, &kCFTypeArrayCallBacks);
+ status = SecTrustSetKeychains(trust, keychains);
+ if(status) goto cleanup;
+
+ /* To make the evaluation as lightweight as possible, specify an empty array
+ * of trusted anchors.
+ */
+ anchors = CFArrayCreate(NULL, NULL, 0, &kCFTypeArrayCallBacks);
+ status = SecTrustSetAnchorCertificates(trust, anchors);
+ if(status) goto cleanup;
+ }
+
+ /* All parameters are locked and loaded, ready to evaluate! */
+ status = SecTrustEvaluate(trust, &trustResult);
+ if(status) goto cleanup;
+
+ /* If we didn't provide trust anchors or a way to look for them,
+ * the evaluation will fail with kSecTrustResultRecoverableTrustFailure.
+ * However, we can tell whether the policy evaluation succeeded by
+ * looking at the per-cert status codes in the returned evidence.
+ */
+ status = SecTrustGetResult(trust, &trustResult, &chain, &evidence);
+ if(status) goto cleanup;
+
+ if (!(trustResult == kSecTrustResultProceed ||
+ trustResult == kSecTrustResultUnspecified ||
+ trustResult == kSecTrustResultRecoverableTrustFailure)) {
+ /* The evaluation failed in a non-recoverable way */
+ status = errSecCertificateCannotOperate;
+ goto cleanup;
+ }
+
+ /* If there are no per-cert policy status codes,
+ * and the cert has not expired, consider it valid for the policy.
+ */
+ if((evidence != NULL) && _CanIgnoreLeafStatusCodes(evidence) &&
+ ((evidence[0].StatusBits & CSSM_CERT_STATUS_EXPIRED) == 0) &&
+ ((evidence[0].StatusBits & CSSM_CERT_STATUS_NOT_VALID_YET) == 0)) {
+ status = errSecSuccess;
+ }
+ else {
+ status = errSecCertificateCannotOperate;
+ }
+
+cleanup:
+ if(props) CFRelease(props);
+ if(chain) CFRelease(chain);
+ if(anchors) CFRelease(anchors);
+ if(keychains) CFRelease(keychains);
+ if(certs) CFRelease(certs);
+ if(trust) CFRelease(trust);
+
+ return status;
+}
+
+static OSStatus
+_FilterWithDate(CFTypeRef validOnDate, SecCertificateRef cert)
+{
+ if (!validOnDate || !cert) return errSecParam;
+
+ CFAbsoluteTime at, nb, na;
+ if (CFGetTypeID(validOnDate) == CFDateGetTypeID())
+ at = CFDateGetAbsoluteTime((CFDateRef)validOnDate);
+ else
+ at = CFAbsoluteTimeGetCurrent();
+
+ OSStatus status = errSecSuccess;
+ nb = SecCertificateNotValidBefore(cert);
+ na = SecCertificateNotValidAfter(cert);
+
+ if (nb == 0 || na == 0 || nb == na)
+ status = errSecCertificateCannotOperate;
+ else if (at < nb)
+ status = errSecCertificateNotValidYet;
+ else if (at > na)
+ status = errSecCertificateExpired;
+
+ return status;
+}
+
+static OSStatus
+_FilterWithTrust(Boolean trustedOnly, SecCertificateRef cert)
+{
+ if (!cert) return errSecParam;
+ if (!trustedOnly) return errSecSuccess;
+
+ CFArrayRef certArray = CFArrayCreate(NULL, (const void**)&cert, 1, &kCFTypeArrayCallBacks);
+ SecPolicyRef policy = SecPolicyCreateWithOID(kSecPolicyAppleX509Basic);
+ OSStatus status = (policy == NULL) ? errSecPolicyNotFound : errSecSuccess;
+
+ if (!status) {
+ SecTrustRef trust = NULL;
+ status = SecTrustCreateWithCertificates(certArray, policy, &trust);
+ if (!status) {
+ SecTrustResultType trustResult;
+ status = SecTrustEvaluate(trust, &trustResult);
+ if (!status) {
+ if (!(trustResult == kSecTrustResultProceed || trustResult == kSecTrustResultUnspecified)) {
+ status = (trustResult == kSecTrustResultDeny) ? errSecTrustSettingDeny : errSecNotTrusted;
+ }
+ }
+ CFRelease(trust);
+ }
+ CFRelease(policy);
+ }
+ if (certArray) {
+ CFRelease(certArray);
+ }
+
+ return status;
+}
+
+static SecKeychainItemRef
+CopyResolvedKeychainItem(CFTypeRef item)
+{
+ SecKeychainItemRef kcItem = NULL;
+ OSStatus status;
+ if (item) {
+ if (CFGetTypeID(item) == CFDataGetTypeID()) {
+ // persistent reference, resolve first
+ status = SecKeychainItemCopyFromPersistentReference((CFDataRef)item, &kcItem);
+ }
+ else {
+ // normal reference
+ kcItem = (SecKeychainItemRef) CFRetain(item);
+ }
+ if (kcItem) {
+ // ask for the item's class:
+ // will return an error if the item has been deleted
+ SecItemClass itemClass;
+ SecKeychainItemRef certRef = NULL;
+ if (CFGetTypeID(kcItem) == SecIdentityGetTypeID()) {
+ status = SecIdentityCopyCertificate((SecIdentityRef)kcItem, (SecCertificateRef *)&certRef);
+ }
+ status = SecKeychainItemCopyAttributesAndData((certRef) ? certRef : kcItem, NULL, &itemClass, NULL, NULL, NULL);
+ if (certRef) {
+ CFRelease(certRef);
+ }
+ if (status) {
+ CFRelease(kcItem);
+ kcItem = NULL;
+ }
+ }
+ }
+ return kcItem;
+}
+
+static OSStatus
+UpdateKeychainSearchAndCopyNext(SecItemParams *params, CFTypeRef *item)
+{
+ // This function refreshes the search parameters in the specific case where
+ // the caller is searching for kSecClassKey items but did not provide the
+ // kSecAttrKeyClass. In that case, params->assumedKeyClass will be set, and
+ // we must perform separate searches to obtain all results.
+
+ OSStatus status = errSecItemNotFound;
+ if (!params || !params->assumedKeyClass || !params->query || !item)
+ return status;
+
+ // Free the previous search reference and attribute list.
+ if (params->search)
+ CFRelease(params->search);
+ params->search = NULL;
+ _FreeAttrList(params->attrList);
+ params->attrList = NULL;
+
+ // Make a copy of the query dictionary so we can set the key class parameter.
+ CFMutableDictionaryRef dict = CFDictionaryCreateMutableCopy(NULL, 0, params->query);
+ CFRelease(params->query);
+ params->query = dict;
+ CFDictionarySetValue(dict, kSecAttrKeyClass, params->assumedKeyClass);
+
+ // Determine the current item class for this search, and the next assumed key class.
+ if (CFEqual(params->assumedKeyClass, kSecAttrKeyClassSymmetric)) {
+ params->itemClass = kSecSymmetricKeyItemClass;
+ params->assumedKeyClass = kSecAttrKeyClassPublic;
+ } else if (CFEqual(params->assumedKeyClass, kSecAttrKeyClassPublic)) {
+ params->itemClass = kSecPublicKeyItemClass;
+ params->assumedKeyClass = kSecAttrKeyClassPrivate;
+ } else {
+ params->itemClass = kSecPrivateKeyItemClass;
+ params->assumedKeyClass = NULL;
+ }
+
+ // Rebuild the attribute list for the new key class.
+ if (_CreateSecKeychainAttributeListFromDictionary(dict, params->itemClass, ¶ms->attrList) == errSecSuccess) {
+ // Create a new search reference for the new attribute list.
+ if (SecKeychainSearchCreateFromAttributes(params->searchList,
+ params->itemClass,
+ (params->attrList->count == 0) ? NULL : params->attrList,
+ (SecKeychainSearchRef*)¶ms->search) == errSecSuccess) {
+ // Return the first matching item from the new search.
+ // We won't come back here again until there are no more matching items for this search.
+ status = SecKeychainSearchCopyNext((SecKeychainSearchRef)params->search, (SecKeychainItemRef*)item);
+ }
+ }
+ return status;
+}
+
+
+static OSStatus
+SecItemSearchCopyNext(SecItemParams *params, CFTypeRef *item)
+{
+ // Generic "copy next match" function for SecKeychainSearchRef or SecIdentitySearchRef.
+ // Returns either a SecKeychainItemRef or a SecIdentityRef in the output parameter,
+ // depending on the type of search reference.
+
+ OSStatus status;
+ CFTypeRef search = (params) ? params->search : NULL;
+ CFTypeID typeID = (search) ? CFGetTypeID(search) : 0;
+ if (typeID == SecIdentitySearchGetTypeID()) {
+ status = SecIdentitySearchCopyNext((SecIdentitySearchRef)search, (SecIdentityRef*)item);
+ }
+ else if (typeID == SecKeychainSearchGetTypeID()) {
+ status = SecKeychainSearchCopyNext((SecKeychainSearchRef)search, (SecKeychainItemRef*)item);
+ // Check if we need to refresh the search for the next key class
+ while (status == errSecItemNotFound && params->assumedKeyClass != NULL)
+ status = UpdateKeychainSearchAndCopyNext(params, item);
+ }
+ else if (typeID == 0 && (params->useItems || params->itemList)) {
+ // No search available, but there is an item list available.
+ // Return the next candidate item from the caller's item list
+ CFArrayRef itemList = (params->useItems) ? params->useItems : params->itemList;
+ CFIndex count = CFArrayGetCount(itemList);
+ *item = (CFTypeRef) NULL;
+ if (params->itemListIndex < count) {
+ *item = (CFTypeRef)CFArrayGetValueAtIndex(itemList, params->itemListIndex++);
+ if (*item) {
+ // Potentially resolve persistent item references here, and
+ // verify the item reference we're about to hand back is still
+ // valid (it could have been deleted from the keychain while
+ // our query was holding onto the itemList).
+ *item = CopyResolvedKeychainItem(*item);
+ if (*item && (CFGetTypeID(*item) == SecIdentityGetTypeID())) {
+ // Persistent reference resolved to an identity, so return that type.
+ params->returnIdentity = true;
+ }
+ }
+ }
+ status = (*item) ? errSecSuccess : errSecItemNotFound;
+ }
+ else {
+ status = errSecItemNotFound;
+ }
+ return status;
+}
+
+static OSStatus
+FilterCandidateItem(CFTypeRef *item, SecItemParams *itemParams, SecIdentityRef *identity)
+{
+ if (!item || *item == NULL || !itemParams)
+ return errSecItemNotFound;
+
+ OSStatus status;
+ CFStringRef commonName = NULL;
+ SecIdentityRef foundIdentity = NULL;
+ if (CFGetTypeID(*item) == SecIdentityGetTypeID()) {
+ // we found a SecIdentityRef, rather than a SecKeychainItemRef;
+ // replace the found "item" with its associated certificate (which is the
+ // item we actually want for purposes of getting attributes, data, or a
+ // persistent data reference), and return the identity separately.
+ SecCertificateRef certificate;
+ status = SecIdentityCopyCertificate((SecIdentityRef) *item, &certificate);
+ if (itemParams->returnIdentity) {
+ foundIdentity = (SecIdentityRef) *item;
+ if (identity) {
+ *identity = foundIdentity;
+ }
+ }
+ else {
+ CFRelease(*item);
+ }
+ *item = (CFTypeRef)certificate;
+ }
+
+ CFDictionaryRef query = itemParams->query;
+
+ if (itemParams->itemClass == kSecCertificateItemClass) {
+ // perform string comparisons first
+ CFStringCompareFlags flags = _StringCompareFlagsFromQuery(query);
+ CFStringRef nameContains, nameStarts, nameEnds, nameExact;
+ if (!CFDictionaryGetValueIfPresent(query, kSecMatchSubjectContains, (const void **)&nameContains))
+ nameContains = NULL;
+ if (!CFDictionaryGetValueIfPresent(query, kSecMatchSubjectStartsWith, (const void **)&nameStarts))
+ nameStarts = NULL;
+ if (!CFDictionaryGetValueIfPresent(query, kSecMatchSubjectEndsWith, (const void **)&nameEnds))
+ nameEnds = NULL;
+ if (!CFDictionaryGetValueIfPresent(query, kSecMatchSubjectWholeString, (const void **)&nameExact))
+ nameExact = NULL;
+ if (nameContains || nameStarts || nameEnds || nameExact) {
+ status = SecCertificateCopyCommonName((SecCertificateRef)*item, &commonName);
+ if (status || !commonName) goto filterOut;
+ }
+ if (nameContains) {
+ CFRange range = CFStringFind(commonName, nameContains, flags);
+ if (range.length < 1)
+ goto filterOut;
+ // certificate item contains string; proceed to next check
+ }
+ if (nameStarts) {
+ CFRange range = CFStringFind(commonName, nameStarts, flags);
+ if (range.length < 1 || range.location > 1)
+ goto filterOut;
+ // certificate item starts with string; proceed to next check
+ }
+ if (nameEnds) {
+ CFRange range = CFStringFind(commonName, nameEnds, flags);
+ if (range.length < 1 || range.location != (CFStringGetLength(commonName) - CFStringGetLength(nameEnds)))
+ goto filterOut;
+ // certificate item ends with string; proceed to next check
+ }
+ if (nameExact) {
+ CFRange range = CFStringFind(commonName, nameExact, flags);
+ if (range.length < 1 || (CFStringGetLength(commonName) != CFStringGetLength(nameExact)))
+ goto filterOut;
+ // certificate item exactly matches string; proceed to next check
+ }
+ if (itemParams->returnIdentity) {
+ // if we already found and returned the identity, we can skip this
+ if (!foundIdentity) {
+ status = SecIdentityCreateWithCertificate(itemParams->searchList, (SecCertificateRef) *item, identity);
+ if (status) goto filterOut;
+ }
+ // certificate item is part of an identity; proceed to next check
+ }
+ if (itemParams->policy) {
+ status = _FilterWithPolicy(itemParams->policy, (CFDateRef)itemParams->validOnDate, (SecCertificateRef) *item);
+ if (status) goto filterOut;
+ // certificate item is valid for specified policy (and optionally specified date)
+ }
+ if (itemParams->validOnDate) {
+ status = _FilterWithDate(itemParams->validOnDate, (SecCertificateRef) *item);
+ if (status) goto filterOut;
+ // certificate item is valid for specified date
+ }
+ if (itemParams->trustedOnly) {
+ // if we are getting candidate items from a SecIdentitySearchCreateWithPolicy search,
+ // their trust has already been validated and we can skip this part.
+ if (!(foundIdentity && itemParams->returnIdentity && itemParams->policy)) {
+ status = _FilterWithTrust(itemParams->trustedOnly, (SecCertificateRef) *item);
+ if (status) goto filterOut;
+ }
+ // certificate item is trusted on this system
+ }
+ }
+ if (itemParams->itemList) {
+ Boolean foundMatch = FALSE;
+ CFIndex idx, count = CFArrayGetCount(itemParams->itemList);
+ for (idx=0; idx<count; idx++) {
+ CFTypeRef anItem = (CFTypeRef) CFArrayGetValueAtIndex(itemParams->itemList, idx);
+ SecKeychainItemRef realItem = NULL;
+ SecCertificateRef aCert = NULL;
+ if (anItem == NULL) {
+ continue;
+ }
+ if (CFDataGetTypeID() == CFGetTypeID(anItem) &&
+ errSecSuccess == SecKeychainItemCopyFromPersistentReference((CFDataRef)anItem, &realItem)) {
+ anItem = realItem;
+ }
+ if (SecIdentityGetTypeID() == CFGetTypeID(anItem) &&
+ errSecSuccess == SecIdentityCopyCertificate((SecIdentityRef)anItem, &aCert)) {
+ anItem = aCert;
+ }
+ if (CFEqual(anItem, (CFTypeRef) *item)) {
+ foundMatch = TRUE;
+ }
+ if (aCert) {
+ CFRelease(aCert);
+ }
+ if (realItem) {
+ CFRelease(realItem);
+ }
+ if (foundMatch) {
+ break;
+ }
+ }
+ if (!foundMatch) goto filterOut;
+ // item was found on provided list
+ }
+
+ if (foundIdentity && !identity) {
+ CFRelease(foundIdentity);
+ }
+ if (commonName) {
+ CFRelease(commonName);
+ }
+
+ // if we get here, consider the item a match
+ return errSecSuccess;
+
+filterOut:
+ if (commonName) {
+ CFRelease(commonName);
+ }
+ CFRelease(*item);
+ *item = NULL;
+ if (foundIdentity) {
+ CFRelease(foundIdentity);
+ if (identity) {
+ *identity = NULL;
+ }
+ }
+ return errSecItemNotFound;
+}
+
+static OSStatus
+AddItemResults(SecKeychainItemRef item,
+ SecIdentityRef identity,
+ SecItemParams *itemParams,
+ CFAllocatorRef allocator,
+ CFMutableArrayRef *items,
+ CFTypeRef *result)
+{
+ // Given a found item (which may also be an identity), this function adds
+ // the requested result types (specified in itemParams) to the appropriate
+ // container as follows:
+ //
+ // 1. If there is only one result type (numResultTypes == 1) and only one
+ // match requested (maxMatches == 1), set *result directly.
+ //
+ // 2. If there are multiple result types (numResultTypes > 1), and only one
+ // match requested (maxMatches == 1), add each result type to itemDict
+ // and set itemDict as the value of *result.
+ //
+ // 3. If there is only one result type (numResultTypes == 1) and multiple
+ // possible matches (maxMatches > 1), add the result type to *items
+ // and set *items as the value of *result.
+ //
+ // 4. If there are multiple result types (numResultTypes > 1) and multiple
+ // possible matches (maxMatches > 1), add each result type to itemDict,
+ // add itemDict to *items, and set *items as the value of *result.
+ //
+ // Note that we allocate *items if needed.
+
+ if (!item || !itemParams || !result)
+ return errSecParam;
+
+ if (itemParams->maxMatches > 1) {
+ // if we can return more than one item, we must have an array
+ if (!items)
+ return errSecParam;
+ else if (*items == NULL)
+ *items = CFArrayCreateMutable(allocator, 0, &kCFTypeArrayCallBacks);
+ }
+
+ OSStatus tmpStatus, status = errSecSuccess;
+ CFMutableArrayRef itemArray = (items) ? *items : NULL;
+ CFMutableDictionaryRef itemDict = NULL;
+ if (itemParams->numResultTypes > 1) {
+ // if we're returning more than one result type, each item we return must be a dictionary
+ itemDict = CFDictionaryCreateMutable(allocator, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+ }
+
+ if (itemParams->returningRef) {
+ const void* itemRef = (identity) ? (const void*)identity : (const void*)item;
+ if (itemDict) {
+ CFDictionaryAddValue(itemDict, kSecValueRef, itemRef);
+ }
+ else if (itemArray) {
+ CFArrayAppendValue(itemArray, itemRef);
+ }
+ else {
+ *result = CFRetain((CFTypeRef)itemRef);
+ }
+ }
+
+ if (itemParams->returningPersistentRef) {
+ CFDataRef persistentRef;
+ SecKeychainItemRef tmpItem = item;
+ if (itemParams->identityRef) {
+ tmpItem = (SecKeychainItemRef)itemParams->identityRef;
+ }
+ tmpStatus = SecKeychainItemCreatePersistentReference(tmpItem, &persistentRef);
+ if (tmpStatus == errSecSuccess) {
+ if (itemDict) {
+ CFDictionaryAddValue(itemDict, kSecValuePersistentRef, persistentRef);
+ }
+ else if (itemArray) {
+ CFArrayAppendValue(itemArray, persistentRef);
+ }
+ else {
+ *result = CFRetain(persistentRef);
+ }
+ CFRelease(persistentRef);
+ }
+ else if (status == errSecSuccess) {
+ status = tmpStatus;
+ }
+ }
+
+ if (itemParams->returningData) {
+ UInt32 length;
+ void *data;
+ tmpStatus = SecKeychainItemCopyContent(item, NULL, NULL, &length, &data);
+ if (tmpStatus == errSecSuccess) {
+ CFDataRef dataRef = CFDataCreate(allocator, (UInt8 *)data, length);
+ if (itemDict) {
+ CFDictionaryAddValue(itemDict, kSecValueData, dataRef);
+ }
+ else if (itemArray) {
+ CFArrayAppendValue(itemArray, dataRef);
+ }
+ else {
+ *result = CFRetain(dataRef);
+ }
+ CFRelease(dataRef);
+ (void) SecKeychainItemFreeContent(NULL, data);
+ }
+ else if (status == errSecSuccess) {
+ status = tmpStatus;
+ }
+ }
+
+ if (itemParams->returningAttributes) {
+ CFDictionaryRef attrsDict = NULL;
+ SecItemClass itemClass;
+ // since we have an item, allow its actual class to override the query-specified item class
+ tmpStatus = SecKeychainItemCopyAttributesAndData(item, NULL, &itemClass, NULL, NULL, NULL);
+ if (tmpStatus) {
+ itemClass = itemParams->itemClass;
+ }
+ tmpStatus = _CreateAttributesDictionaryFromItem(allocator, itemClass, item, &attrsDict);
+ if (attrsDict) {
+ if (itemDict) {
+ // add all keys and values from attrsDict to the item dictionary
+ CFDictionaryApplyFunction(attrsDict, _AddDictValueToOtherDict, &itemDict);
+ }
+ else if (itemArray) {
+ CFArrayAppendValue(itemArray, attrsDict);
+ }
+ else {
+ *result = CFRetain(attrsDict);
+ }
+ CFRelease(attrsDict);
+ }
+ if (tmpStatus && (status == errSecSuccess)) {
+ status = tmpStatus;
+ }
+ }
+
+ if (itemDict) {
+ if (itemArray) {
+ CFArrayAppendValue(itemArray, itemDict);
+ CFRelease(itemDict);
+ *result = itemArray;
+ }
+ else {
+ *result = itemDict;
+ }
+ }
+ else if (itemArray) {
+ *result = itemArray;
+ }
+
+ return status;
+}
+
+CFDataRef _SecItemGetPersistentReference(CFTypeRef raw_item)
+{
+ try {
+ Item item = ItemImpl::required((SecKeychainItemRef)raw_item);
+ return item->getPersistentRef();
+ } catch(...) {
+ return NULL;
+ }
+}
+
+/******************************************************************************/
+#pragma mark SecItem API functions
+/******************************************************************************/
+
+//
+// Approximate result of using iOS sec's copyNumber, 0 return could be zero, or error.
+//
+static SInt32 readNumber(CFTypeRef obj) {
+ CFTypeID tid = CFGetTypeID(obj);
+ SInt32 v = 0;
+ if (tid == CFNumberGetTypeID()) {
+ CFNumberGetValue((CFNumberRef)obj, kCFNumberSInt32Type, &v);
+ return v;
+ } else if (tid == CFBooleanGetTypeID()) {
+ v = CFBooleanGetValue((CFBooleanRef)obj);
+ return v;
+ } else if (tid == CFStringGetTypeID()) {
+ v = CFStringGetIntValue((CFStringRef)obj);
+ CFStringRef t = CFStringCreateWithFormat(0, 0, CFSTR("%ld"), (long)v);
+ /* If a string converted to an int isn't equal to the int printed as
+ a string, return a CFStringRef instead. */
+ if (!CFEqual(t, obj)) {
+ CFRelease(t);
+ return 0;
+ }
+ CFRelease(t);
+ return v;
+ } else
+ return NULL;
+}
+
+//
+// Function to ensure the syncable keychain is unlocked.
+// Currently, this means unlocking the login keychain,
+// which will also unlock the keybag as a side effect.
+//
+static OSStatus SecItemUnlockSynchronizableKeychain()
+{
+ SecKeychainRef keychain = NULL;
+ OSStatus status = SecKeychainCopyLogin(&keychain);
+ if (!status) {
+ status = SecKeychainUnlock(keychain, 0, NULL, false);
+ }
+ CFReleaseSafe(keychain);
+ return status;
+}
+
+//
+// Function to check whether the kSecAttrSynchronizable flag is set in the query.
+//
+static Boolean SecItemSynchronizable(CFDictionaryRef query)
+{
+ CFTypeRef value = CFDictionaryGetValue(query, kSecAttrSynchronizable);
+ Boolean result = (value && readNumber(value));
+
+ return result;
+}
+
+//
+// Function to check whether the kSecAttrSynchronizable flag is set in the query,
+// and has the special value of kSecAttrSynchronizableAny.
+//
+static Boolean SecItemSynchronizableAny(CFDictionaryRef query)
+{
+ CFTypeRef value = CFDictionaryGetValue(query, kSecAttrSynchronizable);
+ if (value) {
+ return (CFGetTypeID(value) == CFStringGetTypeID() &&
+ CFEqual(value, kSecAttrSynchronizableAny));
+ }
+ return false;
+}
+
+//
+// Function to check whether the kSecAttrSynchronizable attribute is being updated.
+//
+static Boolean SecItemHasSynchronizableUpdate(Boolean synchronizable, CFDictionaryRef changes)
+{
+ CFTypeRef newValue = CFDictionaryGetValue(changes, kSecAttrSynchronizable);
+ if (!newValue)
+ return false;
+
+ Boolean new_sync = readNumber(newValue);
+ Boolean old_sync = synchronizable;
+
+ return (old_sync != new_sync);
+}
+
+//
+// Returns true if keychain syncing is globally enabled.
+//
+static Boolean SecItemSyncEnabled()
+{
+ static dispatch_once_t onceToken;
+ static Boolean syncEnabled = true;
+
+ //sudo defaults write /Library/Preferences/com.apple.security SecItemSynchronizable -bool YES
+ dispatch_once(&onceToken, ^{
+ CFTypeRef sync = (CFNumberRef)CFPreferencesCopyValue(CFSTR("SecItemSynchronizable"), CFSTR("com.apple.security"), kCFPreferencesAnyUser, kCFPreferencesCurrentHost);
+
+ if (sync && CFGetTypeID(sync) == CFBooleanGetTypeID()) {
+ syncEnabled = CFBooleanGetValue((CFBooleanRef)sync);
+ CFRelease(sync);
+ }
+ });
+
+ return syncEnabled;
+}
+
+//
+// Function to check whether a synchronizable persistent reference was provided.
+//
+static Boolean SecItemHasSynchronizablePersistentReference(CFDictionaryRef query)
+{
+ CFTypeRef value = CFDictionaryGetValue(query, kSecValuePersistentRef);
+ if (value) {
+ /* Synchronizable persistent ref consists of the sqlite rowid and 4-byte class value */
+ const CFIndex kSynchronizablePersistentRefLength = sizeof(int64_t) + 4;
+ return (CFGetTypeID(value) == CFDataGetTypeID() &&
+ CFDataGetLength((CFDataRef)value) == kSynchronizablePersistentRefLength);
+ }
+ return false;
+}
+
+//
+// Function to apply changes to a mutable dictionary.
+// (CFDictionaryApplierFunction, called by CFDictionaryApplyFunction)
+//
+static void SecItemApplyChanges(const void *key, const void *value, void *context)
+{
+ CFMutableDictionaryRef dict = (CFMutableDictionaryRef) context;
+ if (!dict) return;
+
+ CFDictionarySetValue(dict, key, value);
+}
+
+//
+// Function to change matching items from non-syncable to syncable
+// (if toSyncable is true), otherwise from syncable to non-syncable.
+// This currently moves items between keychain containers.
+//
+static OSStatus SecItemChangeSynchronizability(CFDictionaryRef query, CFDictionaryRef changes, Boolean toSyncable)
+{
+ // Note: the input query dictionary is a mutable copy of the query originally
+ // provided by the caller as the first parameter to SecItemUpdate. It may not
+ // specify returning attributes or data, but we will need both to make a copy.
+ //
+ CFDictionaryRemoveValue((CFMutableDictionaryRef)query, kSecReturnRef);
+ CFDictionaryRemoveValue((CFMutableDictionaryRef)query, kSecReturnPersistentRef);
+ CFDictionaryRemoveValue((CFMutableDictionaryRef)query, kSecReturnData);
+ CFDictionarySetValue((CFMutableDictionaryRef)query, kSecReturnAttributes, kCFBooleanTrue);
+ if (NULL == CFDictionaryGetValue(changes, kSecValueData))
+ CFDictionarySetValue((CFMutableDictionaryRef)query, kSecReturnData, kCFBooleanTrue);
+
+ CFTypeRef result;
+ OSStatus status;
+ if (toSyncable)
+ status = SecItemCopyMatching_osx(query, &result);
+ else
+ status = SecItemCopyMatching_ios(query, &result);
+
+ if (status)
+ return status;
+ if (!result)
+ return errSecItemNotFound;
+
+ CFMutableArrayRef items;
+ if (CFGetTypeID(result) != CFArrayGetTypeID()) {
+ items = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
+ CFArrayAppendValue(items, result);
+ CFRelease(result);
+ }
+ else {
+ items = (CFMutableArrayRef)result;
+ }
+
+ CFIndex idx, count = (items) ? CFArrayGetCount(items) : 0;
+ int priority = LOG_DEBUG;
+ OSStatus err = 0;
+ for (idx = 0; idx < count; idx++) {
+ CFDictionaryRef dict = (CFDictionaryRef) CFArrayGetValueAtIndex(items, idx);
+ CFMutableDictionaryRef item = (CFMutableDictionaryRef)
+ SecItemCopyTranslatedAttributes(dict,
+ CFDictionaryGetValue(query, kSecClass),
+ (toSyncable) ? true : false /*iOSOut*/,
+ true /*pruneMatch*/,
+ true /*pruneSync*/,
+ true /*pruneReturn*/,
+ false /*pruneData*/,
+ (toSyncable) ? true : false /*pruneAccess*/);
+ // hold onto the query before applying changes, in case the item already exists.
+ // note that we cannot include the creation or modification dates from our
+ // found item in this query, as they may not match the item in the other keychain.
+ CFMutableDictionaryRef itemQuery = CFDictionaryCreateMutableCopy(NULL, 0, item);
+ CFDictionaryRemoveValue(itemQuery, kSecAttrCreationDate);
+ CFDictionaryRemoveValue(itemQuery, kSecAttrModificationDate);
+ // apply changes to the item dictionary that we will pass to SecItemAdd
+ CFDictionaryApplyFunction(changes, SecItemApplyChanges, item);
+ if (toSyncable) {
+ CFDictionarySetValue(item, kSecAttrSynchronizable, kCFBooleanTrue);
+ status = SecItemAdd_ios(item, NULL);
+ secitemlog(priority, "ChangeSync: SecItemAdd_ios=%d", status);
+ if (errSecDuplicateItem == status) {
+ // find and apply changes to the existing syncable item.
+ CFDictionarySetValue(itemQuery, kSecAttrSynchronizable, kCFBooleanTrue);
+ status = SecItemUpdate_ios(itemQuery, changes);
+ secitemlog(priority, "ChangeSync: SecItemUpdate_ios=%d", status);
+ }
+ if (errSecSuccess == status) {
+ CFDictionarySetValue(itemQuery, kSecAttrSynchronizable, kCFBooleanFalse);
+ status = SecItemDelete_osx(itemQuery);
+ secitemlog(priority, "ChangeSync: SecItemDelete_osx=%d", status);
+ }
+ }
+ else {
+ CFDictionarySetValue(item, kSecAttrSynchronizable, kCFBooleanFalse);
+ status = SecItemAdd_osx(item, NULL);
+ secitemlog(priority, "ChangeSync: SecItemAdd_osx=%d", status);
+ if (errSecDuplicateItem == status) {
+ // find and apply changes to the existing non-syncable item.
+ CFDictionarySetValue(itemQuery, kSecAttrSynchronizable, kCFBooleanFalse);
+ status = SecItemUpdate_osx(itemQuery, changes);
+ secitemlog(priority, "ChangeSync: SecItemUpdate_osx=%d", status);
+ }
+ if (errSecSuccess == status) {
+ CFDictionarySetValue(itemQuery, kSecAttrSynchronizable, kCFBooleanTrue);
+ status = SecItemDelete_ios(itemQuery);
+ secitemlog(priority, "ChangeSync: SecItemDelete_ios=%d", status);
+ }
+ }
+ CFReleaseSafe(item);
+ CFReleaseSafe(itemQuery);
+ if (status)
+ err = status;
+ }
+ CFReleaseSafe(items);
+
+ return err;
+}
+
+
+extern "C" {
+
+CFTypeRef
+SecItemCreateFromAttributeDictionary(CFDictionaryRef refAttributes) {
+ CFTypeRef ref = NULL;
+ CFStringRef key_class_string = (CFStringRef)CFDictionaryGetValue(refAttributes, kSecClass);
+ SecItemClass key_class;
+ bool key_class_found = false;
+
+ if (CFEqual(key_class_string, kSecClassGenericPassword)) {
+ key_class = kSecGenericPasswordItemClass;
+ key_class_found = true;
+ }
+ if (CFEqual(key_class_string, kSecClassInternetPassword)) {
+ key_class = kSecInternetPasswordItemClass;
+ key_class_found = true;
+ }
+
+ if (key_class_found) {
+ // we carry v_Data around here so the *_ios calls can find it and locate
+ // their own data. Putting things in the attribute list doesn't help as
+ // the osx keychainitem and item calls bail when they don't see a keychain
+ // object. If we need to make them work we either have to bridge them, or
+ // find a way to craft a workable keychain object. #if'ed code left below
+ // in case we need to go down that path.
+
+ struct SecKeychainAttributeList *attrs = (struct SecKeychainAttributeList *)malloc(sizeof(struct SecKeychainAttributeList) + sizeof(struct SecKeychainAttribute) * 0);
+ attrs->attr = (struct SecKeychainAttribute *)(attrs + 1);
+ attrs->count = 0;
+ CFTypeRef v;
+#if 0
+ // The C++ string objects need to last at least as long as the attr struct.
+ string account;
+
+ v = CFDictionaryGetValue(refAttributes, CFSTR("mdat"));
+ if (v) {
+ attrs->attr[attrs->count].tag = kSecModDateItemAttr;
+ // XXX need to convert to YYYYMMDDhhmmSSZ
+ attrs->attr[attrs->count].data = (void*)"19690223140232Z";
+ attrs->attr[attrs->count].length = strlen((char*)(attrs->attr[attrs->count].data));
+ attrs->count++;
+ }
+ v = CFDictionaryGetValue(refAttributes, CFSTR("cdat"));
+ if (v) {
+ attrs->attr[attrs->count].tag = kSecCreationDateItemAttr;
+ // XXX need to convert to YYYYMMDDhhmmSSZ
+ attrs->attr[attrs->count].data = (void*)"19690223140232Z";
+ attrs->attr[attrs->count].length = strlen((char*)(attrs->attr[attrs->count].data));
+ attrs->count++;
+ }
+
+ v = CFDictionaryGetValue(refAttributes, CFSTR("acct"));
+ if (v) {
+ attrs->attr[attrs->count].tag = kSecAccountItemAttr;
+ account = cfString((CFStringRef)v);
+ attrs->attr[attrs->count].data = (void*)(account.c_str());
+ attrs->attr[attrs->count].length = account.length();
+ attrs->count++;
+ }
+
+ // class isn't treated as an attribute by the creation API
+
+ v = CFDictionaryGetValue(refAttributes, CFSTR("svce"));
+ if (v) {
+ attrs->attr[attrs->count].tag = kSecServiceItemAttr;
+ account = cfString((CFStringRef)v);
+ attrs->attr[attrs->count].data = (void*)(account.c_str());
+ attrs->attr[attrs->count].length = account.length();
+ attrs->count++;
+ }
+
+ v = CFDictionaryGetValue(refAttributes, CFSTR("acct"));
+ if (v) {
+ attrs->attr[attrs->count].tag = kSecLabelItemAttr;
+ account = cfString((CFStringRef)v);
+ attrs->attr[attrs->count].data = (void*)(account.c_str());
+ attrs->attr[attrs->count].length = account.length();
+ attrs->count++;
+ }
+#endif
+ Item item = Item(key_class, attrs, 0, "");
+ ItemImpl *real_item = item.get();
+ v = CFDictionaryGetValue(refAttributes, kSecValuePersistentRef);
+ if (v) {
+ real_item->setPersistentRef((CFDataRef)v);
+ }
+ ref = real_item->handle();
+ } else {
+ // keys, certs, identities are not currently sync'able.
+ ref = NULL;
+ }
+ return ref;
+}
+
+/*
+ * SecItemValidateAppleApplicationGroupAccess determines if the caller
+ * is a member of the specified application group, and is signed by Apple.
+ */
+OSStatus
+SecItemValidateAppleApplicationGroupAccess(CFStringRef group)
+{
+ SecTrustedApplicationRef app = NULL;
+ SecRequirementRef requirement = NULL;
+ SecCodeRef code = NULL;
+ OSStatus status = errSecParam;
+
+ if (group) {
+ CFIndex length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(group), kCFStringEncodingUTF8) + 1;
+ char* buffer = (char*) malloc(length);
+ if (buffer) {
+ if (CFStringGetCString(group, buffer, length, kCFStringEncodingUTF8)) {
+ status = SecTrustedApplicationCreateApplicationGroup(buffer, NULL, &app);
+ }
+ free(buffer);
+ } else {
+ status = errSecMemoryError;
+ }
+ }
+ if (!status) {
+ status = SecTrustedApplicationCopyRequirement(app, &requirement);
+ }
+ if (!status) {
+ status = SecCodeCopySelf(kSecCSDefaultFlags, &code);
+ }
+ if (!status) {
+ status = SecCodeCheckValidity(code, kSecCSDefaultFlags, requirement);
+ }
+
+ CFReleaseSafe(code);
+ CFReleaseSafe(requirement);
+ CFReleaseSafe(app);
+ return status;
+}
+
+/*
+ * SecItemCopyTranslatedAttributes accepts a user-provided attribute dictionary
+ * and attempts to return a sanitized copy for passing to the underlying
+ * platform-specific implementation code.
+ *
+ * If iOSOut is true, one or more translations may apply:
+ * - SecKeychain refs are removed, since there aren't multiple keychains
+ * - SecPolicy refs are removed, since they can't be externalized
+ * - SecAccess refs are removed, and potentially translated to entitlements
+ *
+ * If pruneMatch is true, kSecMatch* attributes are removed; this avoids
+ * parameter errors due to strict input checks in secd, which only permits
+ * these constants for calls to SecItemCopyMatching.
+ *
+ * If pruneSync is true, the kSecAttrSynchronizable attribute is removed.
+ * This permits a query to be reused for non-synchronizable items, or to
+ * resolve a search based on a persistent item reference for iOS.
+ *
+ * If pruneReturn is true, kSecReturn* attributes are removed; this avoids
+ * parameter errors due to strict input checks in secd, which do not permit
+ * these constants for calls to SecItemUpdate.
+ */
+CFDictionaryRef
+SecItemCopyTranslatedAttributes(CFDictionaryRef inOSXDict, CFTypeRef itemClass,
+ bool iOSOut, bool pruneMatch, bool pruneSync, bool pruneReturn, bool pruneData, bool pruneAccess)
+{
+ CFMutableDictionaryRef result = CFDictionaryCreateMutableCopy(NULL, 0, inOSXDict);
+ if (result == NULL) {
+ return result;
+ }
+
+ if (pruneSync) {
+ CFDictionaryRemoveValue(result, kSecAttrSynchronizable);
+ }
+
+ if (pruneMatch) {
+ /* Match constants are only supported on iOS for SecItemCopyMatching,
+ * and will generate an error if passed to other SecItem API functions;
+ * on OS X, they're just ignored if not applicable for the context.
+ */
+ CFDictionaryRemoveValue(result, kSecMatchPolicy);
+ CFDictionaryRemoveValue(result, kSecMatchItemList);
+ CFDictionaryRemoveValue(result, kSecMatchSearchList);
+ CFDictionaryRemoveValue(result, kSecMatchIssuers);
+ CFDictionaryRemoveValue(result, kSecMatchEmailAddressIfPresent);
+ CFDictionaryRemoveValue(result, kSecMatchSubjectContains);
+ CFDictionaryRemoveValue(result, kSecMatchCaseInsensitive);
+ CFDictionaryRemoveValue(result, kSecMatchTrustedOnly);
+ CFDictionaryRemoveValue(result, kSecMatchValidOnDate);
+ CFDictionaryRemoveValue(result, kSecMatchLimit);
+ CFDictionaryRemoveValue(result, kSecMatchLimitOne);
+ CFDictionaryRemoveValue(result, kSecMatchLimitAll);
+ }
+
+ if (pruneReturn) {
+ /* Return constants are not supported on iOS for SecItemUpdate,
+ * where they will generate an error; on OS X, they're just ignored
+ * if not applicable for the context.
+ */
+ CFDictionaryRemoveValue(result, kSecReturnData);
+ CFDictionaryRemoveValue(result, kSecReturnAttributes);
+ CFDictionaryRemoveValue(result, kSecReturnRef);
+ CFDictionaryRemoveValue(result, kSecReturnPersistentRef);
+ }
+
+ if (pruneData) {
+ /* Searching on data is not supported. */
+ CFDictionaryRemoveValue(result, kSecValueData);
+ }
+
+ if (pruneAccess) {
+ /* Searching on access lists is not supported */
+ CFDictionaryRemoveValue(result, kSecAttrAccess);
+ }
+
+ if (iOSOut) {
+ /* Remove kSecMatchSearchList (value is array of SecKeychainRef);
+ * cannot specify a keychain search list on iOS
+ */
+ CFDictionaryRemoveValue(result, kSecMatchSearchList);
+
+ /* Remove kSecUseKeychain (value is a SecKeychainRef);
+ * cannot specify a keychain on iOS
+ */
+ CFDictionaryRemoveValue(result, kSecUseKeychain);
+
+ /* Remove kSecMatchPolicy (value is a SecPolicyRef);
+ * TODO: need a way to externalize and restore a policy instance
+ */
+ CFDictionaryRemoveValue(result, kSecMatchPolicy);
+
+ /* Potentially translate kSecAttrAccess (value is a SecAccessRef),
+ * unless kSecAttrAccessGroup has already been specified.
+ */
+ SecAccessRef access = (SecAccessRef) CFDictionaryGetValue(result, kSecAttrAccess);
+ CFStringRef accessGroup = (CFStringRef) CFDictionaryGetValue(result, kSecAttrAccessGroup);
+ if (access != NULL && accessGroup == NULL) {
+ /* Translate "InternetAccounts" application group to an access group */
+ if (errSecSuccess == SecItemValidateAppleApplicationGroupAccess(CFSTR("InternetAccounts"))) {
+ /* The caller is a valid member of the application group. */
+ CFStringRef groupName = CFSTR("appleaccount");
+ CFTypeRef value = CFDictionaryGetValue(result, kSecAttrAuthenticationType);
+ if (value && CFEqual(value, kSecAttrAuthenticationTypeHTMLForm)) {
+ groupName = CFSTR("com.apple.cfnetwork");
+ }
+ CFDictionarySetValue(result, kSecAttrAccessGroup, groupName);
+ }
+ }
+ CFDictionaryRemoveValue(result, kSecAttrAccess);
+
+ /* If item is specified by direct reference, and this is an iOS search,
+ * replace it with a persistent reference.
+ */
+ CFTypeRef directRef = CFDictionaryGetValue(result, kSecValueRef);
+ if (directRef) {
+ CFDataRef persistentRef = _SecItemGetPersistentReference(directRef);
+ if (persistentRef) {
+ CFDictionarySetValue(result, kSecValuePersistentRef, persistentRef);
+ }
+ CFDictionaryRemoveValue(result, kSecValueRef);
+ }
+
+ /* If item is specified by persistent reference, and this is an iOS search,
+ * remove the synchronizable attribute as it will be rejected by secd.
+ */
+ CFTypeRef persistentRef = CFDictionaryGetValue(result, kSecValuePersistentRef);
+ if (persistentRef) {
+ CFDictionaryRemoveValue(result, kSecAttrSynchronizable);
+ }
+
+ /* Remove kSecAttrModificationDate; this should never be used as criteria
+ * for a search, or to add/modify an item. (If we are cloning an item
+ * and want to keep its modification date, we don't call this function.)
+ * It turns out that some clients are using the full attributes dictionary
+ * returned by SecItemCopyMatching as a query to find the same item later,
+ * which won't work once the item is updated.
+ */
+ CFDictionaryRemoveValue(result, kSecAttrModificationDate);
+ }
+ else {
+ /* iOS doesn't add the class attribute, so we must do it here. */
+ if (itemClass)
+ CFDictionarySetValue(result, kSecClass, itemClass);
+
+ /* Remove attributes which are not part of the OS X database schema. */
+ CFDictionaryRemoveValue(result, kSecAttrAccessible);
+ CFDictionaryRemoveValue(result, kSecAttrAccessGroup);
+ CFDictionaryRemoveValue(result, kSecAttrSynchronizable);
+ CFDictionaryRemoveValue(result, kSecAttrTombstone);
+ }
+
+ return result;
+}
+
+/*
+ * SecItemCopyMergedResults takes two input objects, which may be containers,
+ * and returns a retained object which merges the results. Merging depends on the
+ * result type. If each result is valid and is not an array, then only one match was
+ * requested; in that case, the syncable (ios) match is preferred.
+ *
+ * FIXME: There are some edge cases still to deal with; e.g. if the OSX search specified a
+ * particular keychain to search, we do not want to merge in any IOS results. Also, may need
+ * to filter out duplicates if two items differ only in the sync attribute.
+ */
+CFTypeRef
+SecItemCopyMergedResults(CFDictionaryRef query, CFTypeRef result_osx, CFTypeRef result_ios)
+{
+ CFTypeID id_osx = (result_osx) ? CFGetTypeID(result_osx) : 0;
+ CFTypeID id_ios = (result_ios) ? CFGetTypeID(result_ios) : 0;
+ CFTypeID id_array = CFArrayGetTypeID();
+ if ((id_osx == id_array) && (id_ios == id_array)) {
+ // Fold the arrays into one.
+ CFMutableArrayRef results = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
+ CFArrayAppendArray(results, (CFArrayRef)result_ios, CFRangeMake(0, CFArrayGetCount((CFArrayRef)result_ios)));
+ CFArrayAppendArray(results, (CFArrayRef)result_osx, CFRangeMake(0, CFArrayGetCount((CFArrayRef)result_osx)));
+ return results;
+ }
+ // Result type is not an array, so only one match can be returned.
+ return (id_ios) ? CFRetain(result_ios) : CFRetain(result_osx);
+}
+
+} /* extern "C" */
+
+
+OSStatus
+SecItemCopyMatching(CFDictionaryRef query, CFTypeRef *result)
+{
+ secitemlog(LOG_NOTICE, "SecItemCopyMatching");
+ if (!query) {
+ return errSecParam;
+ }
+ secitemshow(query, "SecItemCopyMatching query:");
+
+ OSStatus status_osx = errSecItemNotFound, status_ios = errSecItemNotFound;
+ CFTypeRef result_osx = NULL, result_ios = NULL;
+ Boolean sync_enabled = SecItemSyncEnabled();
+ Boolean search_ios = SecItemSynchronizable(query);
+ Boolean merge_search = SecItemSynchronizableAny(query);
+ Boolean persistref_ios = SecItemHasSynchronizablePersistentReference(query);
+
+ if (sync_enabled && (merge_search || persistref_ios || search_ios)) {
+ CFDictionaryRef attrs_ios = SecItemCopyTranslatedAttributes(query,
+ CFDictionaryGetValue(query, kSecClass), true, false, false, false, true, true);
+ if (!attrs_ios) {
+ status_ios = errSecParam;
+ }
+ else {
+ SecItemUnlockSynchronizableKeychain();
+ status_ios = SecItemCopyMatching_ios(attrs_ios, &result_ios);
+ CFRelease(attrs_ios);
+ }
+ secitemlog(LOG_NOTICE, "SecItemCopyMatching_ios result: %d", status_ios);
+ if (!merge_search || persistref_ios) {
+ AssignOrReleaseResult(result_ios, result);
+ return status_ios; // no need to search non-syncable keychains
+ }
+ }
+
+ CFDictionaryRef attrs_osx = SecItemCopyTranslatedAttributes(query,
+ CFDictionaryGetValue(query, kSecClass), false, false, true, false, true, true);
+ if (!attrs_osx) {
+ status_osx = errSecParam;
+ }
+ else {
+ status_osx = SecItemCopyMatching_osx(attrs_osx, &result_osx);
+ CFRelease(attrs_osx);
+ }
+ secitemlog(LOG_NOTICE, "SecItemCopyMatching_osx result: %d", status_osx);
+
+ // If one of the searches failed to occur or produce results, we can eliminate it
+ if (result_ios == NULL) {
+ AssignOrReleaseResult(result_osx, result);
+ return status_osx; // we can only have non-syncable results
+ }
+ if (result_osx == NULL) {
+ AssignOrReleaseResult(result_ios, result);
+ return status_ios; // we can only have syncable results
+ }
+
+ // If we get here, need to merge results
+ CFTypeRef result_merged = SecItemCopyMergedResults(query, result_osx, result_ios);
+ CFReleaseSafe(result_osx);
+ CFReleaseSafe(result_ios);
+ AssignOrReleaseResult(result_merged, result);
+
+ if (status_osx == status_ios) {
+ return status_osx; // both searches produced the same result
+ }
+ else if (!status_osx || !status_ios) {
+ return errSecSuccess; // one of the searches succeeded
+ }
+ else if (status_osx == errSecItemNotFound) {
+ return status_ios; // this failure was more interesting
+ }
+ return status_osx;
+}
+
+OSStatus
+SecItemAdd(CFDictionaryRef attributes, CFTypeRef *result)
+{
+ secitemlog(LOG_NOTICE, "SecItemAdd");
+ if (!attributes) {
+ return errSecParam;
+ }
+ else if (result) {
+ *result = NULL;
+ }
+ secitemshow(attributes, "SecItemAdd attrs:");
+
+ OSStatus status_osx, status_ios;
+ CFTypeRef result_osx = NULL, result_ios = NULL;
+ Boolean sync_enabled = SecItemSyncEnabled();
+ Boolean add_ios = SecItemSynchronizable(attributes);
+
+ if (sync_enabled && add_ios) {
+ CFDictionaryRef attrs_ios = SecItemCopyTranslatedAttributes(attributes,
+ NULL, true, true, false, false, false, false);
+ if (!attrs_ios) {
+ status_ios = errSecParam;
+ }
+ else {
+ SecItemUnlockSynchronizableKeychain();
+ status_ios = SecItemAdd_ios(attrs_ios, &result_ios);
+ CFRelease(attrs_ios);
+ }
+ secitemlog(LOG_NOTICE, "SecItemAdd_ios result: %d", status_ios);
+ if (result)
+ *result = result_ios;
+ else
+ CFReleaseSafe(result_ios);
+ return status_ios;
+ }
+
+ CFDictionaryRef attrs_osx = SecItemCopyTranslatedAttributes(attributes,
+ NULL, false, false, true, false, false, false);
+ if (!attrs_osx) {
+ status_osx = errSecParam;
+ }
+ else {
+ status_osx = SecItemAdd_osx(attrs_osx, &result_osx);
+ CFRelease(attrs_osx);
+ }
+ secitemlog(LOG_NOTICE, "SecItemAdd_osx result: %d", status_osx);
+ if (result)
+ *result = result_osx;
+ else
+ CFReleaseSafe(result_osx);
+ return status_osx;
+}
+
+OSStatus
+SecItemUpdate(CFDictionaryRef query, CFDictionaryRef attributesToUpdate)
+{
+ secitemlog(LOG_NOTICE, "SecItemUpdate");
+ if (!query || !attributesToUpdate) {
+ return errSecParam;
+ }
+ secitemshow(query, "SecItemUpdate query:");
+ secitemshow(attributesToUpdate, "SecItemUpdate attrs:");
+
+ OSStatus status_osx = errSecItemNotFound, status_ios = errSecItemNotFound;
+ Boolean sync_enabled = SecItemSyncEnabled();
+ Boolean search_ios = SecItemSynchronizable(query);
+ Boolean merge_search = SecItemSynchronizableAny(query);
+ Boolean persistref_ios = SecItemHasSynchronizablePersistentReference(query);
+
+ if (sync_enabled && (merge_search || persistref_ios || search_ios)) {
+ CFDictionaryRef attrs_ios = SecItemCopyTranslatedAttributes(query,
+ CFDictionaryGetValue(query, kSecClass), true, true, false, true, true, true);
+ if (!attrs_ios) {
+ status_ios = errSecParam;
+ }
+ else {
+ SecItemUnlockSynchronizableKeychain();
+ if (SecItemHasSynchronizableUpdate(true, attributesToUpdate))
+ status_ios = SecItemChangeSynchronizability(attrs_ios, attributesToUpdate, false);
+ else
+ status_ios = SecItemUpdate_ios(attrs_ios, attributesToUpdate);
+ CFRelease(attrs_ios);
+ }
+ secitemlog(LOG_NOTICE, "SecItemUpdate_ios result: %d", status_ios);
+ if (!merge_search || persistref_ios)
+ return status_ios;
+ }
+
+ CFDictionaryRef attrs_osx = SecItemCopyTranslatedAttributes(query,
+ CFDictionaryGetValue(query, kSecClass), false, false, true, true, true, true);
+ if (!attrs_osx) {
+ status_osx = errSecParam;
+ }
+ else {
+ if (SecItemHasSynchronizableUpdate(false, attributesToUpdate))
+ status_osx = SecItemChangeSynchronizability(attrs_osx, attributesToUpdate, true);
+ else
+ status_osx = SecItemUpdate_osx(attrs_osx, attributesToUpdate);
+
+ CFRelease(attrs_osx);
+ }
+ secitemlog(LOG_NOTICE, "SecItemUpdate_osx result: %d", status_osx);
+ if (merge_search) {
+ // Harmonize the result of the update attempts.
+ if (status_osx == status_ios) {
+ // both updates produced the same result
+ return status_ios;
+ }
+ else if (!status_osx || !status_ios) {
+ // one of the updates succeeded, but the other failed
+ if (status_osx == errSecItemNotFound || status_ios == errSecItemNotFound)
+ return errSecSuccess; // item only found in one keychain
+ else
+ return (status_osx) ? status_osx : status_ios; // return the error
+ }
+ else if (status_osx == errSecItemNotFound) {
+ // both updates failed, status_ios failure is more interesting
+ // since the item was actually found
+ return status_ios;
+ }
+ }
+ return status_osx;
+}
+
+OSStatus
+SecItemDelete(CFDictionaryRef query)
+{
+ secitemlog(LOG_NOTICE, "SecItemDelete");
+ if (!query) {
+ return errSecParam;
+ }
+ secitemshow(query, "SecItemDelete query:");
+
+ OSStatus status_osx = errSecItemNotFound, status_ios = errSecItemNotFound;
+ Boolean sync_enabled = SecItemSyncEnabled();
+ Boolean search_ios = SecItemSynchronizable(query);
+ Boolean merge_search = SecItemSynchronizableAny(query);
+ Boolean persistref_ios = SecItemHasSynchronizablePersistentReference(query);
+
+ if (sync_enabled && (merge_search || persistref_ios || search_ios)) {
+ CFDictionaryRef attrs_ios = SecItemCopyTranslatedAttributes(query,
+ NULL, true, true, false, true, true, true);
+ if (!attrs_ios) {
+ status_ios = errSecParam;
+ }
+ else {
+ SecItemUnlockSynchronizableKeychain();
+ status_ios = SecItemDelete_ios(attrs_ios);
+ CFRelease(attrs_ios);
+ }
+ secitemlog(LOG_NOTICE, "SecItemDelete_ios result: %d", status_ios);
+ if (!merge_search || persistref_ios)
+ return status_ios;
+ }
+
+ CFDictionaryRef attrs_osx = SecItemCopyTranslatedAttributes(query,
+ NULL, false, false, true, true, true, true);
+ if (!attrs_osx) {
+ status_osx = errSecParam;
+ }
+ else {
+ status_osx = SecItemDelete_osx(attrs_osx);
+ CFRelease(attrs_osx);
+ }
+ secitemlog(LOG_NOTICE, "SecItemDelete_osx result: %d", status_osx);
+
+ if (merge_search) {
+ // Harmonize the result of the delete attempts.
+ if (status_osx == status_ios) {
+ // both deletes produced the same result
+ return status_ios;
+ }
+ else if (!status_osx || !status_ios) {
+ // one of the deletes succeeded, but the other failed
+ if (status_osx == errSecItemNotFound || status_ios == errSecItemNotFound)
+ return errSecSuccess; // item only found in one keychain
+ else
+ return (status_osx) ? status_osx : status_ios; // return the error
+ }
+ else if (status_osx == errSecItemNotFound) {
+ // both deletes failed, status_ios failure is more interesting
+ // since the item was actually found
+ return status_ios;
+ }
+ }
+ return status_osx;
+}
+
+OSStatus
+SecItemCopyMatching_osx(
+ CFDictionaryRef query,
+ CFTypeRef *result)
+{
+ if (!query || !result)
+ return errSecParam;
+ else
+ *result = NULL;
+
+ CFAllocatorRef allocator = CFGetAllocator(query);
+ CFIndex matchCount = 0;
+ CFMutableArrayRef itemArray = NULL;
+ SecKeychainItemRef item = NULL;
+ SecIdentityRef identity = NULL;
+ OSStatus tmpStatus, status = errSecSuccess;
+
+ // validate input query parameters and create the search reference
+ SecItemParams *itemParams = _CreateSecItemParamsFromDictionary(query, &status);
+ require_action(itemParams != NULL, error_exit, itemParams = NULL);
+
+ // find the next match until we hit maxMatches, or no more matches found
+ while ( !(!itemParams->returnAllMatches && matchCount >= itemParams->maxMatches) &&
+ SecItemSearchCopyNext(itemParams, (CFTypeRef*)&item) == errSecSuccess) {
+
+ if (FilterCandidateItem((CFTypeRef*)&item, itemParams, &identity))
+ continue; // move on to next item
+
+ ++matchCount; // we have a match
+
+ tmpStatus = AddItemResults(item, identity, itemParams, allocator, &itemArray, result);
+ if (tmpStatus && (status == errSecSuccess))
+ status = tmpStatus;
+
+ if (item) {
+ CFRelease(item);
+ item = NULL;
+ }
+ if (identity) {
+ CFRelease(identity);
+ identity = NULL;
+ }
+ }
+
+ if (status == errSecSuccess)
+ status = (matchCount > 0) ? errSecSuccess : errSecItemNotFound;
+
+error_exit:
+ if (status != errSecSuccess && result != NULL && *result != NULL) {
+ CFRelease(*result);
+ *result = NULL;
+ }
+ _FreeSecItemParams(itemParams);
+
+ return status;
+}
+
+OSStatus
+SecItemCopyDisplayNames(
+ CFArrayRef items,
+ CFArrayRef *displayNames)
+{
+ BEGIN_SECAPI
+ Required(items);
+ Required(displayNames);
+ //%%%TBI
+ return errSecUnimplemented;
+ END_SECAPI
+}
+
+OSStatus
+SecItemAdd_osx(
+ CFDictionaryRef attributes,
+ CFTypeRef *result)
+{
+ if (!attributes)
+ return errSecParam;
+ else if (result)
+ *result = NULL;
+
+ CFAllocatorRef allocator = CFGetAllocator(attributes);
+ CFMutableArrayRef itemArray = NULL;
+ SecKeychainItemRef item = NULL;
+ OSStatus tmpStatus, status = errSecSuccess;
+
+ // validate input attribute parameters
+ SecItemParams *itemParams = _CreateSecItemParamsFromDictionary(attributes, &status);
+ require_action(itemParams != NULL, error_exit, itemParams = NULL);
+
+ // currently, we don't support adding SecIdentityRef items (an aggregate item class),
+ // since the private key should already be in a keychain by definition. We could support
+ // this as a copy operation for the private key if a different keychain is specified,
+ // but in any case it should try to add the certificate. See <rdar://8317887>.
+ require_action(!itemParams->returnIdentity, error_exit, status = errSecItemInvalidValue);
+
+ if (!itemParams->useItems) {
+ // create a single keychain item specified by the input attributes
+ status = SecKeychainItemCreateFromContent(itemParams->itemClass,
+ itemParams->attrList,
+ (itemParams->itemData) ? (UInt32)CFDataGetLength(itemParams->itemData) : 0,
+ (itemParams->itemData) ? CFDataGetBytePtrVoid(itemParams->itemData) : NULL,
+ itemParams->keychain,
+ itemParams->access,
+ &item);
+ require_noerr(status, error_exit);
+
+ // return results (if requested)
+ if (result) {
+ itemParams->maxMatches = 1; // in case kSecMatchLimit was set to > 1
+ tmpStatus = AddItemResults(item, NULL, itemParams, allocator, &itemArray, result);
+ if (tmpStatus && (status == errSecSuccess))
+ status = tmpStatus;
+ }
+ CFRelease(item);
+ }
+ else {
+ // add multiple items which are specified in the itemParams->useItems array.
+ // -- SecCertificateRef or SecKeyRef items may or may not be in a keychain.
+ // -- SecKeychainItemRef items are in a keychain (by definition), but may be copied to another keychain.
+ // -- CFDataRef items are a persistent reference; the represented item may be copied to another keychain.
+ //
+ OSStatus aggregateStatus = errSecSuccess;
+ CFIndex ix, count = CFArrayGetCount(itemParams->useItems);
+ itemParams->maxMatches = (count > 1) ? (int)count : 2; // force results to always be returned as an array
+ for (ix=0; ix < count; ix++) {
+ CFTypeRef anItem = (CFTypeRef) CFArrayGetValueAtIndex(itemParams->useItems, ix);
+ if (anItem) {
+ if (SecCertificateGetTypeID() == CFGetTypeID(anItem)) {
+ // SecCertificateRef item
+ tmpStatus = SecCertificateAddToKeychain((SecCertificateRef)anItem, itemParams->keychain);
+ if (!tmpStatus && result) {
+ tmpStatus = AddItemResults((SecKeychainItemRef)anItem, NULL, itemParams, allocator, &itemArray, result);
+ }
+ aggregateStatus = _UpdateAggregateStatus(tmpStatus, aggregateStatus, errSecDuplicateItem);
+ }
+ else if (SecKeyGetTypeID() == CFGetTypeID(anItem)) {
+ // SecKeyRef item
+ SecKeychainRef itemKeychain = NULL;
+ tmpStatus = SecKeychainItemCopyKeychain((SecKeychainItemRef)anItem, &itemKeychain);
+ if (tmpStatus == errSecSuccess) {
+ // key was in a keychain, so we can attempt to copy it
+ SecKeychainItemRef itemCopy = NULL;
+ tmpStatus = SecKeychainItemCreateCopy((SecKeychainItemRef)anItem, itemParams->keychain, itemParams->access, &itemCopy);
+ if (!tmpStatus && result) {
+ tmpStatus = AddItemResults(itemCopy, NULL, itemParams, allocator, &itemArray, result);
+ }
+ if (itemCopy) {
+ CFRelease(itemCopy);
+ }
+ }
+ else {
+ // key was not in any keychain, so must be imported
+ SecKeychainItemRef keyItem = NULL;
+ tmpStatus = _ImportKey((SecKeyRef)anItem, itemParams->keychain, itemParams->access, itemParams->attrList, &keyItem);
+ if (!tmpStatus && result) {
+ tmpStatus = AddItemResults(keyItem, NULL, itemParams, allocator, &itemArray, result);
+ }
+ if (keyItem) {
+ CFRelease(keyItem);
+ }
+ }
+ if (itemKeychain) {
+ CFRelease(itemKeychain);
+ }
+ aggregateStatus = _UpdateAggregateStatus(tmpStatus, aggregateStatus, errSecDuplicateItem);
+ }
+ else if (SecKeychainItemGetTypeID() == CFGetTypeID(anItem)) {
+ // SecKeychainItemRef item
+ SecKeychainItemRef itemCopy = NULL;
+ tmpStatus = SecKeychainItemCreateCopy((SecKeychainItemRef)anItem, itemParams->keychain, itemParams->access, &itemCopy);
+ if (!tmpStatus && result) {
+ tmpStatus = AddItemResults(itemCopy, NULL, itemParams, allocator, &itemArray, result);
+ }
+ if (itemCopy) {
+ CFRelease(itemCopy);
+ }
+ aggregateStatus = _UpdateAggregateStatus(tmpStatus, aggregateStatus, errSecDuplicateItem);
+ }
+ else if (CFDataGetTypeID() == CFGetTypeID(anItem)) {
+ // CFDataRef item (persistent reference)
+ SecKeychainItemRef realItem = NULL;
+ tmpStatus = SecKeychainItemCopyFromPersistentReference((CFDataRef)anItem, &realItem);
+ if (tmpStatus == errSecSuccess) {
+ // persistent reference resolved to a keychain item, so we can attempt to copy it
+ SecKeychainItemRef itemCopy = NULL;
+ tmpStatus = SecKeychainItemCreateCopy(realItem, itemParams->keychain, itemParams->access, &itemCopy);
+ if (!tmpStatus && result) {
+ tmpStatus = AddItemResults(itemCopy, NULL, itemParams, allocator, &itemArray, result);
+ }
+ if (itemCopy) {
+ CFRelease(itemCopy);
+ }
+ }
+ if (realItem) {
+ CFRelease(realItem);
+ }
+ aggregateStatus = _UpdateAggregateStatus(tmpStatus, aggregateStatus, errSecDuplicateItem);
+ }
+ }
+ } // end of itemList array loop
+ status = aggregateStatus;
+ } // end processing multiple items
+
+error_exit:
+ if (status != errSecSuccess && result != NULL && *result != NULL) {
+ CFRelease(*result);
+ *result = NULL;
+ }
+ _FreeSecItemParams(itemParams);
+
+ return status;
+}
+
+OSStatus
+SecItemUpdate_osx(
+ CFDictionaryRef query,
+ CFDictionaryRef attributesToUpdate)
+{
+ if (!query || !attributesToUpdate)
+ return errSecParam;
+
+ // run the provided query to get a list of items to update
+ CFTypeRef results = NULL;
+ OSStatus status = SecItemCopyMatching(query, &results);
+ if (status != errSecSuccess)
+ return status; // nothing was matched, or the query was bad
+
+ CFArrayRef items = NULL;
+ if (CFArrayGetTypeID() == CFGetTypeID(results)) {
+ items = (CFArrayRef) results;
+ }
+ else {
+ items = CFArrayCreate(NULL, &results, 1, &kCFTypeArrayCallBacks);
+ CFRelease(results);
+ }
+
+ OSStatus result = errSecSuccess;
+ CFIndex ix, count = CFArrayGetCount(items);
+ for (ix=0; ix < count; ix++) {
+ CFTypeRef anItem = (CFTypeRef) CFArrayGetValueAtIndex(items, ix);
+ if (anItem) {
+ status = _UpdateKeychainItem(anItem, attributesToUpdate);
+ result = _UpdateAggregateStatus(status, result, errSecSuccess);
+ }
+ }
+
+ if (items) {
+ CFRelease(items);
+ }
+ return result;
+}
+
+OSStatus
+SecItemDelete_osx(
+ CFDictionaryRef query)
+{
+ if (!query)
+ return errSecParam;
+
+ // run the provided query to get a list of items to delete
+ CFTypeRef results = NULL;
+ OSStatus status = SecItemCopyMatching_osx(query, &results);
+ if (status != errSecSuccess)
+ return status; // nothing was matched, or the query was bad
+
+ CFArrayRef items = NULL;
+ if (CFArrayGetTypeID() == CFGetTypeID(results)) {
+ items = (CFArrayRef) results;
+ }
+ else {
+ items = CFArrayCreate(NULL, &results, 1, &kCFTypeArrayCallBacks);
+ CFRelease(results);
+ }
+
+ OSStatus result = errSecSuccess;
+ CFIndex ix, count = CFArrayGetCount(items);
+ for (ix=0; ix < count; ix++) {
+ CFTypeRef anItem = (CFTypeRef) CFArrayGetValueAtIndex(items, ix);
+ if (anItem) {
+ if (SecIdentityGetTypeID() == CFGetTypeID(anItem)) {
+ status = _DeleteIdentity((SecIdentityRef)anItem);
+ }
+ else {
+ status = _DeleteKeychainItem(anItem);
+ }
+ result = _UpdateAggregateStatus(status, result, errSecSuccess);
+ }
+ }
+
+ if (items)
+ CFRelease(items);
+
+ return result;
+}