#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
#else
if (priority < LOG_NOTICE) #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 CFStringRef *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);
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;
}
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 CFStringRef *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);
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;
}
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 CFStringRef *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;
}
}
return keyAlgValue;
}
enum ItemRepresentation
{
kStringRepresentation,
kDataRepresentation,
kNumberRepresentation,
kBooleanRepresentation,
kDateRepresentation
};
struct InternalAttributeListInfo
{
UInt32 oldItemType;
const CFStringRef *newItemType;
ItemRepresentation itemRepresentation;
};
static InternalAttributeListInfo gGenericPasswordAttributes[] =
{
{ kSecCreationDateItemAttr, &kSecAttrCreationDate, kDateRepresentation },
{ kSecModDateItemAttr, &kSecAttrModificationDate, kDateRepresentation },
{ kSecDescriptionItemAttr, &kSecAttrDescription, kStringRepresentation },
{ kSecCommentItemAttr, &kSecAttrComment, kStringRepresentation },
{ kSecCreatorItemAttr, &kSecAttrCreator, kNumberRepresentation }, { kSecTypeItemAttr, &kSecAttrType, kNumberRepresentation }, { 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 }, { kSecTypeItemAttr, &kSecAttrType, kNumberRepresentation }, { kSecLabelItemAttr, &kSecAttrLabel, kStringRepresentation },
{ kSecInvisibleItemAttr, &kSecAttrIsInvisible, kBooleanRepresentation },
{ kSecNegativeItemAttr, &kSecAttrIsNegative, kBooleanRepresentation },
{ kSecAccountItemAttr, &kSecAttrAccount, kStringRepresentation },
{ kSecSecurityDomainItemAttr, &kSecAttrSecurityDomain, kStringRepresentation },
{ kSecServerItemAttr, &kSecAttrServer, kStringRepresentation },
{ kSecAuthenticationTypeItemAttr, &kSecAttrAuthenticationType, kStringRepresentation }, { 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 }, { kSecKeyPrintName, &kSecAttrLabel, kStringRepresentation }, { kSecKeyPermanent, &kSecAttrIsPermanent, kBooleanRepresentation },
{ kSecKeyLabel, &kSecAttrApplicationLabel, kDataRepresentation }, { kSecKeyApplicationTag, &kSecAttrApplicationTag, kDataRepresentation },
{ kSecKeyKeyType, &kSecAttrKeyType, kStringRepresentation }, { kSecKeyKeySizeInBits, &kSecAttrKeySizeInBits, kNumberRepresentation },
{ kSecKeyEffectiveKeySize, &kSecAttrEffectiveKeySize, kNumberRepresentation },
{ kSecKeyEncrypt, &kSecAttrCanEncrypt, kBooleanRepresentation },
{ kSecKeyDecrypt, &kSecAttrCanDecrypt, kBooleanRepresentation },
{ kSecKeyDerive, &kSecAttrCanDerive, kBooleanRepresentation },
{ kSecKeySign, &kSecAttrCanSign, kBooleanRepresentation },
{ kSecKeyVerify, &kSecAttrCanVerify, 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)) {
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); 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
)
{
attrList = (SecKeychainAttributeList*) calloc(1, sizeof(SecKeychainAttributeList));
CFIndex itemsInDictionary = CFDictionaryGetCount(dictionaryRef);
CFTypeRef keys[itemsInDictionary];
CFTypeRef values[itemsInDictionary];
CFTypeRef *keysPtr = keys;
CFTypeRef *valuesPtr = values;
CFDictionaryGetKeysAndValues(dictionaryRef, keys, values);
CFIndex count = 0;
CFIndex i;
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)
{
valuesPtr[i] = NULL;
}
}
attrList->count = (UInt32)count;
attrList->attr = (SecKeychainAttribute*) malloc(sizeof(SecKeychainAttribute) * count);
int resultPointer = 0;
for (i = 0; i < itemsInDictionary; ++i)
{
if (values[i] != NULL)
{
attrList->attr[resultPointer].tag = tags[i];
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));
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;
}
dictionaryRef = CFDictionaryCreateMutable(allocator, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
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) {
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) {
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 {
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)) {
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;
}
}
SecKeychainItemFreeContent(&list, NULL);
free(list.attr);
return result;
}
static OSStatus
_CreateAttributesDictionaryFromGenericPasswordItem(
CFAllocatorRef allocator,
SecKeychainItemRef item,
CFDictionaryRef *dictionary)
{
CFMutableDictionaryRef dict = NULL;
OSStatus result = _ConvertOldFormatToNewFormat(allocator, gGenericPasswordAttributes, kNumberOfGenericPasswordAttributes, item, dict);
if (result == errSecSuccess) {
CFDictionaryAddValue(dict, kSecClass, kSecClassGenericPassword);
}
*dictionary = dict;
return result;
}
static OSStatus
_CreateAttributesDictionaryFromCertificateItem(
CFAllocatorRef allocator,
SecKeychainItemRef item,
CFDictionaryRef *dictionary)
{
CFMutableDictionaryRef dict = NULL;
OSStatus result = _ConvertOldFormatToNewFormat(allocator, gCertificateAttributes, kNumberOfCertificateAttributes, item, dict);
if (result == errSecSuccess) {
CFDictionaryAddValue(dict, kSecClass, kSecClassCertificate);
}
*dictionary = dict;
return errSecSuccess;
}
static OSStatus
_CreateAttributesDictionaryFromKeyItem(
CFAllocatorRef allocator,
SecKeychainItemRef item,
CFDictionaryRef *dictionary)
{
#if 0
if (status) {
goto error_exit; }
status = SecKeychainItemCopyAttributesAndData(item, info, &itemClass, &attrList, NULL, NULL);
CFMutableDictionaryRef dict = NULL;
OSStatus result = _ConvertOldFormatToNewFormat(allocator, gKeyAttributes, kNumberOfKeyAttributes, item, dict);
if (result == errSecSuccess) {
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; }
switch (itemClass)
{
case kSecInternetPasswordItemClass:
itemID = CSSM_DL_DB_RECORD_INTERNET_PASSWORD;
break;
case kSecGenericPasswordItemClass:
itemID = CSSM_DL_DB_RECORD_GENERIC_PASSWORD;
break;
case 'ashp':
itemID = CSSM_DL_DB_RECORD_APPLESHARE_PASSWORD;
break;
default:
itemID = itemClass;
break;
}
status = SecKeychainItemCopyKeychain(item, &keychain);
if (status) {
goto error_exit; }
status = SecKeychainAttributeInfoForItemID(keychain, itemID, &info);
if (status) {
goto error_exit; }
status = SecKeychainItemCopyAttributesAndData(item, info, &itemClass, &attrList, NULL, NULL);
if (status) {
goto error_exit; }
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) {
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) {
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 {
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)) {
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:
{
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;
}
static OSStatus
_CreateAttributesDictionaryFromInternetPasswordItem(
CFAllocatorRef allocator,
SecKeychainItemRef item,
CFDictionaryRef *dictionary)
{
OSStatus status;
SecKeychainAttribute attr[] = {
{ kSecServerItemAttr, 0, NULL },
{ kSecSecurityDomainItemAttr, 0, NULL },
{ kSecAccountItemAttr, 0, NULL },
{ kSecPathItemAttr, 0, NULL },
{ kSecPortItemAttr, 0, NULL },
{ kSecProtocolItemAttr, 0, NULL },
{ kSecAuthenticationTypeItemAttr, 0, NULL },
{ kSecCommentItemAttr, 0, NULL },
{ kSecDescriptionItemAttr, 0, NULL },
{ kSecLabelItemAttr, 0, NULL },
{ kSecCreationDateItemAttr, 0, NULL },
{ kSecModDateItemAttr, 0, NULL },
{ kSecCreatorItemAttr, 0, NULL },
{ kSecTypeItemAttr, 0, NULL },
{ kSecInvisibleItemAttr, 0, NULL },
{ kSecNegativeItemAttr, 0, NULL },
};
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;
status = SecKeychainItemCopyContent(item, NULL, &attrList, NULL, NULL);
require_noerr(status, SecKeychainItemCopyContent_failed);
numValues = 0;
keys[numValues] = kSecClass;
values[numValues] = kSecClassInternetPassword;
++numValues;
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;
}
}
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;
}
}
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;
}
}
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;
}
}
if ( attrList.attr[4].length > 0 ) {
keys[numValues] = kSecAttrPort;
values[numValues] = CFNumberCreate(allocator, kCFNumberSInt32Type, attrList.attr[4].data);
if ( values[numValues] != NULL ) {
++numValues;
}
}
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;
}
}
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;
}
}
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;
}
}
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;
}
}
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;
}
}
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;
}
}
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;
}
}
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;
}
}
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;
}
}
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;
}
}
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;
}
}
*dictionary = CFDictionaryCreate(allocator, keys, values, numValues, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
for ( index = 0; index < numValues; ++index )
{
CFRelease(values[index]);
}
(void) SecKeychainItemFreeContent(&attrList, NULL);
SecKeychainItemCopyContent_failed:
return ( status );
}
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;
}
static void
_FreeAttrList(
SecKeychainAttributeList *attrListPtr)
{
UInt32 index;
if ( attrListPtr != NULL ) {
if ( attrListPtr->attr != NULL ) {
for ( index = 0; index < attrListPtr->count; ++index ) {
free(attrListPtr->attr[index].data);
}
free(attrListPtr->attr);
}
free(attrListPtr);
}
}
static OSStatus
_CFDataCreateAttribute(
CFDataRef data,
SecKeychainAttrType tag,
SecKeychainAttributePtr attr)
{
OSStatus status = errSecSuccess;
CFRange range;
attr->tag = tag;
attr->length = (UInt32) CFDataGetLength(data);
range = CFRangeMake(0, (CFIndex)attr->length);
attr->data = malloc(attr->length);
require_action(attr->data != NULL, malloc_failed, status = errSecBufferTooSmall);
CFDataGetBytes(data, range, (UInt8 *)attr->data);
malloc_failed:
return ( status );
}
static OSStatus
_CFStringCreateAttribute(
CFStringRef string,
SecKeychainAttrType tag,
SecKeychainAttributePtr attr)
{
OSStatus status = errSecSuccess;
CFRange range;
attr->tag = tag;
range = CFRangeMake(0, CFStringGetLength(string));
CFStringGetBytes(string, range, kCFStringEncodingUTF8, 0, FALSE, NULL, 0, (CFIndex *)&attr->length);
attr->data = malloc(attr->length);
require_action(attr->data != NULL, malloc_failed, status = errSecBufferTooSmall);
CFStringGetBytes(string, range, kCFStringEncodingUTF8, 0, FALSE, (UInt8 *)attr->data, attr->length, NULL);
malloc_failed:
return ( status );
}
static OSStatus
_CreateSecKeychainGenericPasswordAttributeListFromDictionary(
CFDictionaryRef attrDictionary,
SecKeychainAttributeList **attrList)
{
return _ConvertNewFormatToOldFormat(NULL, gGenericPasswordAttributes, kNumberOfGenericPasswordAttributes, attrDictionary, *attrList);
}
static OSStatus
_CreateSecKeychainCertificateAttributeListFromDictionary(
CFDictionaryRef attrDictionary,
SecKeychainAttributeList **attrList)
{
return _ConvertNewFormatToOldFormat(NULL, gCertificateAttributes, kNumberOfCertificateAttributes, attrDictionary, *attrList);
}
static OSStatus
_CreateSecKeychainKeyAttributeListFromDictionary(
CFDictionaryRef attrDictionary,
SecKeychainAttributeList **attrList)
{
#if 0
return _ConvertNewFormatToOldFormat(NULL, gKeyAttributes, kNumberOfKeyAttributes, attrDictionary, *attrList);
#else
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);
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;
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;
}
}
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;
}
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;
}
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;
}
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;
}
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;
}
}
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;
}
}
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;
}
}
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;
}
}
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;
}
}
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;
}
}
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;
}
}
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;
}
}
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;
}
}
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;
}
}
*attrList = attrListPtr;
return ( errSecSuccess );
malloc_number_failed:
CFDataCreateAttribute_failed:
CFStringCreateAttribute_failed:
malloc_attrPtr_failed:
_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 (!CFEqual(t, obj))
{
CFRelease(t);
return NULL;
}
CFRelease(t);
return CFNumberCreate(0, kCFNumberSInt32Type, &value);
}
return NULL;
}
static OSStatus
_CreateSecKeychainInternetPasswordAttributeListFromDictionary(
CFDictionaryRef attrDictionary,
SecKeychainAttributeList **attrList)
{
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);
if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrServer, (const void **)&value) ) {
status = _CFStringCreateAttribute((CFStringRef)value, kSecServerItemAttr, &attrListPtr->attr[attrListPtr->count]);
require_noerr_quiet(status, CFStringCreateAttribute_failed);
++attrListPtr->count;
}
if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrSecurityDomain, (const void **)&value) ) {
status = _CFStringCreateAttribute((CFStringRef)value, kSecSecurityDomainItemAttr, &attrListPtr->attr[attrListPtr->count]);
require_noerr_quiet(status, CFStringCreateAttribute_failed);
++attrListPtr->count;
}
if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrAccount, (const void **)&value) ) {
status = _CFStringCreateAttribute((CFStringRef)value, kSecAccountItemAttr, &attrListPtr->attr[attrListPtr->count]);
require_noerr_quiet(status, CFStringCreateAttribute_failed);
++attrListPtr->count;
}
if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrPath, (const void **)&value) ) {
status = _CFStringCreateAttribute((CFStringRef)value, kSecPathItemAttr, &attrListPtr->attr[attrListPtr->count]);
require_noerr_quiet(status, CFStringCreateAttribute_failed);
++attrListPtr->count;
}
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;
}
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;
}
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;
}
if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrComment, (const void **)&value) ) {
status = _CFStringCreateAttribute((CFStringRef)value, kSecCommentItemAttr, &attrListPtr->attr[attrListPtr->count]);
require_noerr_quiet(status, CFStringCreateAttribute_failed);
++attrListPtr->count;
}
if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrDescription, (const void **)&value) ) {
status = _CFStringCreateAttribute((CFStringRef)value, kSecDescriptionItemAttr, &attrListPtr->attr[attrListPtr->count]);
require_noerr_quiet(status, CFStringCreateAttribute_failed);
++attrListPtr->count;
}
if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrLabel, (const void **)&value) ) {
status = _CFStringCreateAttribute((CFStringRef)value, kSecLabelItemAttr, &attrListPtr->attr[attrListPtr->count]);
require_noerr_quiet(status, CFStringCreateAttribute_failed);
++attrListPtr->count;
}
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;
}
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;
}
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;
}
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;
}
*attrList = attrListPtr;
return ( errSecSuccess );
malloc_authenticationType_failed:
malloc_protocol_failed:
malloc_port_failed:
CFStringCreateAttribute_failed:
malloc_attrPtr_failed:
_FreeAttrList(attrListPtr);
calloc_attrListPtr_failed:
return ( errSecBufferTooSmall );
}
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;
}
static CFStringRef
_AppNameFromSecTrustedApplication(
CFAllocatorRef alloc,
SecTrustedApplicationRef appRef)
{
CFStringRef result;
OSStatus status;
CFDataRef appDataRef;
result = NULL;
status = SecTrustedApplicationCopyData(appRef, &appDataRef);
if ( status == errSecSuccess ) {
CFStringRef path;
path = CFStringCreateWithCString(NULL, (char *)CFDataGetBytePtrVoid(appDataRef), kCFStringEncodingUTF8);
if ( path != NULL ) {
if ( CFStringHasPrefix(path, CFSTR("/")) && (CFStringFind(path, CFSTR("://"), 0).location == kCFNotFound) ) {
CFRange nameRange, compRg;
nameRange = CFRangeMake(0, CFStringGetLength(path));
while ( (nameRange.length > 0) && (CFStringGetCharacterAtIndex(path, nameRange.length - 1) == '/') ) {
nameRange.length --;
}
if ( nameRange.length > 0 ) {
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 );
}
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; }
status = SecKeychainItemCopyKeychain(privateKey, &keychain);
if (status) {
goto error_exit; }
status = SecKeychainAttributeInfoForItemID(keychain, kSecPrivateKeyItemClass, &info);
if (status) {
goto error_exit; }
status = SecKeychainItemCopyAttributesAndData(privateKey, info, NULL, &keyAttrList, NULL, NULL);
if (status) {
goto error_exit; }
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; }
status = SecKeychainSearchCreateFromAttributes(keychain, kSecPublicKeyItemClass, &attrList, &search);
if (status) {
goto error_exit; }
status = SecKeychainSearchCopyNext(search, &publicKey);
if (status) {
goto error_exit; }
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;
}
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 idx, 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)) {
return SecKeychainItemDelete(itemRef);
}
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);
}
}
}
status = SecKeychainItemCopyAccess(itemRef, &access);
require_noerr(status, finish);
require_quiet(access != NULL, finish);
status = SecAccessCopySelectedACLList(access, CSSM_ACL_AUTHORIZATION_DECRYPT, &aclList);
require_noerr(status, finish);
require_quiet(aclList != NULL, finish);
acl = (SecACLRef)CFArrayGetValueAtIndex(aclList, 0);
require_quiet(acl != NULL, finish);
status = SecACLCopySimpleContents(acl, &appList, &description, &promptSelector);
require_noerr(status, finish);
require_quiet(appList != NULL, finish);
count = CFArrayGetCount(appList);
for ( idx = 0; idx < count; idx++ ) {
SecTrustedApplicationRef itemAppRef = (SecTrustedApplicationRef)CFArrayGetValueAtIndex(appList, idx);
require_quiet(itemAppRef != NULL, finish);
CFReleaseSafe(itemAppName);
itemAppName = _AppNameFromSecTrustedApplication(CFGetAllocator(itemRef), itemAppRef);
if (itemAppName == NULL) {
count = 0;
goto finish;
}
CFReleaseSafe(currentAppRef);
status = SecTrustedApplicationCreateFromPath(NULL, ¤tAppRef);
require_noerr(status, finish);
require_quiet(currentAppRef != NULL, finish);
CFReleaseSafe(currentAppName);
currentAppName = _AppNameFromSecTrustedApplication(CFGetAllocator(itemRef), currentAppRef);
require_quiet(currentAppName != NULL, finish);
if ( CFStringCompare(currentAppName, itemAppName, 0) == kCFCompareEqualTo ) {
count = 0;
goto finish;
}
}
finish:
CFReleaseSafe(currentAppName);
CFReleaseSafe(itemAppName);
CFReleaseSafe(currentAppRef);
CFReleaseSafe(description);
CFReleaseSafe(appList);
CFReleaseSafe(aclList);
CFReleaseSafe(access);
if ((count == 0) || (status == errSecVerifyFailed)) {
status = SecKeychainItemDelete(itemRef);
} else {
status = errSecInvalidOwnerEdit;
}
return status;
}
static OSStatus
_ReplaceKeychainItem(
SecKeychainItemRef itemToUpdate,
SecKeychainAttributeList *changeAttrList,
CFDataRef itemData)
{
OSStatus status;
UInt32 itemID;
SecItemClass itemClass;
SecKeychainAttributeInfo *info = NULL;
SecKeychainAttributeList *attrList = NULL;
SecKeychainAttributeList newAttrList = { 0, NULL};
SecKeychainRef keychain = NULL;
SecKeychainItemRef newItem = NULL;
int priority = LOG_DEBUG;
const char *format = "ReplaceKeychainItem (%d) error %d";
status = SecKeychainItemCopyKeychain(itemToUpdate, &keychain);
if (status) { secitemlog(priority, format, 1, (int)status); }
require_noerr(status, replace_failed);
status = SecKeychainItemCopyAttributesAndData(itemToUpdate, NULL, &itemClass, NULL, NULL, NULL);
if (status) { secitemlog(priority, format, 2, (int)status); }
require_noerr(status, replace_failed);
switch (itemClass)
{
case kSecInternetPasswordItemClass:
itemID = CSSM_DL_DB_RECORD_INTERNET_PASSWORD;
break;
case kSecGenericPasswordItemClass:
itemID = CSSM_DL_DB_RECORD_GENERIC_PASSWORD;
break;
default:
itemID = itemClass;
break;
}
status = SecKeychainAttributeInfoForItemID(keychain, itemID, &info);
if (status) { secitemlog(priority, format, 3, (int)status); }
status = SecKeychainItemCopyAttributesAndData(itemToUpdate, info, &itemClass, &attrList, NULL, NULL);
if (status) { secitemlog(priority, format, 4, (int)status); }
require(attrList != NULL, replace_failed);
if (itemClass == kSecInternetPasswordItemClass || itemClass == kSecGenericPasswordItemClass) {
CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault);
CFStringRef uuidStr = (uuid) ? CFUUIDCreateString(kCFAllocatorDefault, uuid) : CFSTR("MOVED");
CFReleaseSafe(uuid);
if (uuidStr) {
CFIndex maxLength = CFStringGetMaximumSizeForEncoding(CFStringGetLength(uuidStr), kCFStringEncodingUTF8) + 1;
char* buffer = (char*) malloc(maxLength);
if (buffer) {
if (CFStringGetCString(uuidStr, buffer, maxLength, kCFStringEncodingUTF8)) {
UInt32 length = (UInt32)strlen(buffer);
SecKeychainAttribute attrs[] = { { kSecAccountItemAttr, length, (char*)buffer }, };
SecKeychainAttributeList updateAttrList = { sizeof(attrs) / sizeof(attrs[0]), attrs };
status = SecKeychainItemModifyAttributesAndData(itemToUpdate, &updateAttrList, 0, NULL);
if (status) { secitemlog(priority, format, 5, (int)status); }
if (status == errSecVerifyFailed) {
status = SecKeychainItemDelete(itemToUpdate);
if (status) { secitemlog(priority, format, 6, (int)status); }
}
}
free(buffer);
}
CFReleaseSafe(uuidStr);
}
}
require_noerr(status, replace_failed);
newAttrList.count = attrList->count;
newAttrList.attr = (SecKeychainAttribute *) malloc(sizeof(SecKeychainAttribute) * attrList->count);
int i, newCount;
for (i=0, newCount=0; i < attrList->count; i++) {
if (attrList->attr[i].length > 0) {
newAttrList.attr[newCount++] = attrList->attr[i];
#if 0
SecKeychainAttrType tag = attrList->attr[i].tag;
SecKeychainAttrType htag=(SecKeychainAttrType)OSSwapConstInt32(tag);
char tmp[sizeof(SecKeychainAttrType) + 1];
char tmpdata[attrList->attr[i].length + 1];
memcpy(tmp, &htag, sizeof(SecKeychainAttrType));
tmp[sizeof(SecKeychainAttrType)]=0;
memcpy(tmpdata, attrList->attr[i].data, attrList->attr[i].length);
tmpdata[attrList->attr[i].length]=0;
secitemlog(priority, "item attr '%s' = %d bytes: \"%s\"",
tmp, (int)attrList->attr[i].length, tmpdata);
#endif
}
}
newAttrList.count = newCount;
status = SecKeychainItemCreateFromContent(itemClass, &newAttrList,
(UInt32)((itemData) ? CFDataGetLength(itemData) : 0),
(const void *)((itemData) ? CFDataGetBytePtr(itemData) : NULL),
keychain, NULL, &newItem);
if (status) { secitemlog(priority, format, 7, (int)status); }
require_noerr(status, replace_failed);
status = SecKeychainItemDelete(itemToUpdate);
status = (changeAttrList) ? SecKeychainItemModifyContent(newItem, changeAttrList, 0, NULL) : errSecSuccess;
if (status) { secitemlog(priority, format, 8, (int)status); }
if (status == errSecSuccess) {
status = errSecDuplicateItem;
}
replace_failed:
if (newAttrList.attr) {
free(newAttrList.attr);
}
if (attrList) {
SecKeychainItemFreeAttributesAndData(attrList, NULL);
}
if (info) {
SecKeychainFreeAttributeInfo(info);
}
CFReleaseSafe(newItem);
CFReleaseSafe(keychain);
return status;
}
static OSStatus
_UpdateKeychainItem(CFTypeRef item, CFDictionaryRef changedAttributes)
{
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);
if (SecKeychainItemGetTypeID() == itemType ||
SecCertificateGetTypeID() == itemType ||
SecKeyGetTypeID() == itemType) {
itemToUpdate = (SecKeychainItemRef) CFRetain(item);
}
else if (CFDataGetTypeID() == itemType) {
status = SecKeychainItemCopyFromPersistentReference((CFDataRef)item, &itemToUpdate);
}
else if (CFDictionaryGetTypeID() == itemType) {
CFTypeRef value = NULL;
if (CFDictionaryGetValueIfPresent((CFDictionaryRef)item, kSecValueRef, &value)) {
itemToUpdate = (SecKeychainItemRef) CFRetain(value);
}
else if (CFDictionaryGetValueIfPresent((CFDictionaryRef)item, kSecValuePersistentRef, &value)) {
status = SecKeychainItemCopyFromPersistentReference((CFDataRef)value, &itemToUpdate);
}
}
else if (SecIdentityGetTypeID() == itemType) {
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);
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);
}
}
theData = (CFDataRef)CFDictionaryGetValue(changedAttributes, kSecValueData);
if (theData != NULL) {
require_action(CFDataGetTypeID() == CFGetTypeID(theData), update_failed, status = errSecParam);
}
status = SecKeychainItemModifyContent(itemToUpdate,
(changeAttrList->count == 0) ? NULL : changeAttrList,
(theData != NULL) ? (UInt32)CFDataGetLength(theData) : 0,
(theData != NULL) ? CFDataGetBytePtrVoid(theData) : NULL);
require_noerr(status, update_failed);
if (CFDictionaryGetValueIfPresent(changedAttributes, kSecAttrAccess, (const void **)&access)) {
status = SecKeychainItemSetAccess(itemToUpdate, access);
}
update_failed:
if (status == errSecVerifyFailed &&
(itemClass == kSecInternetPasswordItemClass || itemClass == kSecGenericPasswordItemClass)) {
status = _ReplaceKeychainItem(itemToUpdate,
(changeAttrList->count == 0) ? NULL : changeAttrList,
theData);
}
if (itemToUpdate)
CFRelease(itemToUpdate);
_FreeAttrList(changeAttrList);
return status;
}
static OSStatus
_DeleteKeychainItem(CFTypeRef item)
{
OSStatus status = errSecSuccess;
if (!item) {
return errSecParam;
}
SecKeychainItemRef itemToDelete = NULL;
CFTypeID itemType = CFGetTypeID(item);
if (SecKeychainItemGetTypeID() == itemType ||
SecCertificateGetTypeID() == itemType ||
SecKeyGetTypeID() == itemType) {
itemToDelete = (SecKeychainItemRef) CFRetain(item);
}
else if (CFDataGetTypeID() == itemType) {
status = SecKeychainItemCopyFromPersistentReference((CFDataRef)item, &itemToDelete);
}
else if (CFDictionaryGetTypeID() == itemType) {
CFTypeRef value = NULL;
if (CFDictionaryGetValueIfPresent((CFDictionaryRef)item, kSecValueRef, &value)) {
itemToDelete = (SecKeychainItemRef) CFRetain(value);
}
else if (CFDictionaryGetValueIfPresent((CFDictionaryRef)item, kSecValuePersistentRef, &value)) {
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)
{
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)
{
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)) {
itemClass = kSecCertificateItemClass;
if (isIdentity) *isIdentity = true;
}
else if (CFEqual(item, kSecClassKey)) {
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)
{
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()) {
status = SecKeychainItemCopyFromPersistentReference((CFDataRef)item, &itemRef);
}
else {
itemRef = (SecKeychainItemRef) CFRetain(item);
}
if (itemRef) {
SecItemClass itemClass = 0;
CFTypeID itemTypeID = CFGetTypeID(itemRef);
if (itemTypeID == SecIdentityGetTypeID() || itemTypeID == SecCertificateGetTypeID()) {
itemClass = kSecCertificateItemClass;
}
else if (itemTypeID == SecKeychainItemGetTypeID()) {
status = SecKeychainItemCopyAttributesAndData(itemRef, NULL, &itemClass, NULL, NULL, NULL);
}
else if (itemTypeID == SecKeyGetTypeID()) {
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; }
result = itemClass;
}
}
}
}
return result;
}
struct SecItemParams {
CFDictionaryRef query; int numResultTypes; int maxMatches; uint32 keyUsage; Boolean returningAttributes; Boolean returningData; Boolean returningRef; Boolean returningPersistentRef; Boolean returnAllMatches; Boolean returnIdentity; Boolean trustedOnly; Boolean issuerAndSNToMatch; SecItemClass itemClass; SecPolicyRef policy; SecKeychainRef keychain; CFArrayRef useItems; CFArrayRef itemList; CFTypeRef searchList; CFTypeRef matchLimit; CFTypeRef emailAddrToMatch; CFTypeRef validOnDate; CFTypeRef keyClass; CFTypeRef service; CFTypeRef issuer; CFTypeRef serialNumber; CFTypeRef search; CFTypeRef assumedKeyClass; CFIndex itemListIndex; SecKeychainAttributeList *attrList; SecAccessRef access; CFDataRef itemData; CFTypeRef itemRef; SecIdentityRef identityRef; CFDataRef itemPersistentRef; Boolean isPCSItem; };
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 = NULL;
}
else if (!(*value)) {
return errSecSuccess;
}
else {
CFTypeID actualTypeID = CFGetTypeID(*value);
if (!((expectedTypeID == actualTypeID) || (altTypeID && altTypeID == actualTypeID))) {
if ((expectedTypeID == SecKeychainItemGetTypeID()) &&
(actualTypeID == SecKeyGetTypeID() || actualTypeID == SecCertificateGetTypeID())) {
CFRetain(*value);
return errSecSuccess;
}
return errSecItemInvalidValue;
}
else {
CFRetain(*value);
}
}
return errSecSuccess;
}
static void
_EnsureUserDefaultKeychainIsSearched(SecItemParams *itemParams)
{
OSStatus status;
CFArrayRef tmpList = (CFArrayRef) itemParams->searchList;
if (tmpList) {
itemParams->searchList = (CFArrayRef) CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, tmpList);
CFRelease(tmpList);
} else {
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)) {
CFArrayAppendValue((CFMutableArrayRef)itemParams->searchList, userKeychain);
}
CFRelease(userKeychain);
}
}
static void
_EnsureUserDefaultKeychainIsTargeted(SecItemParams *itemParams)
{
if (itemParams->keychain) {
return; }
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);
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);
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); _EnsureUserDefaultKeychainIsTargeted(itemParams); }
}
require_noerr(status = _ValidateDictionaryEntry(dict, kSecValueData, (const void **)&itemParams->itemData, CFDataGetTypeID(), CFStringGetTypeID()), error_exit);
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) {
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);
}
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; }
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);
require_noerr(status = _ValidateDictionaryEntry(dict, kSecAttrAccess, (const void **)&itemParams->access, SecAccessGetTypeID(), NULL), error_exit);
if (itemParams->access == NULL) {
require_noerr(status = _ValidateDictionaryEntry(dict, CFSTR("kSecAttrAccess"), (const void **)&itemParams->access, SecAccessGetTypeID(), NULL), error_exit);
}
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;
if (!itemParams->numResultTypes) {
itemParams->returningRef = TRUE;
itemParams->numResultTypes = 1;
}
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 ((itemParams->itemClass==kSecInternetPasswordItemClass || itemParams->itemClass==kSecGenericPasswordItemClass) && itemParams->returningData)
status = errSecReturnDataUnsupported;
require_noerr(status, error_exit);
}
if (itemParams->useItems) {
if (itemParams->itemClass == 0) {
itemParams->itemClass = _ItemClassFromItemList(itemParams->useItems);
}
status = errSecSuccess;
goto error_exit; }
require_noerr(status = _CreateSecKeychainAttributeListFromDictionary(dict, itemParams->itemClass, &itemParams->attrList), error_exit);
if(itemParams->policy) {
CFDictionaryRef policyDict = SecPolicyCopyProperties(itemParams->policy);
CFStringRef oidStr = (CFStringRef) CFDictionaryGetValue(policyDict, kSecPolicyOid);
if(oidStr && CFStringCompare(kSecPolicyAppleSMIME,oidStr,0) == 0) {
require_noerr(status = _ValidateDictionaryEntry(policyDict, kSecPolicyName, (const void **)&itemParams->emailAddrToMatch, CFStringGetTypeID(), NULL), error_exit);
}
CFRelease(policyDict);
}
if ((itemParams->itemClass == kSecCertificateItemClass) && itemParams->emailAddrToMatch) {
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) {
status = SecKeychainSearchCreateForCertificateByIssuerAndSN_CF(itemParams->searchList,
(CFDataRef)itemParams->issuer,
(CFDataRef)itemParams->serialNumber,
(SecKeychainSearchRef*)&itemParams->search);
}
else if (itemParams->returnIdentity && itemParams->policy) {
status = SecIdentitySearchCreateWithPolicy(itemParams->policy,
(CFStringRef)itemParams->service,
itemParams->keyUsage,
itemParams->searchList,
itemParams->trustedOnly,
(SecIdentitySearchRef*)&itemParams->search);
}
else if (itemParams->returnIdentity) {
status = SecIdentitySearchCreate(itemParams->searchList,
itemParams->keyUsage,
(SecIdentitySearchRef*)&itemParams->search);
}
else {
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
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
}
#if !SECTRUST_OSX
static Boolean
_CanIgnoreLeafStatusCodes(CSSM_TP_APPLE_EVIDENCE_INFO *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) {
result = true;
}
else if (ignorableRevocationStatusCode(scode)) {
result = true;
}
else {
result = false;
break;
}
}
return result;
}
#endif
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;
if (date && (CFGetTypeID(date) == CFDateGetTypeID())) {
status = SecTrustSetVerifyDate(trust, date);
if(status) goto cleanup;
}
props = SecPolicyCopyProperties(policy);
if (props) {
CFTypeRef oid = (CFTypeRef) CFDictionaryGetValue(props, kSecPolicyOid);
if (oid && CFEqual(oid, kSecPolicyAppleX509Basic)) {
needChain = true;
}
}
if (!needChain) {
keychains = CFArrayCreate(NULL, NULL, 0, &kCFTypeArrayCallBacks);
status = SecTrustSetKeychains(trust, keychains);
if(status) goto cleanup;
anchors = CFArrayCreate(NULL, NULL, 0, &kCFTypeArrayCallBacks);
status = SecTrustSetAnchorCertificates(trust, anchors);
if(status) goto cleanup;
}
status = SecTrustEvaluate(trust, &trustResult);
if(status) goto cleanup;
status = SecTrustGetResult(trust, &trustResult, &chain, &evidence);
if(status) goto cleanup;
if (!(trustResult == kSecTrustResultProceed ||
trustResult == kSecTrustResultUnspecified ||
trustResult == kSecTrustResultRecoverableTrustFailure)) {
status = errSecCertificateCannotOperate;
goto cleanup;
}
#if SECTRUST_OSX
if (true) {
(void)SecTrustGetCssmResultCode(trust, &status);
#else
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;
#endif
}
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()) {
status = SecKeychainItemCopyFromPersistentReference((CFDataRef)item, &kcItem);
}
else {
kcItem = (SecKeychainItemRef) CFRetain(item);
}
if (kcItem) {
SecItemClass itemClass;
SecKeychainItemRef certRef = NULL;
if (CFGetTypeID(kcItem) == SecIdentityGetTypeID()) {
status = SecIdentityCopyCertificate((SecIdentityRef)kcItem, (SecCertificateRef *)&certRef);
}
if (certRef) {
itemClass = kSecCertificateItemClass;
}
else {
status = SecKeychainItemCopyAttributesAndData(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)
{
OSStatus status = errSecItemNotFound;
if (!params || !params->assumedKeyClass || !params->query || !item)
return status;
if (params->search)
CFRelease(params->search);
params->search = NULL;
_FreeAttrList(params->attrList);
params->attrList = NULL;
CFMutableDictionaryRef dict = CFDictionaryCreateMutableCopy(NULL, 0, params->query);
CFRelease(params->query);
params->query = dict;
CFDictionarySetValue(dict, kSecAttrKeyClass, params->assumedKeyClass);
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;
}
if (_CreateSecKeychainAttributeListFromDictionary(dict, params->itemClass, ¶ms->attrList) == errSecSuccess) {
if (SecKeychainSearchCreateFromAttributes(params->searchList,
params->itemClass,
(params->attrList->count == 0) ? NULL : params->attrList,
(SecKeychainSearchRef*)¶ms->search) == errSecSuccess) {
status = SecKeychainSearchCopyNext((SecKeychainSearchRef)params->search, (SecKeychainItemRef*)item);
}
}
return status;
}
static OSStatus
SecItemSearchCopyNext(SecItemParams *params, CFTypeRef *item)
{
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);
while (status == errSecItemNotFound && params->assumedKeyClass != NULL)
status = UpdateKeychainSearchAndCopyNext(params, item);
}
else if (typeID == 0 && (params->useItems || params->itemList)) {
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) {
*item = CopyResolvedKeychainItem(*item);
if (*item && (CFGetTypeID(*item) == SecIdentityGetTypeID())) {
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()) {
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) {
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;
}
if (nameStarts) {
CFRange range = CFStringFind(commonName, nameStarts, flags);
if (range.length < 1 || range.location > 1)
goto filterOut;
}
if (nameEnds) {
CFRange range = CFStringFind(commonName, nameEnds, flags);
if (range.length < 1 || range.location != (CFStringGetLength(commonName) - CFStringGetLength(nameEnds)))
goto filterOut;
}
if (nameExact) {
CFRange range = CFStringFind(commonName, nameExact, flags);
if (range.length < 1 || (CFStringGetLength(commonName) != CFStringGetLength(nameExact)))
goto filterOut;
}
if (itemParams->returnIdentity) {
if (!foundIdentity) {
status = SecIdentityCreateWithCertificate(itemParams->searchList, (SecCertificateRef) *item, identity);
if (status) goto filterOut;
}
}
if (itemParams->policy) {
status = _FilterWithPolicy(itemParams->policy, (CFDateRef)itemParams->validOnDate, (SecCertificateRef) *item);
if (status) goto filterOut;
}
if (itemParams->validOnDate) {
status = _FilterWithDate(itemParams->validOnDate, (SecCertificateRef) *item);
if (status) goto filterOut;
}
if (itemParams->trustedOnly) {
if (!(foundIdentity && itemParams->returnIdentity && itemParams->policy)) {
status = _FilterWithTrust(itemParams->trustedOnly, (SecCertificateRef) *item);
if (status) goto filterOut;
}
}
}
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;
}
if (foundIdentity && !identity) {
CFRelease(foundIdentity);
}
if (commonName) {
CFRelease(commonName);
}
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)
{
if (!item || !itemParams || !result)
return errSecParam;
if (itemParams->maxMatches > 1) {
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) {
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) {
if (CFGetTypeID(item) == SecCertificateGetTypeID()) {
CFDataRef dataRef = SecCertificateCopyData((SecCertificateRef)item);
if (dataRef) {
if (itemDict) {
CFDictionaryAddValue(itemDict, kSecValueData, dataRef);
}
else if (itemArray) {
CFArrayAppendValue(itemArray, dataRef);
}
else {
*result = CFRetain(dataRef);
}
CFRelease(dataRef);
status = errSecSuccess;
}
else {
status = errSecAllocate;
}
}
else {
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;
tmpStatus = SecKeychainItemCopyAttributesAndData(item, NULL, &itemClass, NULL, NULL, NULL);
if (tmpStatus) {
itemClass = itemParams->itemClass;
}
tmpStatus = _CreateAttributesDictionaryFromItem(allocator, itemClass, item, &attrsDict);
if (attrsDict) {
if (itemDict) {
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
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 (!CFEqual(t, obj)) {
CFRelease(t);
return 0;
}
CFRelease(t);
return v;
} else
return NULL;
}
static OSStatus SecItemUnlockSynchronizableKeychain()
{
SecKeychainRef keychain = NULL;
OSStatus status = SecKeychainCopyLogin(&keychain);
if (!status) {
status = SecKeychainUnlock(keychain, 0, NULL, false);
}
CFReleaseSafe(keychain);
return status;
}
static Boolean SecItemSynchronizable(CFDictionaryRef query)
{
CFTypeRef value = CFDictionaryGetValue(query, kSecAttrSynchronizable);
Boolean result = (value && readNumber(value));
return result;
}
static Boolean SecItemNoLegacy(CFDictionaryRef query)
{
CFTypeRef value = CFDictionaryGetValue(query, kSecAttrNoLegacy);
Boolean result = (value && readNumber(value));
return result;
}
static Boolean SecItemSynchronizableAny(CFDictionaryRef query)
{
CFTypeRef value = CFDictionaryGetValue(query, kSecAttrSynchronizable);
if (value) {
return (CFGetTypeID(value) == CFStringGetTypeID() &&
CFEqual(value, kSecAttrSynchronizableAny));
}
return false;
}
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);
}
static Boolean SecItemSyncEnabled()
{
static dispatch_once_t onceToken;
static Boolean syncEnabled = true;
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;
}
static Boolean SecItemHasSynchronizablePersistentReference(CFDictionaryRef query)
{
CFTypeRef value = CFDictionaryGetValue(query, kSecValuePersistentRef);
if (value) {
const CFIndex kSynchronizablePersistentRefLength = sizeof(int64_t) + 4;
return (CFGetTypeID(value) == CFDataGetTypeID() &&
CFDataGetLength((CFDataRef)value) == kSynchronizablePersistentRefLength);
}
return false;
}
static void SecItemApplyChanges(const void *key, const void *value, void *context)
{
CFMutableDictionaryRef dict = (CFMutableDictionaryRef) context;
if (!dict) return;
CFDictionarySetValue(dict, key, value);
}
static OSStatus SecItemChangeSynchronizability(CFDictionaryRef query, CFDictionaryRef changes, Boolean toSyncable)
{
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 ,
true ,
true ,
true ,
false ,
(toSyncable) ? true : false );
CFMutableDictionaryRef itemQuery = CFDictionaryCreateMutableCopy(NULL, 0, item);
CFDictionaryRemoveValue(itemQuery, kSecAttrCreationDate);
CFDictionaryRemoveValue(itemQuery, kSecAttrModificationDate);
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) {
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) {
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) {
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
string account;
v = CFDictionaryGetValue(refAttributes, CFSTR("mdat"));
if (v) {
attrs->attr[attrs->count].tag = kSecModDateItemAttr;
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;
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++;
}
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 {
ref = NULL;
}
return ref;
}
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;
}
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) {
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) {
CFDictionaryRemoveValue(result, kSecReturnData);
CFDictionaryRemoveValue(result, kSecReturnAttributes);
CFDictionaryRemoveValue(result, kSecReturnRef);
CFDictionaryRemoveValue(result, kSecReturnPersistentRef);
}
if (pruneData) {
CFDictionaryRemoveValue(result, kSecValueData);
}
if (pruneAccess) {
CFDictionaryRemoveValue(result, kSecAttrAccess);
}
if (iOSOut) {
CFDictionaryRemoveValue(result, kSecMatchSearchList);
CFDictionaryRemoveValue(result, kSecUseKeychain);
CFDictionaryRemoveValue(result, kSecMatchPolicy);
SecAccessRef access = (SecAccessRef) CFDictionaryGetValue(result, kSecAttrAccess);
CFStringRef accessGroup = (CFStringRef) CFDictionaryGetValue(result, kSecAttrAccessGroup);
if (access != NULL && accessGroup == NULL) {
if (errSecSuccess == SecItemValidateAppleApplicationGroupAccess(CFSTR("InternetAccounts"))) {
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);
CFTypeRef directRef = CFDictionaryGetValue(result, kSecValueRef);
if (directRef) {
CFDataRef persistentRef = _SecItemGetPersistentReference(directRef);
if (persistentRef) {
CFDictionarySetValue(result, kSecValuePersistentRef, persistentRef);
}
CFDictionaryRemoveValue(result, kSecValueRef);
}
CFTypeRef persistentRef = CFDictionaryGetValue(result, kSecValuePersistentRef);
if (persistentRef) {
CFDictionaryRemoveValue(result, kSecAttrSynchronizable);
}
CFDictionaryRemoveValue(result, kSecAttrModificationDate);
}
else {
if (itemClass)
CFDictionarySetValue(result, kSecClass, itemClass);
CFDictionaryRemoveValue(result, kSecAttrAccessible);
CFDictionaryRemoveValue(result, kSecAttrAccessGroup);
CFDictionaryRemoveValue(result, kSecAttrSynchronizable);
CFDictionaryRemoveValue(result, kSecAttrTombstone);
}
CFDictionaryRemoveValue(result, kSecAttrNoLegacy);
return result;
}
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)) {
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;
}
return (id_ios) ? CFRetain(result_ios) : CFRetain(result_osx);
}
}
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 ios_only = SecItemNoLegacy(query);
Boolean sync_enabled = SecItemSyncEnabled();
Boolean search_ios = SecItemSynchronizable(query);
Boolean merge_search = SecItemSynchronizableAny(query);
Boolean persistref_ios = SecItemHasSynchronizablePersistentReference(query);
if (ios_only || (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 (ios_only || !merge_search || persistref_ios) {
AssignOrReleaseResult(result_ios, result);
return status_ios; }
}
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 (result_ios == NULL) {
AssignOrReleaseResult(result_osx, result);
return status_osx; }
if (result_osx == NULL) {
AssignOrReleaseResult(result_ios, result);
return status_ios; }
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; }
else if (!status_osx || !status_ios) {
return errSecSuccess; }
else if (status_osx == errSecItemNotFound) {
return status_ios; }
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 ios_only = SecItemNoLegacy(attributes);
Boolean sync_enabled = SecItemSyncEnabled();
Boolean add_ios = SecItemSynchronizable(attributes);
if (ios_only || (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 ios_only = SecItemNoLegacy(query);
Boolean sync_enabled = SecItemSyncEnabled();
Boolean search_ios = SecItemSynchronizable(query);
Boolean merge_search = SecItemSynchronizableAny(query);
Boolean persistref_ios = SecItemHasSynchronizablePersistentReference(query);
if (ios_only || (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 (ios_only || !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) {
if (status_osx == status_ios) {
return status_ios;
}
else if (!status_osx || !status_ios) {
if (status_osx == errSecItemNotFound || status_ios == errSecItemNotFound)
return errSecSuccess; else
return (status_osx) ? status_osx : status_ios; }
else if (status_osx == errSecItemNotFound) {
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 ios_only = SecItemNoLegacy(query);
Boolean sync_enabled = SecItemSyncEnabled();
Boolean search_ios = SecItemSynchronizable(query);
Boolean merge_search = SecItemSynchronizableAny(query);
Boolean persistref_ios = SecItemHasSynchronizablePersistentReference(query);
if (ios_only || (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 (ios_only || !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) {
if (status_osx == status_ios) {
return status_ios;
}
else if (!status_osx || !status_ios) {
if (status_osx == errSecItemNotFound || status_ios == errSecItemNotFound)
return errSecSuccess; else
return (status_osx) ? status_osx : status_ios; }
else if (status_osx == errSecItemNotFound) {
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;
SecItemParams *itemParams = _CreateSecItemParamsFromDictionary(query, &status);
require_action(itemParams != NULL, error_exit, itemParams = NULL);
while ( !(!itemParams->returnAllMatches && matchCount >= itemParams->maxMatches) &&
SecItemSearchCopyNext(itemParams, (CFTypeRef*)&item) == errSecSuccess) {
if (FilterCandidateItem((CFTypeRef*)&item, itemParams, &identity))
continue;
++matchCount;
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);
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;
SecItemParams *itemParams = _CreateSecItemParamsFromDictionary(attributes, &status);
require_action(itemParams != NULL, error_exit, itemParams = NULL);
require_action(!itemParams->returnIdentity, error_exit, status = errSecItemInvalidValue);
if (!itemParams->useItems) {
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);
if (result) {
itemParams->maxMatches = 1; tmpStatus = AddItemResults(item, NULL, itemParams, allocator, &itemArray, result);
if (tmpStatus && (status == errSecSuccess))
status = tmpStatus;
}
CFRelease(item);
}
else {
OSStatus aggregateStatus = errSecSuccess;
CFIndex ix, count = CFArrayGetCount(itemParams->useItems);
itemParams->maxMatches = (count > 1) ? (int)count : 2; for (ix=0; ix < count; ix++) {
CFTypeRef anItem = (CFTypeRef) CFArrayGetValueAtIndex(itemParams->useItems, ix);
if (anItem) {
if (SecCertificateGetTypeID() == CFGetTypeID(anItem)) {
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)) {
SecKeychainRef itemKeychain = NULL;
tmpStatus = SecKeychainItemCopyKeychain((SecKeychainItemRef)anItem, &itemKeychain);
if (tmpStatus == errSecSuccess) {
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 {
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 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)) {
SecKeychainItemRef realItem = NULL;
tmpStatus = SecKeychainItemCopyFromPersistentReference((CFDataRef)anItem, &realItem);
if (tmpStatus == errSecSuccess) {
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);
}
}
} status = aggregateStatus;
}
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;
CFTypeRef results = NULL;
OSStatus status = SecItemCopyMatching(query, &results);
if (status != errSecSuccess)
return status;
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;
CFTypeRef results = NULL;
OSStatus status = SecItemCopyMatching_osx(query, &results);
if (status != errSecSuccess)
return status;
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;
}