1/*
2 * Copyright (c) 2006-2013 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24#include "SecBridge.h"
25#include "SecInternal.h"
26#include <CoreFoundation/CoreFoundation.h>
27#include <security_utilities/cfutilities.h>
28#include <Security/SecBase.h>
29#include <Security/SecKeychainItem.h>
30#include <Security/SecCertificate.h>
31#include <sys/param.h>
32#include "cssmdatetime.h"
33#include "SecItem.h"
34#include "SecItemPriv.h"
35#include "SecIdentitySearchPriv.h"
36#include "SecKeychainPriv.h"
37#include "SecCertificatePriv.h"
38#include "SecCertificatePrivP.h"
39#include "TrustAdditions.h"
40
41#include <AssertMacros.h>
42#include <syslog.h>
43
44#include <Security/SecTrustedApplication.h>
45#include <Security/SecTrustedApplicationPriv.h>
46#include <Security/SecCode.h>
47#include <Security/SecCodePriv.h>
48#include <Security/SecRequirement.h>
49
50const uint8_t kUUIDStringLength = 36;
51
52OSStatus SecItemAdd_osx(CFDictionaryRef attributes, CFTypeRef *result);
53OSStatus SecItemCopyMatching_osx(CFDictionaryRef query, CFTypeRef *result);
54OSStatus SecItemUpdate_osx(CFDictionaryRef query, CFDictionaryRef attributesToUpdate);
55OSStatus SecItemDelete_osx(CFDictionaryRef query);
56
57extern "C" {
58OSStatus SecItemAdd_ios(CFDictionaryRef attributes, CFTypeRef *result);
59OSStatus SecItemCopyMatching_ios(CFDictionaryRef query, CFTypeRef *result);
60OSStatus SecItemUpdate_ios(CFDictionaryRef query, CFDictionaryRef attributesToUpdate);
61OSStatus SecItemDelete_ios(CFDictionaryRef query);
62
63CFTypeRef SecItemCreateFromAttributeDictionary(CFDictionaryRef refAttributes);
64CFTypeRef SecItemCopyMergedResults(CFDictionaryRef query, CFTypeRef result_osx, CFTypeRef result_ios);
65OSStatus SecItemValidateAppleApplicationGroupAccess(CFStringRef group);
66CFDictionaryRef SecItemCopyTranslatedAttributes(CFDictionaryRef inOSXDict, CFTypeRef itemClass,
67	bool iOSOut, bool pruneMatch, bool pruneSync, bool pruneReturn, bool pruneData, bool pruneAccess);
68}
69
70static void secitemlog(int priority, const char *format, ...)
71{
72#ifndef NDEBUG
73	// log everything
74#else
75	if (priority < LOG_NOTICE) // log warnings and errors
76#endif
77	{
78		va_list list;
79		va_start(list, format);
80		vsyslog(priority, format, list);
81		va_end(list);
82	}
83}
84
85static void secitemshow(CFTypeRef obj, const char *context)
86{
87#ifndef NDEBUG
88	CFStringRef desc = CFCopyDescription(obj);
89	if (!desc) return;
90
91	CFIndex length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(desc), kCFStringEncodingUTF8) + 1;
92	char* buffer = (char*) malloc(length);
93	if (buffer) {
94		Boolean converted = CFStringGetCString(desc, buffer, length, kCFStringEncodingUTF8);
95		if (converted) {
96			const char *prefix = (context) ? context : "";
97			const char *separator = (context) ? " " : "";
98			secitemlog(LOG_NOTICE, "%s%s%s", prefix, separator, buffer);
99		}
100		free(buffer);
101	}
102	CFRelease(desc);
103#endif
104}
105
106
107#define CFDataGetBytePtrVoid CFDataGetBytePtr
108
109#pragma mark SecItem private utility functions
110
111/******************************************************************************/
112
113struct ProtocolAttributeInfo {
114	const CFTypeRef *protocolValue;
115	SecProtocolType protocolType;
116};
117
118static ProtocolAttributeInfo gProtocolTypes[] = {
119	{ &kSecAttrProtocolFTP, kSecProtocolTypeFTP },
120	{ &kSecAttrProtocolFTPAccount, kSecProtocolTypeFTPAccount },
121	{ &kSecAttrProtocolHTTP, kSecProtocolTypeHTTP },
122	{ &kSecAttrProtocolIRC, kSecProtocolTypeIRC },
123	{ &kSecAttrProtocolNNTP, kSecProtocolTypeNNTP },
124	{ &kSecAttrProtocolPOP3, kSecProtocolTypePOP3 },
125	{ &kSecAttrProtocolSMTP, kSecProtocolTypeSMTP },
126	{ &kSecAttrProtocolSOCKS, kSecProtocolTypeSOCKS },
127	{ &kSecAttrProtocolIMAP, kSecProtocolTypeIMAP },
128	{ &kSecAttrProtocolLDAP, kSecProtocolTypeLDAP },
129	{ &kSecAttrProtocolAppleTalk, kSecProtocolTypeAppleTalk },
130	{ &kSecAttrProtocolAFP, kSecProtocolTypeAFP },
131	{ &kSecAttrProtocolTelnet, kSecProtocolTypeTelnet },
132	{ &kSecAttrProtocolSSH, kSecProtocolTypeSSH },
133	{ &kSecAttrProtocolFTPS, kSecProtocolTypeFTPS },
134	{ &kSecAttrProtocolHTTPS, kSecProtocolTypeHTTPS },
135	{ &kSecAttrProtocolHTTPProxy, kSecProtocolTypeHTTPProxy },
136	{ &kSecAttrProtocolHTTPSProxy, kSecProtocolTypeHTTPSProxy },
137	{ &kSecAttrProtocolFTPProxy, kSecProtocolTypeFTPProxy },
138	{ &kSecAttrProtocolSMB, kSecProtocolTypeSMB },
139	{ &kSecAttrProtocolRTSP, kSecProtocolTypeRTSP },
140	{ &kSecAttrProtocolRTSPProxy, kSecProtocolTypeRTSPProxy },
141	{ &kSecAttrProtocolDAAP, kSecProtocolTypeDAAP },
142	{ &kSecAttrProtocolEPPC, kSecProtocolTypeEPPC },
143	{ &kSecAttrProtocolIPP, kSecProtocolTypeIPP },
144	{ &kSecAttrProtocolNNTPS, kSecProtocolTypeNNTPS },
145	{ &kSecAttrProtocolLDAPS, kSecProtocolTypeLDAPS },
146	{ &kSecAttrProtocolTelnetS, kSecProtocolTypeTelnetS },
147	{ &kSecAttrProtocolIMAPS, kSecProtocolTypeIMAPS },
148	{ &kSecAttrProtocolIRCS, kSecProtocolTypeIRCS },
149	{ &kSecAttrProtocolPOP3S, kSecProtocolTypePOP3S }
150};
151
152static const int kNumberOfProtocolTypes = sizeof(gProtocolTypes) / sizeof(ProtocolAttributeInfo);
153
154/*
155 * _SecProtocolTypeForSecAttrProtocol converts a SecAttrProtocol to a SecProtocolType.
156 */
157static SecProtocolType
158_SecProtocolTypeForSecAttrProtocol(
159	CFTypeRef protocol)
160{
161	SecProtocolType result = kSecProtocolTypeAny;
162
163	if (protocol != NULL) {
164		CFIndex count;
165		for (count=0; count<kNumberOfProtocolTypes; count++) {
166			if (CFEqual(protocol, *(gProtocolTypes[count].protocolValue))) {
167				result = gProtocolTypes[count].protocolType;
168				break;
169			}
170		}
171	}
172
173	return result;
174}
175
176/*
177 * _SecAttrProtocolForSecProtocolType converts a SecProtocolType to a SecAttrProtocol.
178 */
179static CFTypeRef
180_SecAttrProtocolForSecProtocolType(
181	SecProtocolType protocolType)
182{
183	CFTypeRef result = NULL;
184	CFIndex count;
185	for (count=0; count<kNumberOfProtocolTypes; count++) {
186		if (gProtocolTypes[count].protocolType == protocolType) {
187			result = *(gProtocolTypes[count].protocolValue);
188			break;
189		}
190	}
191
192	return result;
193}
194
195
196/******************************************************************************/
197
198struct AuthenticationAttributeInfo {
199	const CFTypeRef *authValue;
200	SecAuthenticationType authType;
201};
202
203static AuthenticationAttributeInfo gAuthTypes[] = {
204	{ &kSecAttrAuthenticationTypeNTLM, kSecAuthenticationTypeNTLM },
205	{ &kSecAttrAuthenticationTypeMSN, kSecAuthenticationTypeMSN },
206	{ &kSecAttrAuthenticationTypeDPA, kSecAuthenticationTypeDPA },
207	{ &kSecAttrAuthenticationTypeRPA, kSecAuthenticationTypeRPA },
208	{ &kSecAttrAuthenticationTypeHTTPBasic, kSecAuthenticationTypeHTTPBasic },
209	{ &kSecAttrAuthenticationTypeHTTPDigest, kSecAuthenticationTypeHTTPDigest },
210	{ &kSecAttrAuthenticationTypeHTMLForm, kSecAuthenticationTypeHTMLForm },
211	{ &kSecAttrAuthenticationTypeDefault, kSecAuthenticationTypeDefault }
212};
213
214static const int kNumberOfAuthenticationTypes = sizeof(gAuthTypes) / sizeof(AuthenticationAttributeInfo);
215
216/*
217 * _SecAuthenticationTypeForSecAttrAuthenticationType converts a
218 * SecAttrAuthenticationType to a SecAuthenticationType.
219 */
220static SecAuthenticationType
221_SecAuthenticationTypeForSecAttrAuthenticationType(
222	CFTypeRef authenticationType)
223{
224	SecAuthenticationType result = kSecAuthenticationTypeAny;
225
226	if (authenticationType != NULL) {
227		CFIndex count;
228		for (count=0; count<kNumberOfAuthenticationTypes; count++) {
229			if (CFEqual(authenticationType, *(gAuthTypes[count].authValue))) {
230				result = gAuthTypes[count].authType;
231				break;
232			}
233		}
234	}
235
236	return result;
237}
238
239/*
240 * _SecAttrAuthenticationTypeForSecAuthenticationType converts a SecAuthenticationType
241 * to a SecAttrAuthenticationType.
242 */
243static CFTypeRef
244_SecAttrAuthenticationTypeForSecAuthenticationType(
245	SecAuthenticationType authenticationType)
246{
247	CFTypeRef result = NULL;
248	CFIndex count;
249	for (count=0; count<kNumberOfAuthenticationTypes; count++) {
250		if (gAuthTypes[count].authType == authenticationType) {
251			result = *(gAuthTypes[count].authValue);
252			break;
253		}
254	}
255
256	return result;
257}
258
259
260/******************************************************************************/
261
262struct KeyAlgorithmInfo {
263	const CFTypeRef *keyType;
264	UInt32 keyValue;
265};
266
267static KeyAlgorithmInfo gKeyTypes[] = {
268	{ &kSecAttrKeyTypeRSA, CSSM_ALGID_RSA },
269	{ &kSecAttrKeyTypeDSA, CSSM_ALGID_DSA },
270	{ &kSecAttrKeyTypeAES, CSSM_ALGID_AES },
271	{ &kSecAttrKeyTypeDES, CSSM_ALGID_DES },
272	{ &kSecAttrKeyType3DES, CSSM_ALGID_3DES },
273	{ &kSecAttrKeyTypeRC4, CSSM_ALGID_RC4 },
274	{ &kSecAttrKeyTypeRC2, CSSM_ALGID_RC2 },
275	{ &kSecAttrKeyTypeCAST, CSSM_ALGID_CAST },
276	{ &kSecAttrKeyTypeECDSA, CSSM_ALGID_ECDSA },
277	{ &kSecAttrKeyTypeEC, CSSM_ALGID_ECDSA }
278};
279
280static const int kNumberOfKeyTypes = sizeof(gKeyTypes) / sizeof (KeyAlgorithmInfo);
281
282
283static UInt32 _SecAlgorithmTypeFromSecAttrKeyType(
284	CFTypeRef keyTypeRef)
285{
286	UInt32 keyAlgValue = 0;
287	if (CFStringGetTypeID() != CFGetTypeID(keyTypeRef))
288		return keyAlgValue;
289
290	int ix;
291	for (ix=0; ix<kNumberOfKeyTypes; ix++) {
292		if (CFEqual(keyTypeRef, *(gKeyTypes[ix].keyType))) {
293			keyAlgValue = gKeyTypes[ix].keyValue;
294			return keyAlgValue;
295		}
296	}
297
298	//%%%TODO try to convert the input string to a number here
299
300	return keyAlgValue;
301}
302
303
304enum ItemRepresentation
305{
306	kStringRepresentation,
307	kDataRepresentation,
308	kNumberRepresentation,
309	kBooleanRepresentation,
310	kDateRepresentation
311};
312
313
314struct InternalAttributeListInfo
315{
316	UInt32 oldItemType;
317	const CFTypeRef *newItemType;
318	ItemRepresentation itemRepresentation;
319};
320
321
322static InternalAttributeListInfo gGenericPasswordAttributes[] =
323{
324	{ kSecCreationDateItemAttr, &kSecAttrCreationDate, kDateRepresentation },
325	{ kSecModDateItemAttr, &kSecAttrModificationDate, kDateRepresentation },
326	{ kSecDescriptionItemAttr, &kSecAttrDescription, kStringRepresentation },
327	{ kSecCommentItemAttr, &kSecAttrComment, kStringRepresentation },
328	{ kSecCreatorItemAttr, &kSecAttrCreator, kNumberRepresentation }, // UInt32, a.k.a. FourCharCode
329	{ kSecTypeItemAttr, &kSecAttrType, kNumberRepresentation }, // UInt32, a.k.a. FourCharCode
330	{ kSecLabelItemAttr, &kSecAttrLabel, kStringRepresentation },
331	{ kSecInvisibleItemAttr, &kSecAttrIsInvisible, kBooleanRepresentation },
332	{ kSecNegativeItemAttr, &kSecAttrIsNegative, kBooleanRepresentation },
333	{ kSecAccountItemAttr, &kSecAttrAccount, kStringRepresentation },
334	{ kSecServiceItemAttr, &kSecAttrService, kStringRepresentation },
335	{ kSecGenericItemAttr, &kSecAttrGeneric, kDataRepresentation }
336};
337
338static const int kNumberOfGenericPasswordAttributes = sizeof(gGenericPasswordAttributes) / sizeof (InternalAttributeListInfo);
339
340#if 0
341static InternalAttributeListInfo gInternetPasswordAttributes[] =
342{
343	{ kSecCreationDateItemAttr, &kSecAttrCreationDate, kDateRepresentation },
344	{ kSecModDateItemAttr, &kSecAttrModificationDate, kDateRepresentation },
345	{ kSecDescriptionItemAttr, &kSecAttrDescription, kStringRepresentation },
346	{ kSecCommentItemAttr, &kSecAttrComment, kStringRepresentation },
347	{ kSecCreatorItemAttr, &kSecAttrCreator, kNumberRepresentation }, // UInt32, a.k.a. FourCharCode
348	{ kSecTypeItemAttr, &kSecAttrType, kNumberRepresentation }, // UInt32, a.k.a. FourCharCode
349	{ kSecLabelItemAttr, &kSecAttrLabel, kStringRepresentation },
350	{ kSecInvisibleItemAttr, &kSecAttrIsInvisible, kBooleanRepresentation },
351	{ kSecNegativeItemAttr, &kSecAttrIsNegative, kBooleanRepresentation },
352	{ kSecAccountItemAttr, &kSecAttrAccount, kStringRepresentation },
353	{ kSecSecurityDomainItemAttr, &kSecAttrSecurityDomain, kStringRepresentation },
354	{ kSecServerItemAttr, &kSecAttrServer, kStringRepresentation },
355	{ kSecAuthenticationTypeItemAttr, &kSecAttrAuthenticationType, kStringRepresentation }, // maps from UInt32 value to string constant
356	{ kSecPortItemAttr, &kSecAttrPort, kNumberRepresentation },
357	{ kSecPathItemAttr, &kSecAttrPath, kStringRepresentation }
358};
359
360static const int kNumberOfInternetPasswordAttributes = sizeof(gInternetPasswordAttributes) / sizeof (InternalAttributeListInfo);
361#endif
362
363static InternalAttributeListInfo gCertificateAttributes[] =
364{
365	{ kSecLabelItemAttr, &kSecAttrLabel, kStringRepresentation },
366	{ kSecSubjectItemAttr, &kSecAttrSubject, kDataRepresentation },
367	{ kSecIssuerItemAttr, &kSecAttrIssuer, kDataRepresentation },
368	{ kSecSerialNumberItemAttr, &kSecAttrSerialNumber, kDataRepresentation },
369	{ kSecPublicKeyHashItemAttr, &kSecAttrPublicKeyHash, kDataRepresentation },
370	{ kSecSubjectKeyIdentifierItemAttr, &kSecAttrSubjectKeyID, kDataRepresentation },
371	{ kSecCertTypeItemAttr, &kSecAttrCertificateType, kDataRepresentation },
372	{ kSecCertEncodingItemAttr, &kSecAttrCertificateEncoding, kDataRepresentation }
373};
374
375static const int kNumberOfCertificateAttributes = sizeof(gCertificateAttributes) / sizeof(InternalAttributeListInfo);
376
377
378static InternalAttributeListInfo gKeyAttributes[] =
379{
380    { kSecKeyKeyClass, &kSecAttrKeyClass, kStringRepresentation }, // key class maps from UInt32 value to string constant
381    { kSecKeyPrintName, &kSecAttrLabel, kStringRepresentation }, // note that "print name" maps to the user-visible label
382//  { kSecKeyAlias, /* not yet exposed by SecItem */, kDataRepresentation },
383    { kSecKeyPermanent, &kSecAttrIsPermanent, kBooleanRepresentation },
384//  { kSecKeyPrivate, /* not yet exposed by SecItem */, kBooleanRepresentation },
385//  { kSecKeyModifiable, /* not yet exposed by SecItem */, kBooleanRepresentation },
386    { kSecKeyLabel, &kSecAttrApplicationLabel, kDataRepresentation }, // this contains the hash of the key (or the public key hash, if asymmetric) as a CFData. Legacy keys may contain a UUID as a CFString
387    { kSecKeyApplicationTag, &kSecAttrApplicationTag, kDataRepresentation },
388//  { kSecKeyKeyCreator, /* not yet exposed by SecItem */, kStringRepresentation }, // this is the GUID of the CSP that owns this key
389    { kSecKeyKeyType, &kSecAttrKeyType, kStringRepresentation }, // algorithm type is given as a string constant (e.g. kSecAttrKeyTypeAES)
390    { kSecKeyKeySizeInBits, &kSecAttrKeySizeInBits, kNumberRepresentation },
391    { kSecKeyEffectiveKeySize, &kSecAttrEffectiveKeySize, kNumberRepresentation },
392//  { kSecKeyStartDate, /* not yet exposed by SecItem */, kDateRepresentation },
393//  { kSecKeyEndDate, /* not yet exposed by SecItem */, kDateRepresentation },
394//  { kSecKeySensitive, /* not yet exposed by SecItem */, kBooleanRepresentation },
395//  { kSecKeyAlwaysSensitive, /* not yet exposed by SecItem */, kBooleanRepresentation },
396//  { kSecKeyExtractable, /* not yet exposed by SecItem */, kBooleanRepresentation },
397//  { kSecKeyNeverExtractable, /* not yet exposed by SecItem */, kBooleanRepresentation },
398    { kSecKeyEncrypt, &kSecAttrCanEncrypt, kBooleanRepresentation },
399    { kSecKeyDecrypt, &kSecAttrCanDecrypt, kBooleanRepresentation },
400    { kSecKeyDerive, &kSecAttrCanDerive, kBooleanRepresentation },
401    { kSecKeySign, &kSecAttrCanSign, kBooleanRepresentation },
402    { kSecKeyVerify, &kSecAttrCanVerify, kBooleanRepresentation },
403//  { kSecKeySignRecover, /* not yet exposed by SecItem */, kBooleanRepresentation },
404//  { kSecKeyVerifyRecover, /* not yet exposed by SecItem */, kBooleanRepresentation },
405    { kSecKeyWrap, &kSecAttrCanWrap, kBooleanRepresentation },
406    { kSecKeyUnwrap, &kSecAttrCanUnwrap, kBooleanRepresentation }
407};
408
409static const int kNumberOfKeyAttributes = sizeof(gKeyAttributes) / sizeof(InternalAttributeListInfo);
410
411
412static void* CloneDataByType(ItemRepresentation type, CFTypeRef value, UInt32& length)
413{
414	switch (type)
415	{
416		case kStringRepresentation:
417		{
418			if (CFStringGetTypeID() != CFGetTypeID(value)) {
419				length = 0;
420				return NULL;
421			}
422			CFIndex maxLength = CFStringGetMaximumSizeForEncoding(CFStringGetLength((CFStringRef) value), kCFStringEncodingUTF8) + 1;
423			char* buffer = (char*) malloc(maxLength);
424			Boolean converted = CFStringGetCString((CFStringRef) value, buffer, maxLength, kCFStringEncodingUTF8);
425			if (converted) {
426				length = (UInt32)strlen(buffer);
427			}
428			else {
429				length = 0;
430				free(buffer);
431				buffer = NULL;
432			}
433			return buffer;
434		}
435
436		case kDataRepresentation:
437		{
438			if (CFStringGetTypeID() == CFGetTypeID(value)) {
439                // We may have a string here, since the key label may be a GUID for the symmetric keys
440                CFIndex maxLength = CFStringGetMaximumSizeForEncoding(CFStringGetLength((CFStringRef) value), kCFStringEncodingUTF8) + 1;
441                char* buffer = (char*) malloc(maxLength);
442                Boolean converted = CFStringGetCString((CFStringRef) value, buffer, maxLength, kCFStringEncodingUTF8);
443                if (converted) {
444                    length = (UInt32)strlen(buffer);
445                }
446                else {
447                    length = 0;
448                    free(buffer);
449                    buffer = NULL;
450                }
451                return buffer;
452			}
453
454			if (CFDataGetTypeID() != CFGetTypeID(value)) {
455				length = 0;
456				return NULL;
457			}
458			length = (UInt32)CFDataGetLength((CFDataRef) value);
459			uint8_t* buffer = (uint8_t*) malloc(length);
460			CFDataGetBytes((CFDataRef) value, CFRangeMake(0, length), buffer);
461			return buffer;
462		}
463
464		case kNumberRepresentation:
465		{
466			if (CFNumberGetTypeID() != CFGetTypeID(value)) {
467				length = 0;
468				return NULL;
469			}
470			uint32_t* buffer = (uint32_t*) malloc(sizeof(uint32_t));
471			Boolean converted = CFNumberGetValue((CFNumberRef) value, kCFNumberSInt32Type, buffer);
472			if (converted) {
473				length = sizeof(uint32_t);
474			}
475			else {
476				length = 0;
477				free(buffer);
478				buffer = NULL;
479			}
480			return buffer;
481		}
482
483		case kBooleanRepresentation:
484		{
485			if (CFBooleanGetTypeID() != CFGetTypeID(value)) {
486				length = 0;
487				return NULL;
488			}
489			uint32_t* buffer = (uint32_t*) malloc(sizeof(uint32_t));
490			*buffer = (CFEqual(kCFBooleanTrue, value)) ? 1 : 0;
491			length = sizeof(uint32_t);
492			return buffer;
493		}
494
495		case kDateRepresentation:
496		{
497			if (CFDateGetTypeID() != CFGetTypeID(value)) {
498				length = 0;
499				return NULL;
500			}
501			char* buffer = (char*) calloc(1, 32); // max length of a CSSM date string
502			CSSMDateTimeUtils::CFDateToCssmDate((CFDateRef) value, buffer);
503			length = (UInt32)strlen(buffer);
504			return buffer;
505		}
506
507		default:
508		{
509			length = 0;
510			return NULL;
511		}
512	}
513}
514
515
516static OSStatus
517_ConvertNewFormatToOldFormat(
518	CFAllocatorRef allocator,
519	const InternalAttributeListInfo* info,
520	int infoNumItems,
521	CFDictionaryRef dictionaryRef,
522	SecKeychainAttributeList* &attrList
523	)
524{
525	// get the keychain attributes array from the data item
526	// here's the problem.  On the one hand, we have a dictionary that is purported to contain
527	// attributes for our type.  On the other hand, the dictionary may contain items we don't support,
528	// and we therefore don't know how many attributes we will have unless we count them first
529
530	// setup the return
531	attrList = (SecKeychainAttributeList*) calloc(1, sizeof(SecKeychainAttributeList));
532
533	// make storage to extract the dictionary items
534	CFIndex itemsInDictionary = CFDictionaryGetCount(dictionaryRef);
535	CFTypeRef keys[itemsInDictionary];
536	CFTypeRef values[itemsInDictionary];
537
538	CFTypeRef *keysPtr = keys;
539	CFTypeRef *valuesPtr = values;
540
541	CFDictionaryGetKeysAndValues(dictionaryRef, keys, values);
542
543	// count the number of items we are interested in
544	CFIndex count = 0;
545	CFIndex i;
546
547	// since this is one of those nasty order n^2 loops, we cache as much stuff as possible so that
548	// we don't pay the price for this twice
549	SecKeychainAttrType tags[itemsInDictionary];
550	ItemRepresentation types[itemsInDictionary];
551
552	for (i = 0; i < itemsInDictionary; ++i)
553	{
554		CFTypeRef key = keysPtr[i];
555
556		int j;
557		for (j = 0; j < infoNumItems; ++j)
558		{
559			if (CFEqual(*(info[j].newItemType), key))
560			{
561				tags[i] = info[j].oldItemType;
562				types[i] = info[j].itemRepresentation;
563				count += 1;
564				break;
565			}
566		}
567
568		if (j >= infoNumItems)
569		{
570			// if we got here, we aren't interested in this item.
571			valuesPtr[i] = NULL;
572		}
573	}
574
575	// now we can make the result array
576	attrList->count = (UInt32)count;
577	attrList->attr = (SecKeychainAttribute*) malloc(sizeof(SecKeychainAttribute) * count);
578
579	// fill out the array
580	int resultPointer = 0;
581	for (i = 0; i < itemsInDictionary; ++i)
582	{
583		if (values[i] != NULL)
584		{
585			attrList->attr[resultPointer].tag = tags[i];
586
587			// we have to clone the data pointer.  The caller will need to make sure to throw these away
588			// with _FreeAttrList when it is done...
589			attrList->attr[resultPointer].data = CloneDataByType(types[i], valuesPtr[i], attrList->attr[resultPointer].length);
590			resultPointer += 1;
591		}
592	}
593
594	return errSecSuccess;
595}
596
597
598
599static OSStatus
600_ConvertOldFormatToNewFormat(
601	CFAllocatorRef allocator,
602	const InternalAttributeListInfo* info,
603	int infoNumItems,
604	SecKeychainItemRef itemRef,
605	CFMutableDictionaryRef& dictionaryRef)
606{
607	SecKeychainAttributeList list;
608	list.count = infoNumItems;
609	list.attr = (SecKeychainAttribute*) calloc(infoNumItems, sizeof(SecKeychainAttribute));
610
611	// fill out the array.  We only need to fill in the tags, since calloc zeros what it returns
612	int i;
613	for (i = 0; i < infoNumItems; ++i)
614	{
615		list.attr[i].tag = info[i].oldItemType;
616	}
617
618	OSStatus result = SecKeychainItemCopyContent(itemRef, NULL, &list, NULL, NULL);
619	if (result != errSecSuccess)
620	{
621		dictionaryRef = NULL;
622		free(list.attr);
623		return result;
624	}
625
626	// create the dictionary
627	dictionaryRef = CFDictionaryCreateMutable(allocator, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
628
629	// add the pairs
630	for (i = 0; i < infoNumItems; ++i)
631	{
632		if (list.attr[i].data == NULL)
633			continue;
634
635		switch (info[i].itemRepresentation)
636		{
637			case kStringRepresentation:
638			{
639				CFStringRef stringRef;
640				if (info[i].oldItemType == kSecKeyKeyClass) {
641					// special case: kSecKeyKeyClass is a UInt32 value that maps to a CFStringRef constant
642					uint32_t keyRecordValue = *((uint32_t*)list.attr[i].data);
643					bool retainString = true;
644					switch (keyRecordValue) {
645						case CSSM_DL_DB_RECORD_PUBLIC_KEY :
646							stringRef = (CFStringRef) kSecAttrKeyClassPublic;
647							break;
648						case CSSM_DL_DB_RECORD_PRIVATE_KEY:
649							stringRef = (CFStringRef) kSecAttrKeyClassPrivate;
650							break;
651						case CSSM_DL_DB_RECORD_SYMMETRIC_KEY:
652							stringRef = (CFStringRef) kSecAttrKeyClassSymmetric;
653							break;
654						default:
655							stringRef = CFStringCreateWithFormat(allocator, NULL, CFSTR("%d"), keyRecordValue);
656							break;
657					}
658					if (stringRef) {
659						if (retainString) CFRetain(stringRef);
660						CFDictionaryAddValue(dictionaryRef, *(info[i].newItemType), stringRef);
661						CFRelease(stringRef);
662					}
663				}
664				else if (info[i].oldItemType == kSecKeyKeyType) {
665					// special case: kSecKeyKeyType is a UInt32 value that maps to a CFStringRef constant
666					uint32_t keyAlgValue = *((uint32_t*)list.attr[i].data);
667					bool retainString = true;
668					switch (keyAlgValue) {
669						case CSSM_ALGID_RSA :
670							stringRef = (CFStringRef) kSecAttrKeyTypeRSA;
671							break;
672						case CSSM_ALGID_DSA :
673							stringRef = (CFStringRef) kSecAttrKeyTypeDSA;
674							break;
675						case CSSM_ALGID_AES :
676							stringRef = (CFStringRef) kSecAttrKeyTypeAES;
677							break;
678						case CSSM_ALGID_DES :
679							stringRef = (CFStringRef) kSecAttrKeyTypeDES;
680							break;
681						case CSSM_ALGID_3DES :
682							stringRef = (CFStringRef) kSecAttrKeyType3DES;
683							break;
684						case CSSM_ALGID_RC4 :
685							stringRef = (CFStringRef) kSecAttrKeyTypeRC4;
686							break;
687						case CSSM_ALGID_RC2 :
688							stringRef = (CFStringRef) kSecAttrKeyTypeRC2;
689							break;
690						case CSSM_ALGID_CAST :
691							stringRef = (CFStringRef) kSecAttrKeyTypeCAST;
692							break;
693						case CSSM_ALGID_ECDSA :
694							stringRef = (CFStringRef) kSecAttrKeyTypeEC;
695							break;
696						default :
697							stringRef = CFStringCreateWithFormat(allocator, NULL, CFSTR("%d"), keyAlgValue);
698							retainString = false;
699							break;
700					}
701					if (stringRef) {
702						if (retainString) CFRetain(stringRef);
703						CFDictionaryAddValue(dictionaryRef, *(info[i].newItemType), stringRef);
704						CFRelease(stringRef);
705					}
706				}
707				else {
708					// normal case: attribute contains a string
709					stringRef = CFStringCreateWithBytes(allocator, (UInt8*)list.attr[i].data, list.attr[i].length, kCFStringEncodingUTF8, FALSE);
710					if (stringRef == NULL)
711						stringRef = (CFStringRef) CFRetain(kCFNull);
712					CFDictionaryAddValue(dictionaryRef, *(info[i].newItemType), stringRef);
713					CFRelease(stringRef);
714				}
715			}
716			break;
717
718			case kDataRepresentation:
719			{
720                if ((info[i].oldItemType == kSecKeyLabel) && (list.attr[i].length == kUUIDStringLength)) {
721					// It's possible that there could be a string here because the key label may have a UUID
722					CFStringRef stringRef = CFStringCreateWithBytes(allocator, (UInt8*)list.attr[i].data, list.attr[i].length, kCFStringEncodingUTF8, FALSE);
723					if (stringRef == NULL)
724						stringRef = (CFStringRef) CFRetain(kCFNull);
725					CFDictionaryAddValue(dictionaryRef, *(info[i].newItemType), stringRef);
726					CFRelease(stringRef);
727                    break;
728                }
729                CFDataRef dataRef = CFDataCreate(allocator, (UInt8*) list.attr[i].data, list.attr[i].length);
730                if (dataRef == NULL)
731                    dataRef = (CFDataRef) CFRetain(kCFNull);
732                CFDictionaryAddValue(dictionaryRef, *(info[i].newItemType), dataRef);
733                CFRelease(dataRef);
734			}
735			break;
736
737			case kNumberRepresentation:
738			{
739				CFNumberRef numberRef = CFNumberCreate(allocator, kCFNumberSInt32Type, list.attr[i].data);
740				if (numberRef == NULL)
741					numberRef = (CFNumberRef) CFRetain(kCFNull);
742				CFDictionaryAddValue(dictionaryRef, *(info[i].newItemType), numberRef);
743				CFRelease(numberRef);
744			}
745			break;
746
747			case kBooleanRepresentation:
748			{
749				uint32_t value = *((uint32_t*)list.attr[i].data);
750				CFBooleanRef boolRef = (value) ? kCFBooleanTrue : kCFBooleanFalse;
751				CFDictionaryAddValue(dictionaryRef, *(info[i].newItemType), boolRef);
752			}
753			break;
754
755			case kDateRepresentation:
756			{
757				CFDateRef dateRef = NULL;
758				CSSMDateTimeUtils::CssmDateStringToCFDate((const char *)list.attr[i].data, list.attr[i].length, &dateRef);
759				if (dateRef == NULL)
760					dateRef = (CFDateRef) CFRetain(kCFNull);
761				CFDictionaryAddValue(dictionaryRef, *(info[i].newItemType), dateRef);
762				CFRelease(dateRef);
763			}
764			break;
765		}
766	}
767
768	// cleanup
769	SecKeychainItemFreeContent(&list, NULL);
770	free(list.attr);
771
772	return result;
773}
774
775
776
777//
778/*
779 * _CreateAttributesDictionaryFromGenericPasswordItem creates a CFDictionaryRef using the
780 * attributes of item.
781 */
782static OSStatus
783_CreateAttributesDictionaryFromGenericPasswordItem(
784	CFAllocatorRef allocator,
785	SecKeychainItemRef item,
786	CFDictionaryRef *dictionary)
787{
788	// do the basic allocations
789	CFMutableDictionaryRef dict = NULL;
790	OSStatus result = _ConvertOldFormatToNewFormat(allocator, gGenericPasswordAttributes, kNumberOfGenericPasswordAttributes, item, dict);
791	if (result == errSecSuccess) // did we complete OK
792	{
793		CFDictionaryAddValue(dict, kSecClass, kSecClassGenericPassword);
794	}
795
796	*dictionary = dict;
797
798	return result;
799}
800
801
802
803/*
804 * _CreateAttributesDictionaryFromCertificateItem creates a CFDictionaryRef using the
805 * attributes of item.
806 */
807static OSStatus
808_CreateAttributesDictionaryFromCertificateItem(
809	CFAllocatorRef allocator,
810	SecKeychainItemRef item,
811	CFDictionaryRef *dictionary)
812{
813	// do the basic allocations
814	CFMutableDictionaryRef dict = NULL;
815	OSStatus result = _ConvertOldFormatToNewFormat(allocator, gCertificateAttributes, kNumberOfCertificateAttributes, item, dict);
816	if (result == errSecSuccess) // did we complete OK
817	{
818		CFDictionaryAddValue(dict, kSecClass, kSecClassCertificate);
819	}
820
821	*dictionary = dict;
822
823	return errSecSuccess;
824}
825
826/*
827 * _CreateAttributesDictionaryFromKeyItem creates a CFDictionaryRef using the
828 * attributes of item.
829 */
830static OSStatus
831_CreateAttributesDictionaryFromKeyItem(
832	CFAllocatorRef allocator,
833	SecKeychainItemRef item,
834	CFDictionaryRef *dictionary)
835{
836#if 0
837//%%%FIXME this ought to work, but the call to SecKeychainCopyContent in _ConvertOldFormatToNewFormat fails.
838// Need to rewrite _ConvertOldFormatToNewFormat so that it uses SecKeychainAttributeInfoForItemID and
839// SecKeychainItemCopyAttributesAndData to get the attributes, rather than SecKeychainCopyContent.
840
841	if (status) {
842		goto error_exit; // unable to get the attribute info (i.e. database schema)
843	}
844
845	status = SecKeychainItemCopyAttributesAndData(item, info, &itemClass, &attrList, NULL, NULL);
846
847	// do the basic allocations
848	CFMutableDictionaryRef dict = NULL;
849	OSStatus result = _ConvertOldFormatToNewFormat(allocator, gKeyAttributes, kNumberOfKeyAttributes, item, dict);
850	if (result == errSecSuccess) // did we complete OK
851	{
852		CFDictionaryAddValue(dict, kSecClass, kSecClassKey);
853	}
854
855	*dictionary = dict;
856
857	return errSecSuccess;
858#endif
859
860	CFMutableDictionaryRef dict = CFDictionaryCreateMutable(allocator, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
861	unsigned int ix;
862	SecItemClass itemClass = 0;
863	UInt32 itemID;
864	SecKeychainAttributeList *attrList = NULL;
865	SecKeychainAttributeInfo *info = NULL;
866	SecKeychainRef keychain = NULL;
867
868	OSStatus status = SecKeychainItemCopyAttributesAndData(item, NULL, &itemClass, NULL, NULL, NULL);
869	if (status) {
870		goto error_exit; // item must have an itemClass
871	}
872
873	switch (itemClass)
874	{
875    case kSecInternetPasswordItemClass:
876		itemID = CSSM_DL_DB_RECORD_INTERNET_PASSWORD;
877		break;
878    case kSecGenericPasswordItemClass:
879		itemID = CSSM_DL_DB_RECORD_GENERIC_PASSWORD;
880		break;
881    case 'ashp': /* kSecAppleSharePasswordItemClass */
882		itemID = CSSM_DL_DB_RECORD_APPLESHARE_PASSWORD;
883		break;
884	default:
885		itemID = itemClass;
886		break;
887	}
888
889	status = SecKeychainItemCopyKeychain(item, &keychain);
890	if (status) {
891		goto error_exit; // item must have a keychain, so we can get the attribute info for it
892	}
893
894	status = SecKeychainAttributeInfoForItemID(keychain, itemID, &info);
895	if (status) {
896		goto error_exit; // unable to get the attribute info (i.e. database schema)
897	}
898
899	status = SecKeychainItemCopyAttributesAndData(item, info, &itemClass, &attrList, NULL, NULL);
900	if (status) {
901		goto error_exit; // unable to get the attribute info (i.e. database schema)
902	}
903
904	for (ix = 0; ix < info->count; ++ix)
905	{
906		SecKeychainAttribute *attribute = &attrList->attr[ix];
907		if (!attribute->length && !attribute->data)
908			continue;
909
910		UInt32 j, count = kNumberOfKeyAttributes;
911		InternalAttributeListInfo *intInfo = NULL;
912		for (j=0; j<count; j++) {
913			if (gKeyAttributes[j].oldItemType == info->tag[ix]) {
914				intInfo = &gKeyAttributes[j];
915				break;
916			}
917		}
918		if (!intInfo)
919			continue;
920
921		switch (intInfo->itemRepresentation)
922		{
923			case kStringRepresentation:
924			{
925				CFStringRef stringRef;
926				if (intInfo->oldItemType == kSecKeyKeyClass) {
927					// special case: kSecKeyKeyClass is a UInt32 value that maps to a CFStringRef constant
928					UInt32 keyRecordValue = *((UInt32*)attribute->data);
929					bool retainString = true;
930					switch (keyRecordValue) {
931						case CSSM_DL_DB_RECORD_PUBLIC_KEY :
932							stringRef = (CFStringRef) kSecAttrKeyClassPublic;
933							break;
934						case CSSM_DL_DB_RECORD_PRIVATE_KEY:
935							stringRef = (CFStringRef) kSecAttrKeyClassPrivate;
936							break;
937						case CSSM_DL_DB_RECORD_SYMMETRIC_KEY:
938							stringRef = (CFStringRef) kSecAttrKeyClassSymmetric;
939							break;
940						default:
941							stringRef = CFStringCreateWithFormat(allocator, NULL, CFSTR("%u"), (unsigned int)keyRecordValue);
942							break;
943					}
944					if (stringRef) {
945						if (retainString) CFRetain(stringRef);
946						CFDictionaryAddValue(dict, *(intInfo->newItemType), stringRef);
947						CFRelease(stringRef);
948					}
949				}
950				else if (intInfo->oldItemType == kSecKeyKeyType) {
951					// special case: kSecKeyKeyType is a UInt32 value that maps to a CFStringRef constant
952					UInt32 keyAlgValue = *((UInt32*)attribute->data);
953					bool retainString = true;
954					switch (keyAlgValue) {
955						case CSSM_ALGID_RSA :
956							stringRef = (CFStringRef) kSecAttrKeyTypeRSA;
957							break;
958						case CSSM_ALGID_DSA :
959							stringRef = (CFStringRef) kSecAttrKeyTypeDSA;
960							break;
961						case CSSM_ALGID_AES :
962							stringRef = (CFStringRef) kSecAttrKeyTypeAES;
963							break;
964						case CSSM_ALGID_DES :
965							stringRef = (CFStringRef) kSecAttrKeyTypeDES;
966							break;
967						case CSSM_ALGID_3DES :
968							stringRef = (CFStringRef) kSecAttrKeyType3DES;
969							break;
970						case CSSM_ALGID_RC4 :
971							stringRef = (CFStringRef) kSecAttrKeyTypeRC4;
972							break;
973						case CSSM_ALGID_RC2 :
974							stringRef = (CFStringRef) kSecAttrKeyTypeRC2;
975							break;
976						case CSSM_ALGID_CAST :
977							stringRef = (CFStringRef) kSecAttrKeyTypeCAST;
978							break;
979						case CSSM_ALGID_ECDSA :
980							stringRef = (CFStringRef) kSecAttrKeyTypeEC;
981							break;
982						default :
983							stringRef = CFStringCreateWithFormat(allocator, NULL, CFSTR("%u"), (unsigned int)keyAlgValue);
984							retainString = false;
985							break;
986					}
987					if (stringRef) {
988						if (retainString) CFRetain(stringRef);
989						CFDictionaryAddValue(dict, *(intInfo->newItemType), stringRef);
990						CFRelease(stringRef);
991					}
992                }
993				else {
994					// normal case: attribute contains a string
995					stringRef = CFStringCreateWithBytes(allocator, (UInt8*)attribute->data, attribute->length, kCFStringEncodingUTF8, FALSE);
996					if (stringRef == NULL)
997						stringRef = (CFStringRef) CFRetain(kCFNull);
998					CFDictionaryAddValue(dict, *(intInfo->newItemType), stringRef);
999					CFRelease(stringRef);
1000				}
1001			}
1002			break;
1003
1004			case kDataRepresentation:
1005			{
1006                if ((intInfo->oldItemType == kSecKeyLabel) && (attribute->length == kUUIDStringLength)) {
1007					// It's possible that there could be a string here because the key label may have a UUID
1008                    CFStringRef stringRef = CFStringCreateWithBytes(allocator, (UInt8*)attribute->data, attribute->length, kCFStringEncodingUTF8, FALSE);
1009					if (stringRef == NULL)
1010						stringRef = (CFStringRef) CFRetain(kCFNull);
1011					CFDictionaryAddValue(dict, *(intInfo->newItemType), stringRef);
1012					CFRelease(stringRef);
1013                    break;
1014                }
1015
1016				CFDataRef dataRef = CFDataCreate(allocator, (UInt8*)attribute->data, attribute->length);
1017				if (dataRef == NULL)
1018					dataRef = (CFDataRef) CFRetain(kCFNull);
1019				CFDictionaryAddValue(dict, *(intInfo->newItemType), dataRef);
1020				CFRelease(dataRef);
1021			}
1022			break;
1023
1024			case kNumberRepresentation:
1025			{
1026				CFNumberRef numberRef = CFNumberCreate(allocator, kCFNumberSInt32Type, attribute->data);
1027				if (numberRef == NULL)
1028					numberRef = (CFNumberRef) CFRetain(kCFNull);
1029				CFDictionaryAddValue(dict, *(intInfo->newItemType), numberRef);
1030				CFRelease(numberRef);
1031			}
1032			break;
1033
1034			case kBooleanRepresentation:
1035			{
1036				UInt32 value = *((UInt32*)attribute->data);
1037				CFBooleanRef boolRef = (value) ? kCFBooleanTrue : kCFBooleanFalse;
1038				CFDictionaryAddValue(dict, *(intInfo->newItemType), boolRef);
1039			}
1040			break;
1041
1042			case kDateRepresentation:
1043			{
1044				//%%% FIXME need to convert from a CSSM date string to a CFDateRef here
1045				CFDateRef dateRef = NULL;
1046				if (dateRef == NULL)
1047					dateRef = (CFDateRef) CFRetain(kCFNull);
1048				CFDictionaryAddValue(dict, *(intInfo->newItemType), dateRef);
1049				CFRelease(dateRef);
1050			}
1051			break;
1052		}
1053	}
1054
1055	CFDictionaryAddValue(dict, kSecClass, kSecClassKey);
1056
1057error_exit:
1058
1059	if (attrList)
1060		SecKeychainItemFreeAttributesAndData(attrList, NULL);
1061
1062	if (info)
1063		SecKeychainFreeAttributeInfo(info);
1064
1065	if (keychain)
1066		CFRelease(keychain);
1067
1068	*dictionary = dict;
1069
1070	return status;
1071}
1072
1073
1074/*
1075 * _CreateAttributesDictionaryFromInternetPasswordItem creates a CFDictionaryRef using the
1076 * attributes of item.
1077 */
1078static OSStatus
1079_CreateAttributesDictionaryFromInternetPasswordItem(
1080	CFAllocatorRef allocator,
1081	SecKeychainItemRef item,
1082	CFDictionaryRef *dictionary)
1083{
1084	OSStatus status;
1085	SecKeychainAttribute attr[] = {
1086		{ kSecServerItemAttr, 0, NULL },		/* [0] server */
1087		{ kSecSecurityDomainItemAttr, 0, NULL },	/* [1] securityDomain */
1088		{ kSecAccountItemAttr, 0, NULL },		/* [2] account */
1089		{ kSecPathItemAttr, 0, NULL },			/* [3] path */
1090		{ kSecPortItemAttr, 0, NULL },			/* [4] port */
1091		{ kSecProtocolItemAttr, 0, NULL },		/* [5] protocol */
1092		{ kSecAuthenticationTypeItemAttr, 0, NULL },	/* [6] authenticationType */
1093		{ kSecCommentItemAttr, 0, NULL },		/* [7] comment */
1094		{ kSecDescriptionItemAttr, 0, NULL },		/* [8] description */
1095		{ kSecLabelItemAttr, 0, NULL },			/* [9] label */
1096		{ kSecCreationDateItemAttr, 0, NULL },	/* [10] creation date */
1097		{ kSecModDateItemAttr, 0, NULL },		/* [11] modification date */
1098		{ kSecCreatorItemAttr, 0, NULL },		/* [12] creator */
1099		{ kSecTypeItemAttr, 0, NULL },			/* [13] type */
1100		{ kSecInvisibleItemAttr, 0, NULL },		/* [14] invisible */
1101		{ kSecNegativeItemAttr, 0, NULL },		/* [15] negative */
1102	};
1103	SecKeychainAttributeList attrList = { sizeof(attr) / sizeof(SecKeychainAttribute), attr };
1104	CFIndex numValues;
1105	CFIndex index;
1106	CFTypeRef keys[(sizeof(attr) / sizeof(SecKeychainAttribute)) + 2];
1107	CFTypeRef values[(sizeof(attr) / sizeof(SecKeychainAttribute)) + 2];
1108
1109	*dictionary = NULL;
1110
1111	// copy the item's attributes
1112	status = SecKeychainItemCopyContent(item, NULL, &attrList, NULL, NULL);
1113	require_noerr(status, SecKeychainItemCopyContent_failed);
1114
1115	numValues = 0;
1116
1117	// add kSecClass
1118	keys[numValues] = kSecClass;
1119	values[numValues] = kSecClassInternetPassword;
1120	++numValues;
1121
1122	// add kSecAttrServer
1123	if ( attrList.attr[0].length > 0 ) {
1124		keys[numValues] = kSecAttrServer;
1125		values[numValues] = CFStringCreateWithBytes(allocator, (UInt8 *)attrList.attr[0].data, attrList.attr[0].length, kCFStringEncodingUTF8, FALSE);
1126		if ( values[numValues] != NULL ) {
1127			++numValues;
1128		}
1129	}
1130
1131	// add kSecAttrSecurityDomain
1132	if ( attrList.attr[1].length > 0 ) {
1133		keys[numValues] = kSecAttrSecurityDomain;
1134		values[numValues] = CFStringCreateWithBytes(allocator, (UInt8 *)attrList.attr[1].data, attrList.attr[1].length, kCFStringEncodingUTF8, FALSE);
1135		if ( values[numValues] != NULL ) {
1136			++numValues;
1137		}
1138	}
1139
1140	// add kSecAttrAccount
1141	if ( attrList.attr[2].length > 0 ) {
1142		keys[numValues] = kSecAttrAccount;
1143		values[numValues] = CFStringCreateWithBytes(allocator, (UInt8 *)attrList.attr[2].data, attrList.attr[2].length, kCFStringEncodingUTF8, FALSE);
1144		if ( values[numValues] != NULL ) {
1145			++numValues;
1146		}
1147	}
1148
1149	// add kSecAttrPath
1150	if ( attrList.attr[3].length > 0 ) {
1151		keys[numValues] = kSecAttrPath;
1152		values[numValues] = CFStringCreateWithBytes(allocator, (UInt8 *)attrList.attr[3].data, attrList.attr[3].length, kCFStringEncodingUTF8, FALSE);
1153		if ( values[numValues] != NULL ) {
1154			++numValues;
1155		}
1156	}
1157
1158	// add kSecAttrPort
1159	if ( attrList.attr[4].length > 0 ) {
1160		keys[numValues] = kSecAttrPort;
1161		values[numValues] = CFNumberCreate(allocator, kCFNumberSInt32Type, attrList.attr[4].data);
1162		if ( values[numValues] != NULL ) {
1163			++numValues;
1164		}
1165	}
1166
1167	// add kSecAttrProtocol
1168	if ( attrList.attr[5].length > 0 ) {
1169		keys[numValues] = kSecAttrProtocol;
1170		values[numValues] = _SecAttrProtocolForSecProtocolType(*(SecProtocolType*)attrList.attr[5].data);
1171		if ( values[numValues] != NULL ) {
1172			CFRetain(values[numValues]);
1173			++numValues;
1174		}
1175	}
1176
1177	// add kSecAttrAuthenticationType
1178	if ( attrList.attr[6].length > 0 ) {
1179		keys[numValues] = kSecAttrAuthenticationType;
1180		values[numValues] = _SecAttrAuthenticationTypeForSecAuthenticationType(*(SecProtocolType*)attrList.attr[6].data);
1181		if ( values[numValues] != NULL ) {
1182			CFRetain(values[numValues]);
1183			++numValues;
1184		}
1185	}
1186
1187	// add kSecAttrComment
1188	if ( attrList.attr[7].length > 0 ) {
1189		keys[numValues] = kSecAttrComment;
1190		values[numValues] = CFStringCreateWithBytes(allocator, (UInt8 *)attrList.attr[7].data, attrList.attr[7].length, kCFStringEncodingUTF8, FALSE);
1191		if ( values[numValues] != NULL ) {
1192			++numValues;
1193		}
1194	}
1195
1196	// add kSecAttrDescription
1197	if ( attrList.attr[8].length > 0 ) {
1198		keys[numValues] = kSecAttrDescription;
1199		values[numValues] = CFStringCreateWithBytes(allocator, (UInt8 *)attrList.attr[8].data, attrList.attr[8].length, kCFStringEncodingUTF8, FALSE);
1200		if ( values[numValues] != NULL ) {
1201			++numValues;
1202		}
1203	}
1204
1205	// add kSecAttrLabel
1206	if ( attrList.attr[9].length > 0 ) {
1207		keys[numValues] = kSecAttrLabel;
1208		values[numValues] = CFStringCreateWithBytes(allocator, (UInt8 *)attrList.attr[9].data, attrList.attr[9].length, kCFStringEncodingUTF8, FALSE);
1209		if ( values[numValues] != NULL ) {
1210			++numValues;
1211		}
1212	}
1213
1214	// add kSecAttrCreationDate
1215	if ( attrList.attr[10].length > 0 ) {
1216		CFDateRef creationDate = NULL;
1217		CSSMDateTimeUtils::CssmDateStringToCFDate((const char *)attrList.attr[10].data, attrList.attr[10].length, &creationDate);
1218		keys[numValues] = kSecAttrCreationDate;
1219		values[numValues] = creationDate;
1220		if ( values[numValues] != NULL ) {
1221			++numValues;
1222		}
1223	}
1224
1225	// add kSecAttrModificationDate
1226	if ( attrList.attr[11].length > 0 ) {
1227		CFDateRef modDate = NULL;
1228		CSSMDateTimeUtils::CssmDateStringToCFDate((const char *)attrList.attr[11].data, attrList.attr[11].length, &modDate);
1229		keys[numValues] = kSecAttrModificationDate;
1230		values[numValues] = modDate;
1231		if ( values[numValues] != NULL ) {
1232			++numValues;
1233		}
1234	}
1235
1236	// add kSecCreatorItemAttr
1237	if ( attrList.attr[12].length > 0 ) {
1238		CFNumberRef numberRef = CFNumberCreate(allocator, kCFNumberSInt32Type, attrList.attr[12].data);
1239		keys[numValues] = kSecAttrCreator;
1240		values[numValues] = numberRef;
1241		if ( values[numValues] != NULL ) {
1242			CFRetain(values[numValues]);
1243			++numValues;
1244		}
1245	}
1246
1247	// add kSecTypeItemAttr
1248	if ( attrList.attr[13].length > 0 ) {
1249		CFNumberRef numberRef = CFNumberCreate(allocator, kCFNumberSInt32Type, attrList.attr[13].data);
1250		keys[numValues] = kSecAttrType;
1251		values[numValues] = numberRef;
1252		if ( values[numValues] != NULL ) {
1253			CFRetain(values[numValues]);
1254			++numValues;
1255		}
1256	}
1257
1258	// add kSecInvisibleItemAttr
1259	if ( attrList.attr[14].length > 0 ) {
1260		uint32_t value = *((uint32_t*)attrList.attr[14].data);
1261		CFBooleanRef boolRef = (value) ? kCFBooleanTrue : kCFBooleanFalse;
1262		keys[numValues] = kSecAttrIsInvisible;
1263		values[numValues] = boolRef;
1264		if ( values[numValues] != NULL ) {
1265			CFRetain(values[numValues]);
1266			++numValues;
1267		}
1268	}
1269
1270	// add kSecNegativeItemAttr
1271	if ( attrList.attr[15].length > 0 ) {
1272		uint32_t value = *((uint32_t*)attrList.attr[15].data);
1273		CFBooleanRef boolRef = (value) ? kCFBooleanTrue : kCFBooleanFalse;
1274		keys[numValues] = kSecAttrIsNegative;
1275		values[numValues] = boolRef;
1276		if ( values[numValues] != NULL ) {
1277			CFRetain(values[numValues]);
1278			++numValues;
1279		}
1280	}
1281
1282	// create the dictionary
1283	*dictionary = CFDictionaryCreate(allocator, keys, values, numValues, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1284
1285	// release the values added to the dictionary
1286	for ( index = 0; index < numValues; ++index )
1287	{
1288		CFRelease(values[index]);
1289	}
1290
1291	// and free the attributes
1292	(void) SecKeychainItemFreeContent(&attrList, NULL);
1293
1294SecKeychainItemCopyContent_failed:
1295
1296	return ( status );
1297}
1298
1299
1300/*
1301 * _CreateAttributesDictionaryFromItem creates a CFDictionaryRef using the
1302 * attributes of the specified item class and item.
1303 */
1304static OSStatus
1305_CreateAttributesDictionaryFromItem(
1306	CFAllocatorRef allocator,
1307	SecItemClass itemClass,
1308	SecKeychainItemRef item,
1309	CFDictionaryRef *dictionary)
1310{
1311	switch (itemClass)
1312	{
1313		case kSecInternetPasswordItemClass:
1314			return _CreateAttributesDictionaryFromInternetPasswordItem(allocator, item, dictionary);
1315
1316		case kSecGenericPasswordItemClass:
1317			return _CreateAttributesDictionaryFromGenericPasswordItem(allocator, item, dictionary);
1318
1319		case kSecCertificateItemClass:
1320			return _CreateAttributesDictionaryFromCertificateItem(allocator, item, dictionary);
1321
1322		case kSecPublicKeyItemClass:
1323		case kSecPrivateKeyItemClass:
1324		case kSecSymmetricKeyItemClass:
1325			return _CreateAttributesDictionaryFromKeyItem(allocator, item, dictionary);
1326
1327		default:
1328			*dictionary = NULL;
1329			break;
1330	}
1331	return errSecParam;
1332}
1333
1334
1335/*
1336 * _FreeAttrList frees the memory allocated for the SecKeychainAttributeList
1337 * by the _CreateSecKeychainAttributeListFromDictionary function.
1338 */
1339static void
1340_FreeAttrList(
1341	SecKeychainAttributeList *attrListPtr)
1342{
1343	UInt32 index;
1344
1345	if ( attrListPtr != NULL ) {
1346		if ( attrListPtr->attr != NULL ) {
1347			// free any attribute data
1348			for ( index = 0; index < attrListPtr->count; ++index ) {
1349				free(attrListPtr->attr[index].data);
1350			}
1351			// free the attribute array
1352			free(attrListPtr->attr);
1353		}
1354		// free the attribute list
1355		free(attrListPtr);
1356	}
1357}
1358
1359/*
1360 * _CFDataCreateAttribute initializes the SecKeychainAttribute pointed to by
1361 * attr using the data and tag parameters.
1362 *
1363 * The memory for the SecKeychainAttribute's data field is allocated with malloc
1364 * and must be released by the caller (this is normally done by calling _FreeAttrList).
1365 */
1366static OSStatus
1367_CFDataCreateAttribute(
1368	CFDataRef data,
1369	SecKeychainAttrType tag,
1370	SecKeychainAttributePtr attr)
1371{
1372	OSStatus status = errSecSuccess;
1373	CFRange range;
1374
1375	// set the attribute tag
1376	attr->tag = tag;
1377
1378	// determine the attribute length
1379	attr->length = (UInt32) CFDataGetLength(data);
1380	range = CFRangeMake(0, (CFIndex)attr->length);
1381
1382	// allocate memory for the attribute bytes
1383	attr->data = malloc(attr->length);
1384	require_action(attr->data != NULL, malloc_failed, status = errSecBufferTooSmall);
1385
1386	// get the attribute bytes
1387	CFDataGetBytes(data, range, (UInt8 *)attr->data);
1388
1389malloc_failed:
1390
1391	return ( status );
1392}
1393
1394/*
1395 * _CFStringCreateAttribute initializes the SecKeychainAttribute pointed to by
1396 * attr using the string and tag parameters.
1397 *
1398 * The memory for the SecKeychainAttribute's data field is allocated with malloc
1399 * and must be released by the caller (this is normally done by calling _FreeAttrList).
1400 */
1401static OSStatus
1402_CFStringCreateAttribute(
1403	CFStringRef string,
1404	SecKeychainAttrType tag,
1405	SecKeychainAttributePtr attr)
1406{
1407	OSStatus status = errSecSuccess;
1408	CFRange range;
1409
1410	// set the attribute tag
1411	attr->tag = tag;
1412
1413	// determine the attribute length
1414	range = CFRangeMake(0, CFStringGetLength(string));
1415	CFStringGetBytes(string, range, kCFStringEncodingUTF8, 0, FALSE, NULL, 0, (CFIndex *)&attr->length);
1416
1417	// allocate memory for the attribute bytes
1418	attr->data = malloc(attr->length);
1419	require_action(attr->data != NULL, malloc_failed, status = errSecBufferTooSmall);
1420
1421	// get the attribute bytes
1422	CFStringGetBytes(string, range, kCFStringEncodingUTF8, 0, FALSE, (UInt8 *)attr->data, attr->length, NULL);
1423
1424malloc_failed:
1425
1426	return ( status );
1427}
1428
1429
1430/*
1431 * _CreateSecKeychainGenericPasswordAttributeListFromDictionary creates a SecKeychainAttributeList
1432 * from the attribute key/values in attrDictionary.
1433 *
1434 * If this function returns errSecSuccess, the pointer to the SecKeychainAttributeList
1435 * must be freed by the caller with _FreeAttrList()
1436 */
1437static OSStatus
1438_CreateSecKeychainGenericPasswordAttributeListFromDictionary(
1439	CFDictionaryRef attrDictionary,
1440	SecKeychainAttributeList **attrList)
1441{
1442	return _ConvertNewFormatToOldFormat(NULL, gGenericPasswordAttributes, kNumberOfGenericPasswordAttributes, attrDictionary, *attrList);
1443}
1444
1445
1446/*
1447 * _CreateSecKeychainCertificateAttributeListFromDictionary creates a SecKeychainAttributeList
1448 * from the attribute key/values in attrDictionary.
1449 *
1450 * If this function returns errSecSuccess, the pointer to the SecKeychainAttributeList
1451 * must be freed by the caller with _FreeAttrList()
1452 */
1453static OSStatus
1454_CreateSecKeychainCertificateAttributeListFromDictionary(
1455	CFDictionaryRef attrDictionary,
1456	SecKeychainAttributeList **attrList)
1457{
1458	return _ConvertNewFormatToOldFormat(NULL, gCertificateAttributes, kNumberOfCertificateAttributes, attrDictionary, *attrList);
1459}
1460
1461
1462/*
1463 * _CreateSecKeychainKeyAttributeListFromDictionary creates a SecKeychainAttributeList
1464 * from the attribute key/values in attrDictionary.
1465 *
1466 * If this function returns errSecSuccess, the pointer to the SecKeychainAttributeList
1467 * must be freed by the caller with _FreeAttrList()
1468 */
1469static OSStatus
1470_CreateSecKeychainKeyAttributeListFromDictionary(
1471	CFDictionaryRef attrDictionary,
1472	SecKeychainAttributeList **attrList)
1473{
1474#if 0
1475	//%%%FIXME this function should work for key attributes, but currently doesn't; need to debug
1476	return _ConvertNewFormatToOldFormat(NULL, gKeyAttributes, kNumberOfKeyAttributes, attrDictionary, *attrList);
1477#else
1478	// explicitly build attribute list for supported key attributes
1479	// NOTE: this code supports only MaxSecKeyAttributes (15) attributes
1480	const int MaxSecKeyAttributes = 15;
1481
1482	OSStatus status;
1483	CFTypeRef value;
1484	SecKeychainAttributeList *attrListPtr;
1485
1486	attrListPtr = (SecKeychainAttributeList*)calloc(1, sizeof(SecKeychainAttributeList));
1487	require_action(attrListPtr != NULL, calloc_attrListPtr_failed, status = errSecBufferTooSmall);
1488
1489	attrListPtr->attr = (SecKeychainAttribute*)calloc(MaxSecKeyAttributes, sizeof(SecKeychainAttribute));
1490	require_action(attrListPtr->attr != NULL, malloc_attrPtr_failed, status = errSecBufferTooSmall);
1491
1492	// [0] get the kSecKeyKeyClass value
1493	if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrKeyClass, (const void **)&value) && value) {
1494		UInt32 keyRecordValue = 0;
1495		if (CFEqual(kSecAttrKeyClassPublic, value))
1496			keyRecordValue = CSSM_DL_DB_RECORD_PUBLIC_KEY;
1497		else if (CFEqual(kSecAttrKeyClassPrivate, value))
1498			keyRecordValue = CSSM_DL_DB_RECORD_PRIVATE_KEY;
1499		else if (CFEqual(kSecAttrKeyClassSymmetric, value))
1500			keyRecordValue = CSSM_DL_DB_RECORD_SYMMETRIC_KEY;
1501
1502		// only use this attribute if we recognize the value!
1503		if (keyRecordValue != 0) {
1504			attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32));
1505			require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_number_failed, status = errSecBufferTooSmall);
1506
1507			attrListPtr->attr[attrListPtr->count].tag = kSecKeyKeyClass;
1508			attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32);
1509			*((UInt32*)attrListPtr->attr[attrListPtr->count].data) = keyRecordValue;
1510
1511			++attrListPtr->count;
1512		}
1513	}
1514
1515	// [1] get the kSecKeyPrintName string
1516	if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrLabel, (const void **)&value) && value) {
1517		status = _CFStringCreateAttribute((CFStringRef)value, kSecKeyPrintName, &attrListPtr->attr[attrListPtr->count]);
1518		require_noerr_quiet(status, CFStringCreateAttribute_failed);
1519
1520		++attrListPtr->count;
1521	}
1522
1523	// [2] get the kSecKeyPermanent boolean
1524	if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrIsPermanent, (const void **)&value) && value) {
1525		attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32));
1526		require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_number_failed, status = errSecBufferTooSmall);
1527
1528		attrListPtr->attr[attrListPtr->count].tag = kSecKeyPermanent;
1529		attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32);
1530		*((UInt32*)attrListPtr->attr[attrListPtr->count].data) = (CFEqual(kCFBooleanTrue, value)) ? 1 : 0;
1531
1532		++attrListPtr->count;
1533	}
1534
1535	// [3] get the kSecKeyLabel string
1536	if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrApplicationLabel, (const void **)&value) && value) {
1537        if (CFStringGetTypeID() == CFGetTypeID(value))
1538            status = _CFStringCreateAttribute((CFStringRef)value, kSecKeyLabel, &attrListPtr->attr[attrListPtr->count]);
1539        else if (CFDataGetTypeID() == CFGetTypeID(value))
1540            status = _CFDataCreateAttribute((CFDataRef)value, kSecKeyLabel, &attrListPtr->attr[attrListPtr->count]);
1541        else
1542            status = errSecParam;
1543
1544		require_noerr_quiet(status, CFStringCreateAttribute_failed);
1545
1546		++attrListPtr->count;
1547	}
1548
1549	// [4] get the kSecKeyApplicationTag data
1550	if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrApplicationTag, (const void **)&value) && value) {
1551		if (CFStringGetTypeID() == CFGetTypeID(value))
1552			status = _CFStringCreateAttribute((CFStringRef)value, kSecKeyApplicationTag, &attrListPtr->attr[attrListPtr->count]);
1553		else if (CFDataGetTypeID() == CFGetTypeID(value))
1554			status = _CFDataCreateAttribute((CFDataRef)value, kSecKeyApplicationTag, &attrListPtr->attr[attrListPtr->count]);
1555		else
1556			status = errSecParam;
1557
1558		require_noerr_quiet(status, CFDataCreateAttribute_failed);
1559		++attrListPtr->count;
1560	}
1561
1562	// [5] get the kSecKeyKeyType number
1563	if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrKeyType, (const void **)&value) && value) {
1564		UInt32 keyAlgValue = _SecAlgorithmTypeFromSecAttrKeyType(kSecAttrKeyType);
1565		if (keyAlgValue != 0) {
1566			attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32));
1567			require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_number_failed, status = errSecBufferTooSmall);
1568
1569			attrListPtr->attr[attrListPtr->count].tag = kSecKeyKeyType;
1570			attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32);
1571			*((UInt32*)attrListPtr->attr[attrListPtr->count].data) = keyAlgValue;
1572
1573			++attrListPtr->count;
1574		}
1575	}
1576
1577	// [6] get the kSecKeyKeySizeInBits number
1578	if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrKeySizeInBits, (const void **)&value) && value) {
1579		if (CFNumberGetTypeID() == CFGetTypeID(value)) {
1580			attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32));
1581			require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_number_failed, status = errSecBufferTooSmall);
1582
1583			attrListPtr->attr[attrListPtr->count].tag = kSecKeyKeySizeInBits;
1584			attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32);
1585			CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, attrListPtr->attr[attrListPtr->count].data);
1586
1587			++attrListPtr->count;
1588		}
1589	}
1590
1591	// [7] get the kSecKeyEffectiveKeySize number
1592	if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrEffectiveKeySize, (const void **)&value) && value) {
1593		if (CFNumberGetTypeID() == CFGetTypeID(value)) {
1594			attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32));
1595			require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_number_failed, status = errSecBufferTooSmall);
1596
1597			attrListPtr->attr[attrListPtr->count].tag = kSecKeyEffectiveKeySize;
1598			attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32);
1599			CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, attrListPtr->attr[attrListPtr->count].data);
1600
1601			++attrListPtr->count;
1602		}
1603	}
1604
1605	// [8] get the kSecKeyEncrypt boolean
1606	if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrCanEncrypt, (const void **)&value) && value) {
1607		if (CFBooleanGetTypeID() == CFGetTypeID(value)) {
1608			attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32));
1609			require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_number_failed, status = errSecBufferTooSmall);
1610
1611			attrListPtr->attr[attrListPtr->count].tag = kSecKeyEncrypt;
1612			attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32);
1613			*((UInt32*)attrListPtr->attr[attrListPtr->count].data) = (CFEqual(kCFBooleanTrue, value)) ? 1 : 0;
1614
1615			++attrListPtr->count;
1616		}
1617	}
1618
1619	// [9] get the kSecKeyDecrypt boolean
1620	if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrCanDecrypt, (const void **)&value) && value) {
1621		if (CFBooleanGetTypeID() == CFGetTypeID(value)) {
1622			attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32));
1623			require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_number_failed, status = errSecBufferTooSmall);
1624
1625			attrListPtr->attr[attrListPtr->count].tag = kSecKeyDecrypt;
1626			attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32);
1627			*((UInt32*)attrListPtr->attr[attrListPtr->count].data) = (CFEqual(kCFBooleanTrue, value)) ? 1 : 0;
1628
1629			++attrListPtr->count;
1630		}
1631	}
1632
1633	// [10] get the kSecKeyDerive boolean
1634	if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrCanDerive, (const void **)&value) && value) {
1635		if (CFBooleanGetTypeID() == CFGetTypeID(value)) {
1636			attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32));
1637			require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_number_failed, status = errSecBufferTooSmall);
1638
1639			attrListPtr->attr[attrListPtr->count].tag = kSecKeyDerive;
1640			attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32);
1641			*((UInt32*)attrListPtr->attr[attrListPtr->count].data) = (CFEqual(kCFBooleanTrue, value)) ? 1 : 0;
1642
1643			++attrListPtr->count;
1644		}
1645	}
1646
1647	// [11] get the kSecKeySign boolean
1648	if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrCanSign, (const void **)&value) && value) {
1649		if (CFBooleanGetTypeID() == CFGetTypeID(value)) {
1650			attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32));
1651			require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_number_failed, status = errSecBufferTooSmall);
1652
1653			attrListPtr->attr[attrListPtr->count].tag = kSecKeySign;
1654			attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32);
1655			*((UInt32*)attrListPtr->attr[attrListPtr->count].data) = (CFEqual(kCFBooleanTrue, value)) ? 1 : 0;
1656
1657			++attrListPtr->count;
1658		}
1659	}
1660
1661	// [12] get the kSecKeyVerify boolean
1662	if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrCanVerify, (const void **)&value) && value) {
1663		if (CFBooleanGetTypeID() == CFGetTypeID(value)) {
1664			attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32));
1665			require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_number_failed, status = errSecBufferTooSmall);
1666
1667			attrListPtr->attr[attrListPtr->count].tag = kSecKeyVerify;
1668			attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32);
1669			*((UInt32*)attrListPtr->attr[attrListPtr->count].data) = (CFEqual(kCFBooleanTrue, value)) ? 1 : 0;
1670
1671			++attrListPtr->count;
1672		}
1673	}
1674
1675	// [13] get the kSecKeyWrap boolean
1676	if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrCanWrap, (const void **)&value) && value) {
1677		if (CFBooleanGetTypeID() == CFGetTypeID(value)) {
1678			attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32));
1679			require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_number_failed, status = errSecBufferTooSmall);
1680
1681			attrListPtr->attr[attrListPtr->count].tag = kSecKeyWrap;
1682			attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32);
1683			*((UInt32*)attrListPtr->attr[attrListPtr->count].data) = (CFEqual(kCFBooleanTrue, value)) ? 1 : 0;
1684
1685			++attrListPtr->count;
1686		}
1687	}
1688
1689	// [14] get the kSecKeyUnwrap boolean
1690	if (CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrCanUnwrap, (const void **)&value) && value) {
1691		if (CFBooleanGetTypeID() == CFGetTypeID(value)) {
1692			attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32));
1693			require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_number_failed, status = errSecBufferTooSmall);
1694
1695			attrListPtr->attr[attrListPtr->count].tag = kSecKeyUnwrap;
1696			attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32);
1697			*((UInt32*)attrListPtr->attr[attrListPtr->count].data) = (CFEqual(kCFBooleanTrue, value)) ? 1 : 0;
1698
1699			++attrListPtr->count;
1700		}
1701	}
1702
1703	// return the pointer to the attrList
1704	*attrList = attrListPtr;
1705
1706	return ( errSecSuccess );
1707
1708	/***************/
1709
1710malloc_number_failed:
1711CFDataCreateAttribute_failed:
1712CFStringCreateAttribute_failed:
1713malloc_attrPtr_failed:
1714
1715	// free any attributes
1716	_FreeAttrList(attrListPtr);
1717
1718calloc_attrListPtr_failed:
1719
1720	return ( errSecBufferTooSmall );
1721
1722#endif
1723}
1724
1725static CFTypeRef copyNumber(CFTypeRef obj)
1726{
1727    if (!obj)
1728        return NULL;
1729
1730    CFTypeID tid = CFGetTypeID(obj);
1731    if (tid == CFNumberGetTypeID())
1732    {
1733        CFRetain(obj);
1734        return obj;
1735    }
1736
1737    if (tid == CFBooleanGetTypeID())
1738    {
1739        SInt32 value = CFBooleanGetValue((CFBooleanRef)obj);
1740        return CFNumberCreate(0, kCFNumberSInt32Type, &value);
1741    }
1742
1743    if (tid == CFStringGetTypeID())
1744    {
1745        SInt32 value = CFStringGetIntValue((CFStringRef)obj);
1746        CFStringRef t = CFStringCreateWithFormat(0, 0, CFSTR("%ld"), (long) value);
1747        /* If a string converted to an int isn't equal to the int printed as
1748         a string, return a NULL instead. */
1749        if (!CFEqual(t, obj))
1750        {
1751            CFRelease(t);
1752            return NULL;
1753        }
1754        CFRelease(t);
1755        return CFNumberCreate(0, kCFNumberSInt32Type, &value);
1756    }
1757    return NULL;
1758}
1759
1760/*
1761 * _CreateSecKeychainInternetPasswordAttributeListFromDictionary creates a SecKeychainAttributeList
1762 * from the attribute key/values in attrDictionary.
1763 *
1764 * If this function returns errSecSuccess, the pointer to the SecKeychainAttributeList
1765 * must be freed by the caller with _FreeAttrList()
1766 */
1767static OSStatus
1768_CreateSecKeychainInternetPasswordAttributeListFromDictionary(
1769	CFDictionaryRef attrDictionary,
1770	SecKeychainAttributeList **attrList)
1771{
1772	// explicitly build attribute list for supported key attributes
1773	// NOTE: this code supports only MaxSecKeychainAttributes (14) attributes
1774	const int MaxSecKeychainAttributes = 14;
1775
1776	OSStatus status;
1777	CFTypeRef value;
1778	SecKeychainAttributeList *attrListPtr;
1779
1780	attrListPtr = (SecKeychainAttributeList*)calloc(1, sizeof(SecKeychainAttributeList));
1781	require_action(attrListPtr != NULL, calloc_attrListPtr_failed, status = errSecBufferTooSmall);
1782
1783	attrListPtr->attr = (SecKeychainAttribute*)calloc(MaxSecKeychainAttributes, sizeof(SecKeychainAttribute));
1784	require_action(attrListPtr->attr != NULL, malloc_attrPtr_failed, status = errSecBufferTooSmall);
1785
1786
1787	// [0] get the serverName string
1788	if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrServer, (const void **)&value) ) {
1789		status = _CFStringCreateAttribute((CFStringRef)value, kSecServerItemAttr, &attrListPtr->attr[attrListPtr->count]);
1790		require_noerr_quiet(status, CFStringCreateAttribute_failed);
1791
1792		++attrListPtr->count;
1793	}
1794
1795	// [1] get the securityDomain string
1796	if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrSecurityDomain, (const void **)&value) ) {
1797		status = _CFStringCreateAttribute((CFStringRef)value, kSecSecurityDomainItemAttr, &attrListPtr->attr[attrListPtr->count]);
1798		require_noerr_quiet(status, CFStringCreateAttribute_failed);
1799
1800		++attrListPtr->count;
1801	}
1802
1803	// [2] get the accountName string
1804	if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrAccount, (const void **)&value) ) {
1805		status = _CFStringCreateAttribute((CFStringRef)value, kSecAccountItemAttr, &attrListPtr->attr[attrListPtr->count]);
1806		require_noerr_quiet(status, CFStringCreateAttribute_failed);
1807
1808		++attrListPtr->count;
1809	}
1810
1811	// [3] get the path string
1812	if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrPath, (const void **)&value) ) {
1813		status = _CFStringCreateAttribute((CFStringRef)value, kSecPathItemAttr, &attrListPtr->attr[attrListPtr->count]);
1814		require_noerr_quiet(status, CFStringCreateAttribute_failed);
1815
1816		++attrListPtr->count;
1817	}
1818
1819	// [4] get the port number
1820	if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrPort, (const void **)&value) ) {
1821		attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt16));
1822		require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_port_failed, status = errSecBufferTooSmall);
1823
1824        CFTypeRef num = copyNumber(value);
1825		require_action(num != NULL, CFStringCreateAttribute_failed, status = errSecParam);
1826		attrListPtr->attr[attrListPtr->count].tag = kSecPortItemAttr;
1827		attrListPtr->attr[attrListPtr->count].length = sizeof(UInt16);
1828		CFNumberGetValue((CFNumberRef)num, kCFNumberSInt16Type, attrListPtr->attr[attrListPtr->count].data);
1829        CFRelease(num);
1830
1831		++attrListPtr->count;
1832	}
1833
1834	// [5] get the protocol
1835	if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrProtocol, (const void **)&value) ) {
1836		attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(SecProtocolType));
1837		require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_protocol_failed, status = errSecBufferTooSmall);
1838
1839		attrListPtr->attr[attrListPtr->count].tag = kSecProtocolItemAttr;
1840		attrListPtr->attr[attrListPtr->count].length = sizeof(SecProtocolType);
1841		*(SecProtocolType *)(attrListPtr->attr[attrListPtr->count].data) = _SecProtocolTypeForSecAttrProtocol(value);
1842
1843		++attrListPtr->count;
1844	}
1845
1846	// [6] get the authenticationType
1847	if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrAuthenticationType, (const void **)&value) ) {
1848		attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(SecAuthenticationType));
1849		require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_authenticationType_failed, status = errSecBufferTooSmall);
1850
1851		attrListPtr->attr[attrListPtr->count].tag = kSecAuthenticationTypeItemAttr;
1852		attrListPtr->attr[attrListPtr->count].length = sizeof(SecAuthenticationType);
1853		*(SecAuthenticationType *)(attrListPtr->attr[attrListPtr->count].data) = _SecAuthenticationTypeForSecAttrAuthenticationType(value);
1854
1855		++attrListPtr->count;
1856	}
1857
1858	// [7] get the comment string
1859	if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrComment, (const void **)&value) ) {
1860		status = _CFStringCreateAttribute((CFStringRef)value, kSecCommentItemAttr, &attrListPtr->attr[attrListPtr->count]);
1861		require_noerr_quiet(status, CFStringCreateAttribute_failed);
1862
1863		++attrListPtr->count;
1864	}
1865
1866	// [8] get the description string
1867	if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrDescription, (const void **)&value) ) {
1868		status = _CFStringCreateAttribute((CFStringRef)value, kSecDescriptionItemAttr, &attrListPtr->attr[attrListPtr->count]);
1869		require_noerr_quiet(status, CFStringCreateAttribute_failed);
1870
1871		++attrListPtr->count;
1872	}
1873
1874	// [9] get the label string
1875	if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrLabel, (const void **)&value) ) {
1876		status = _CFStringCreateAttribute((CFStringRef)value, kSecLabelItemAttr, &attrListPtr->attr[attrListPtr->count]);
1877		require_noerr_quiet(status, CFStringCreateAttribute_failed);
1878
1879		++attrListPtr->count;
1880	}
1881
1882	// [10] get the creator code
1883	if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrCreator, (const void **)&value) ) {
1884		attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32));
1885		require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_port_failed, status = errSecBufferTooSmall);
1886
1887        CFTypeRef num = copyNumber(value);
1888		require_action(num != NULL, CFStringCreateAttribute_failed, status = errSecParam);
1889		attrListPtr->attr[attrListPtr->count].tag = kSecCreatorItemAttr;
1890		attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32);
1891		CFNumberGetValue((CFNumberRef)num, kCFNumberSInt32Type, attrListPtr->attr[attrListPtr->count].data);
1892        CFRelease(num);
1893
1894		++attrListPtr->count;
1895	}
1896
1897	// [11] get the type code
1898	if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrType, (const void **)&value) ) {
1899		attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32));
1900		require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_port_failed, status = errSecBufferTooSmall);
1901
1902        CFTypeRef num = copyNumber(value);
1903		require_action(num != NULL, CFStringCreateAttribute_failed, status = errSecParam);
1904		attrListPtr->attr[attrListPtr->count].tag = kSecTypeItemAttr;
1905		attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32);
1906		CFNumberGetValue((CFNumberRef)num, kCFNumberSInt32Type, attrListPtr->attr[attrListPtr->count].data);
1907        CFRelease(num);
1908
1909		++attrListPtr->count;
1910	}
1911
1912	// [12] get the invisible flag
1913	if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrIsInvisible, (const void **)&value) ) {
1914		attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32));
1915		require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_port_failed, status = errSecBufferTooSmall);
1916
1917		attrListPtr->attr[attrListPtr->count].tag = kSecInvisibleItemAttr;
1918		attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32);
1919		*(UInt32 *)(attrListPtr->attr[attrListPtr->count].data) = (CFBooleanGetValue((CFBooleanRef)value)) ? 1 : 0;
1920
1921		++attrListPtr->count;
1922	}
1923
1924	// [13] get the negative flag
1925	if ( CFDictionaryGetValueIfPresent(attrDictionary, kSecAttrIsNegative, (const void **)&value) ) {
1926		attrListPtr->attr[attrListPtr->count].data = malloc(sizeof(UInt32));
1927		require_action(attrListPtr->attr[attrListPtr->count].data != NULL, malloc_port_failed, status = errSecBufferTooSmall);
1928
1929		attrListPtr->attr[attrListPtr->count].tag = kSecNegativeItemAttr;
1930		attrListPtr->attr[attrListPtr->count].length = sizeof(UInt32);
1931		*(UInt32 *)(attrListPtr->attr[attrListPtr->count].data) = (CFBooleanGetValue((CFBooleanRef)value)) ? 1 : 0;
1932
1933		++attrListPtr->count;
1934	}
1935
1936	// return the pointer to the attrList
1937	*attrList = attrListPtr;
1938
1939	return ( errSecSuccess );
1940
1941	/***************/
1942
1943malloc_authenticationType_failed:
1944malloc_protocol_failed:
1945malloc_port_failed:
1946CFStringCreateAttribute_failed:
1947malloc_attrPtr_failed:
1948
1949	// free any attributes
1950	_FreeAttrList(attrListPtr);
1951
1952calloc_attrListPtr_failed:
1953
1954	return ( errSecBufferTooSmall );
1955}
1956
1957
1958/*
1959 * _CreateSecKeychainAttributeListFromDictionary creates a SecKeychainAttributeList
1960 * from the attribute key/values in attrDictionary for the specified item class.
1961 *
1962 * If this function returns errSecSuccess, the pointer to the SecKeychainAttributeList
1963 * must be freed by the caller with _FreeAttrList()
1964 */
1965static OSStatus
1966_CreateSecKeychainAttributeListFromDictionary(
1967	CFDictionaryRef attrDictionary,
1968	SecItemClass itemClass,
1969	SecKeychainAttributeList **attrList)
1970{
1971	switch (itemClass)
1972	{
1973		case kSecInternetPasswordItemClass:
1974			return _CreateSecKeychainInternetPasswordAttributeListFromDictionary(attrDictionary, attrList);
1975
1976		case kSecGenericPasswordItemClass:
1977			return _CreateSecKeychainGenericPasswordAttributeListFromDictionary(attrDictionary, attrList);
1978
1979		case kSecCertificateItemClass:
1980			return _CreateSecKeychainCertificateAttributeListFromDictionary(attrDictionary, attrList);
1981
1982		case kSecPublicKeyItemClass:
1983		case kSecPrivateKeyItemClass:
1984		case kSecSymmetricKeyItemClass:
1985			return _CreateSecKeychainKeyAttributeListFromDictionary(attrDictionary, attrList);
1986
1987		default:
1988			break;
1989	}
1990	return errSecParam;
1991}
1992
1993
1994/*
1995 * _AppNameFromSecTrustedApplication attempts to pull the name of the
1996 * application/tool from the SecTrustedApplicationRef.
1997 */
1998static CFStringRef
1999_AppNameFromSecTrustedApplication(
2000	CFAllocatorRef alloc,
2001	SecTrustedApplicationRef appRef)
2002{
2003	CFStringRef result;
2004	OSStatus status;
2005	CFDataRef appDataRef;
2006
2007	result = NULL;
2008
2009	// get the data for item's application/tool
2010	status = SecTrustedApplicationCopyData(appRef, &appDataRef);
2011	if ( status == errSecSuccess ) {
2012		CFStringRef path;
2013
2014		// convert it to a CFString potentially containing the path
2015		path = CFStringCreateWithCString(NULL, (char *)CFDataGetBytePtrVoid(appDataRef), kCFStringEncodingUTF8);
2016		if ( path != NULL ) {
2017			// the path has to start with a "/" and cannot contain "://"
2018			if ( CFStringHasPrefix(path, CFSTR("/")) && (CFStringFind(path, CFSTR("://"), 0).location == kCFNotFound) ) {
2019				CFRange nameRange, compRg;
2020
2021				nameRange = CFRangeMake(0, CFStringGetLength(path));
2022
2023				// remove the trailing slashes (if any)
2024				while ( (nameRange.length > 0) && (CFStringGetCharacterAtIndex(path, nameRange.length - 1) == '/') ) {
2025					nameRange.length --;
2026				}
2027
2028				if ( nameRange.length > 0 ) {
2029					// find last slash and adjust nameRange be everything after it
2030					if ( CFStringFindWithOptions(path, CFSTR("/"), nameRange, kCFCompareBackwards, &compRg) ) {
2031						nameRange.length = nameRange.location + nameRange.length - (compRg.location + 1);
2032						nameRange.location = compRg.location + 1;
2033					}
2034
2035					result = CFStringCreateWithSubstring(alloc, path, nameRange);
2036				}
2037			}
2038			CFRelease(path);
2039		}
2040		CFRelease(appDataRef);
2041	}
2042
2043	return ( result );
2044}
2045
2046/* (This function really belongs in SecIdentity.cpp!)
2047 *
2048 * Returns the public key item corresponding to the identity, if it exists in
2049 * the same keychain as the private key. Note that the public key might not
2050 * exist in the same keychain (e.g. if the identity was imported via PKCS12),
2051 * in which case it will not be found.
2052 */
2053static OSStatus
2054_SecIdentityCopyPublicKey(
2055	SecIdentityRef identityRef,
2056	SecKeyRef *publicKeyRef)
2057{
2058	OSStatus status;
2059	UInt32 count;
2060	SecKeychainAttribute attr = { kSecKeyLabel, 0, NULL };
2061	SecKeychainAttributeList attrList = { 1, &attr };
2062	SecKeychainAttributeList *keyAttrList = NULL;
2063	SecKeychainAttributeInfo *info = NULL;
2064	SecKeychainSearchRef search = NULL;
2065	SecKeychainRef keychain = NULL;
2066	SecKeychainItemRef privateKey = NULL;
2067	SecKeychainItemRef publicKey = NULL;
2068
2069	status = SecIdentityCopyPrivateKey(identityRef, (SecKeyRef *)&privateKey);
2070	if (status) {
2071		goto error_exit; // identity must have a private key
2072	}
2073	status = SecKeychainItemCopyKeychain(privateKey, &keychain);
2074	if (status) {
2075		goto error_exit; // private key must have a keychain, so we can get the attribute info for it
2076	}
2077	status = SecKeychainAttributeInfoForItemID(keychain, kSecPrivateKeyItemClass, &info);
2078	if (status) {
2079		goto error_exit; // unable to get the attribute info (i.e. database schema) for private keys
2080	}
2081	status = SecKeychainItemCopyAttributesAndData(privateKey, info, NULL, &keyAttrList, NULL, NULL);
2082	if (status) {
2083		goto error_exit; // unable to get the key label attribute for the private key
2084	}
2085
2086	// use the found kSecKeyLabel attribute from the private key in a separate attribute list for searching
2087	for (count = 0; count < keyAttrList->count; count++) {
2088		if (keyAttrList->attr[count].tag == kSecKeyLabel) {
2089			attr.length = keyAttrList->attr[count].length;
2090			attr.data = keyAttrList->attr[count].data;
2091			break;
2092		}
2093	}
2094	if (!attr.length || !attr.data) {
2095		status = errSecNoSuchAttr;
2096		goto error_exit; // the private key didn't have the hash of the public key in its kSecKeyLabel
2097	}
2098	status = SecKeychainSearchCreateFromAttributes(keychain, kSecPublicKeyItemClass, &attrList, &search);
2099	if (status) {
2100		goto error_exit; // unable to create the search reference
2101	}
2102	status = SecKeychainSearchCopyNext(search, &publicKey);
2103	if (status) {
2104		goto error_exit; // unable to find the public key
2105	}
2106
2107	if (publicKeyRef)
2108		*publicKeyRef = (SecKeyRef)publicKey;
2109	else
2110		CFRelease(publicKey);
2111
2112error_exit:
2113	if (status != errSecSuccess) {
2114		if (publicKeyRef)
2115			*publicKeyRef = NULL;
2116		if (publicKey)
2117			CFRelease(publicKey);
2118	}
2119	if (search)
2120		CFRelease(search);
2121
2122	if (keyAttrList)
2123		SecKeychainItemFreeAttributesAndData(keyAttrList, NULL);
2124
2125	if (info)
2126		SecKeychainFreeAttributeInfo(info);
2127
2128	if (keychain)
2129		CFRelease(keychain);
2130
2131	if (privateKey)
2132		CFRelease(privateKey);
2133
2134	return status;
2135}
2136
2137
2138/*
2139 * Deletes a keychain item if the current application/tool is the only application/tool
2140 * with decrypt access to that keychain item. If more than one application/tool
2141 * has decrypt access to the keychain item, the item is left on the keychain.
2142 *
2143 * TBD: If more than one app/tool has access to the keychain item, we should remove
2144 * the current app/tool's decrypt access. There's no easy way to do that with
2145 * current keychain APIs without bringing up the security UI.
2146 */
2147static OSStatus
2148_SafeSecKeychainItemDelete(
2149	SecKeychainItemRef itemRef)
2150{
2151	OSStatus status;
2152	SecAccessRef access;
2153	CFArrayRef aclList;
2154	SecACLRef acl;
2155	CFArrayRef appList;
2156	CFStringRef description;
2157	CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR promptSelector;
2158
2159	SecItemClass itemClass;
2160	status = SecKeychainItemCopyAttributesAndData(itemRef, NULL, &itemClass, NULL, NULL, NULL);
2161	if (!status && (itemClass == kSecCertificateItemClass || itemClass == kSecPublicKeyItemClass)) {
2162		// the item doesn't have any access controls, so delete it normally
2163		return SecKeychainItemDelete(itemRef);
2164	}
2165
2166	// skip access control checking for web form passwords: <rdar://10957301>
2167	// This permits Safari to manage the removal of all web form passwords,
2168	// regardless of whether they are shared by multiple applications.
2169	if (itemClass == kSecInternetPasswordItemClass) {
2170		UInt32 tags[1] = { kSecAuthenticationTypeItemAttr };
2171		SecKeychainAttributeInfo attrInfo = { 1, tags, NULL };
2172		SecKeychainAttributeList *attrs = NULL;
2173		status = SecKeychainItemCopyAttributesAndData(itemRef, &attrInfo, NULL, &attrs, NULL, NULL);
2174		if (!status && attrs) {
2175			bool webFormPassword = (attrs->attr[0].length == 4 && (!memcmp(attrs->attr[0].data, "form", 4)));
2176			SecKeychainItemFreeAttributesAndData(attrs, NULL);
2177			if (webFormPassword) {
2178				return SecKeychainItemDelete(itemRef);
2179			}
2180		}
2181	}
2182
2183	// copy the access of the keychain item
2184	status = SecKeychainItemCopyAccess(itemRef, &access);
2185	require_noerr(status, SecKeychainItemCopyAccessFailed);
2186
2187	// copy the decrypt access control lists -- this is what has access to the keychain item
2188	status = SecAccessCopySelectedACLList(access, CSSM_ACL_AUTHORIZATION_DECRYPT, &aclList);
2189	require_noerr(status, SecAccessCopySelectedACLListFailed);
2190	require_quiet(aclList != NULL, noACLList);
2191
2192	// get the access control list
2193	acl = (SecACLRef)CFArrayGetValueAtIndex(aclList, 0);
2194	require_quiet(acl != NULL, noACL);
2195
2196	// copy the application list, description, and CSSM prompt selector for a given access control list entry
2197	status = SecACLCopySimpleContents(acl, &appList, &description, &promptSelector);
2198	require_noerr(status, SecACLCopySimpleContentsFailed);
2199	require_quiet(appList != NULL, noAppList);
2200
2201	// does only a single application/tool have decrypt access to this item?
2202	if ( CFArrayGetCount(appList) == 1 ) {
2203		SecTrustedApplicationRef itemAppRef, currentAppRef;
2204		CFStringRef itemAppName, currentAppName;
2205
2206		// get SecTrustedApplicationRef for item's application/tool
2207		itemAppRef = (SecTrustedApplicationRef)CFArrayGetValueAtIndex(appList, 0);
2208		require(itemAppRef != NULL, noItemAppRef);
2209
2210		// copy the name out
2211		itemAppName = _AppNameFromSecTrustedApplication(CFGetAllocator(itemRef), itemAppRef);
2212		require(itemAppName != NULL, noAppName);
2213
2214		// create SecTrustedApplicationRef for current application/tool
2215		status = SecTrustedApplicationCreateFromPath(NULL, &currentAppRef);
2216		require((status == errSecSuccess) && (currentAppRef != NULL), SecTrustedApplicationCreateFromPathFailed);
2217
2218		// copy the name out
2219		currentAppName = _AppNameFromSecTrustedApplication(CFGetAllocator(itemRef), currentAppRef);
2220		require(currentAppName != NULL, noCurrentAppName);
2221
2222		// compare the current application/tool's name to this item's application/tool's name to see if we own the decrypt access
2223		if ( CFStringCompare(currentAppName, itemAppName, 0) == kCFCompareEqualTo ) {
2224			// delete the keychain item
2225			SecKeychainItemDelete(itemRef);
2226		}
2227
2228		CFRelease(currentAppName);
2229	noCurrentAppName:
2230		CFRelease(currentAppRef);
2231	SecTrustedApplicationCreateFromPathFailed:
2232		CFRelease(itemAppName);
2233	noAppName:
2234	noItemAppRef:
2235		;
2236	}
2237
2238	if ( description ) {
2239		CFRelease(description);
2240	}
2241	CFRelease(appList);
2242noAppList:
2243SecACLCopySimpleContentsFailed:
2244noACL:
2245	CFRelease(aclList);
2246noACLList:
2247SecAccessCopySelectedACLListFailed:
2248	CFRelease(access);
2249SecKeychainItemCopyAccessFailed:
2250
2251	return status;
2252}
2253
2254static OSStatus
2255_UpdateKeychainItem(CFTypeRef item, CFDictionaryRef changedAttributes)
2256{
2257	// This function updates a single keychain item, which may be specified as
2258	// a reference, persistent reference or attribute dictionary, with the
2259	// attributes provided.
2260
2261	OSStatus status = errSecSuccess;
2262	if (!item) {
2263		return errSecParam;
2264	}
2265
2266	SecItemClass itemClass;
2267	SecAccessRef access = NULL;
2268	SecKeychainAttributeList *changeAttrList = NULL;
2269	SecKeychainItemRef itemToUpdate = NULL;
2270	CFDataRef theData = NULL;
2271	CFTypeID itemType = CFGetTypeID(item);
2272
2273	// validate input item (must be convertible to a SecKeychainItemRef)
2274	if (SecKeychainItemGetTypeID() == itemType ||
2275		SecCertificateGetTypeID() == itemType ||
2276		SecKeyGetTypeID() == itemType) {
2277		// item is already a reference, retain it
2278		itemToUpdate = (SecKeychainItemRef) CFRetain(item);
2279	}
2280	else if (CFDataGetTypeID() == itemType) {
2281		// item is a persistent reference, must convert it
2282		status = SecKeychainItemCopyFromPersistentReference((CFDataRef)item, &itemToUpdate);
2283	}
2284	else if (CFDictionaryGetTypeID() == itemType) {
2285		// item is a dictionary
2286		CFTypeRef value = NULL;
2287		if (CFDictionaryGetValueIfPresent((CFDictionaryRef)item, kSecValueRef, &value)) {
2288			// kSecValueRef value is a SecKeychainItemRef, retain it
2289			itemToUpdate = (SecKeychainItemRef) CFRetain(value);
2290		}
2291		else if (CFDictionaryGetValueIfPresent((CFDictionaryRef)item, kSecValuePersistentRef, &value)) {
2292			// kSecValuePersistentRef value is a persistent reference, must convert it
2293			status = SecKeychainItemCopyFromPersistentReference((CFDataRef)value, &itemToUpdate);
2294		}
2295	}
2296	else if (SecIdentityGetTypeID() == itemType) {
2297		// item is a certificate + private key; since we can't really change the
2298		// certificate's attributes, assume we want to update the private key
2299		status = SecIdentityCopyPrivateKey((SecIdentityRef)item, (SecKeyRef*)&itemToUpdate);
2300	}
2301	require_action(itemToUpdate != NULL, update_failed, status = errSecInvalidItemRef);
2302	require_noerr(status, update_failed);
2303
2304	status = SecKeychainItemCopyContent(itemToUpdate, &itemClass, NULL, NULL, NULL);
2305	require_noerr(status, update_failed);
2306
2307	// build changeAttrList from changedAttributes dictionary
2308	switch (itemClass)
2309	{
2310		case kSecInternetPasswordItemClass:
2311		{
2312			status = _CreateSecKeychainInternetPasswordAttributeListFromDictionary(changedAttributes, &changeAttrList);
2313			require_noerr(status, update_failed);
2314		}
2315		break;
2316
2317		case kSecGenericPasswordItemClass:
2318		{
2319			status = _CreateSecKeychainGenericPasswordAttributeListFromDictionary(changedAttributes, &changeAttrList);
2320			require_noerr(status, update_failed);
2321		}
2322		break;
2323
2324		case kSecCertificateItemClass:
2325		{
2326			status = _CreateSecKeychainCertificateAttributeListFromDictionary(changedAttributes, &changeAttrList);
2327			require_noerr(status, update_failed);
2328		}
2329		break;
2330
2331		case kSecPublicKeyItemClass:
2332		case kSecPrivateKeyItemClass:
2333		case kSecSymmetricKeyItemClass:
2334		{
2335			status = _CreateSecKeychainKeyAttributeListFromDictionary(changedAttributes, &changeAttrList);
2336			require_noerr(status, update_failed);
2337		}
2338	}
2339
2340	// get the password
2341	// (if the caller is not updating the password, this value will be NULL)
2342	theData = (CFDataRef)CFDictionaryGetValue(changedAttributes, kSecValueData);
2343	if (theData != NULL) {
2344		require_action(CFDataGetTypeID() == CFGetTypeID(theData), update_failed, status = errSecParam);
2345	}
2346	// update item
2347	status = SecKeychainItemModifyContent(itemToUpdate,
2348				(changeAttrList->count == 0) ? NULL : changeAttrList,
2349				(theData != NULL) ? (UInt32)CFDataGetLength(theData) : 0,
2350				(theData != NULL) ? CFDataGetBytePtrVoid(theData) : NULL);
2351
2352	// one more thing... update access?
2353	if (CFDictionaryGetValueIfPresent(changedAttributes, kSecAttrAccess, (const void **)&access)) {
2354		status = SecKeychainItemSetAccess(itemToUpdate, access);
2355	}
2356
2357update_failed:
2358	if (itemToUpdate)
2359		CFRelease(itemToUpdate);
2360	_FreeAttrList(changeAttrList);
2361	return status;
2362}
2363
2364static OSStatus
2365_DeleteKeychainItem(CFTypeRef item)
2366{
2367	// This function deletes a single keychain item, which may be specified as
2368	// a reference, persistent reference or attribute dictionary. It will not
2369	// delete non-keychain items or aggregate items (such as a SecIdentityRef);
2370	// it is assumed that the caller will pass identity components separately.
2371
2372	OSStatus status = errSecSuccess;
2373	if (!item) {
2374		return errSecParam;
2375	}
2376
2377	SecKeychainItemRef itemToDelete = NULL;
2378	CFTypeID itemType = CFGetTypeID(item);
2379	if (SecKeychainItemGetTypeID() == itemType ||
2380		SecCertificateGetTypeID() == itemType ||
2381		SecKeyGetTypeID() == itemType) {
2382		// item is already a reference, retain it
2383		itemToDelete = (SecKeychainItemRef) CFRetain(item);
2384	}
2385	else if (CFDataGetTypeID() == itemType) {
2386		// item is a persistent reference, must convert it
2387		status = SecKeychainItemCopyFromPersistentReference((CFDataRef)item, &itemToDelete);
2388	}
2389	else if (CFDictionaryGetTypeID() == itemType) {
2390		// item is a dictionary
2391		CFTypeRef value = NULL;
2392		if (CFDictionaryGetValueIfPresent((CFDictionaryRef)item, kSecValueRef, &value)) {
2393			// kSecValueRef value is a SecKeychainItemRef, retain it
2394			itemToDelete = (SecKeychainItemRef) CFRetain(value);
2395		}
2396		else if (CFDictionaryGetValueIfPresent((CFDictionaryRef)item, kSecValuePersistentRef, &value)) {
2397			// kSecValuePersistentRef value is a persistent reference, must convert it
2398			status = SecKeychainItemCopyFromPersistentReference((CFDataRef)value, &itemToDelete);
2399		}
2400	}
2401
2402	if (itemToDelete) {
2403		if (!status) {
2404			status = _SafeSecKeychainItemDelete(itemToDelete);
2405		}
2406		CFRelease(itemToDelete);
2407	}
2408
2409	return status;
2410}
2411
2412static OSStatus
2413_DeleteIdentity(SecIdentityRef identity)
2414{
2415	OSStatus status, result = errSecSuccess;
2416	SecKeyRef privateKey = NULL;
2417	SecCertificateRef certificate = NULL;
2418
2419	status = SecIdentityCopyPrivateKey(identity, &privateKey);
2420	if (!status) {
2421		SecKeyRef publicKey = NULL;
2422		status = _SecIdentityCopyPublicKey(identity, &publicKey);
2423		if (!status) {
2424			status = _DeleteKeychainItem(publicKey);
2425			CFRelease(publicKey);
2426		}
2427		status = _DeleteKeychainItem(privateKey);
2428	}
2429
2430	if (privateKey) CFRelease(privateKey);
2431	if (status) result = status;
2432
2433	status = SecIdentityCopyCertificate(identity, &certificate);
2434	if (!status) {
2435		status = _DeleteKeychainItem(certificate);
2436	}
2437
2438	if (certificate) CFRelease(certificate);
2439	if (status) result = status;
2440
2441	return result;
2442}
2443
2444static OSStatus
2445_UpdateAggregateStatus(OSStatus newStatus, OSStatus curStatus, OSStatus baseStatus)
2446{
2447	// This function is used when atomically processing multiple items,
2448	// where an overall error result must be returned for the entire operation.
2449	// When newStatus is something other than errSecSuccess, we want to keep the "most
2450	// interesting" status (which usually will be newStatus, unless curStatus is
2451	// already set; in that case, newStatus can trump curStatus only by being
2452	// something different than baseStatus.)
2453
2454	OSStatus result = curStatus;
2455
2456	if (newStatus != errSecSuccess) {
2457		result = newStatus;
2458		if (curStatus != errSecSuccess) {
2459			result = (newStatus != baseStatus) ? newStatus : curStatus;
2460		}
2461	}
2462	return result;
2463}
2464
2465static void
2466_AddDictValueToOtherDict(const void *key, const void *value, void *context)
2467{
2468	// CFDictionaryApplierFunction
2469	// This function just takes the given key/value pair,
2470	// and adds it to another dictionary supplied in the context argument.
2471
2472	CFMutableDictionaryRef dict = *((CFMutableDictionaryRef*) context);
2473	if (key && value) {
2474		CFDictionaryAddValue(dict, key, value);
2475	}
2476}
2477
2478static CFStringCompareFlags
2479_StringCompareFlagsFromQuery(CFDictionaryRef query)
2480{
2481	CFTypeRef value;
2482	CFStringCompareFlags flags = 0;
2483	if (!query) return flags;
2484
2485	if (CFDictionaryGetValueIfPresent(query, kSecMatchSubjectStartsWith, (const void **)&value) ||
2486		CFDictionaryGetValueIfPresent(query, kSecMatchSubjectEndsWith, (const void **)&value))
2487		flags |= kCFCompareAnchored;
2488
2489	if (CFDictionaryGetValueIfPresent(query, kSecMatchSubjectEndsWith, (const void **)&value))
2490		flags |= kCFCompareBackwards;
2491
2492	if (CFDictionaryGetValueIfPresent(query, kSecMatchCaseInsensitive, (const void **)&value) && CFEqual(kCFBooleanTrue, value))
2493		flags |= kCFCompareCaseInsensitive;
2494
2495	if (CFDictionaryGetValueIfPresent(query, kSecMatchDiacriticInsensitive, (const void **)&value) && CFEqual(kCFBooleanTrue, value))
2496		flags |= kCFCompareDiacriticInsensitive;
2497
2498	if (CFDictionaryGetValueIfPresent(query, kSecMatchWidthInsensitive, (const void **)&value) && CFEqual(kCFBooleanTrue, value))
2499		flags |= kCFCompareWidthInsensitive;
2500
2501	return flags;
2502}
2503
2504static uint32
2505_CssmKeyUsageFromQuery(CFDictionaryRef query)
2506{
2507	CFTypeRef value;
2508	uint32 keyUsage = 0;
2509	if (!query) return keyUsage;
2510
2511	if (CFDictionaryGetValueIfPresent(query, kSecAttrCanEncrypt, (const void **)&value) && CFEqual(kCFBooleanTrue, value))
2512		keyUsage |= CSSM_KEYUSE_ENCRYPT;
2513
2514	if (CFDictionaryGetValueIfPresent(query, kSecAttrCanDecrypt, (const void **)&value) && CFEqual(kCFBooleanTrue, value))
2515		keyUsage |= CSSM_KEYUSE_DECRYPT;
2516
2517	if (CFDictionaryGetValueIfPresent(query, kSecAttrCanSign, (const void **)&value) && CFEqual(kCFBooleanTrue, value))
2518		keyUsage |= CSSM_KEYUSE_SIGN;
2519
2520	if (CFDictionaryGetValueIfPresent(query, kSecAttrCanVerify, (const void **)&value) && CFEqual(kCFBooleanTrue, value))
2521		keyUsage |= CSSM_KEYUSE_VERIFY;
2522
2523	if (CFDictionaryGetValueIfPresent(query, kSecAttrCanWrap, (const void **)&value) && CFEqual(kCFBooleanTrue, value))
2524		keyUsage |= CSSM_KEYUSE_WRAP;
2525
2526	if (CFDictionaryGetValueIfPresent(query, kSecAttrCanUnwrap, (const void **)&value) && CFEqual(kCFBooleanTrue, value))
2527		keyUsage |= CSSM_KEYUSE_UNWRAP;
2528
2529	if (CFDictionaryGetValueIfPresent(query, kSecAttrCanDerive, (const void **)&value) && CFEqual(kCFBooleanTrue, value))
2530		keyUsage |= CSSM_KEYUSE_DERIVE;
2531
2532	return keyUsage;
2533}
2534
2535static SecItemClass
2536_ConvertItemClass(const void* item, const void* keyClass, Boolean *isIdentity)
2537{
2538	SecItemClass itemClass = (SecItemClass) 0;
2539	if (isIdentity) *isIdentity = false;
2540
2541	if (CFEqual(item, kSecClassGenericPassword)) {
2542		itemClass = kSecGenericPasswordItemClass;
2543	}
2544	else if (CFEqual(item, kSecClassInternetPassword)) {
2545		itemClass = kSecInternetPasswordItemClass;
2546	}
2547	else if (CFEqual(item, kSecClassCertificate)) {
2548		itemClass = kSecCertificateItemClass;
2549	}
2550	else if (CFEqual(item, kSecClassIdentity)) {
2551		// will perform a certificate lookup
2552		itemClass = kSecCertificateItemClass;
2553		if (isIdentity) *isIdentity = true;
2554	}
2555	else if (CFEqual(item, kSecClassKey)) {
2556		// examine second parameter to determine type of key
2557		if (!keyClass || CFEqual(keyClass, kSecAttrKeyClassSymmetric)) {
2558			itemClass = kSecSymmetricKeyItemClass;
2559		}
2560		else if (keyClass && CFEqual(keyClass, kSecAttrKeyClassPublic)) {
2561			itemClass = kSecPublicKeyItemClass;
2562		}
2563		else if (keyClass && CFEqual(keyClass, kSecAttrKeyClassPrivate)) {
2564			itemClass = kSecPrivateKeyItemClass;
2565		}
2566	}
2567
2568	return itemClass;
2569}
2570
2571static SecItemClass
2572_ItemClassFromItemList(CFArrayRef itemList)
2573{
2574	// Given a list of items (standard or persistent references),
2575	// determine whether they all have the same item class. Returns
2576	// the item class, or 0 if multiple classes in list.
2577	SecItemClass result = 0;
2578	CFIndex index, count = (itemList) ? CFArrayGetCount(itemList) : 0;
2579	for (index=0; index < count; index++) {
2580		CFTypeRef item = (CFTypeRef) CFArrayGetValueAtIndex(itemList, index);
2581		if (item) {
2582			SecKeychainItemRef itemRef = NULL;
2583			OSStatus status;
2584			if (CFGetTypeID(item) == CFDataGetTypeID()) {
2585				// persistent reference, resolve first
2586				status = SecKeychainItemCopyFromPersistentReference((CFDataRef)item, &itemRef);
2587			}
2588			else {
2589				itemRef = (SecKeychainItemRef) CFRetain(item);
2590			}
2591			if (itemRef) {
2592				SecItemClass itemClass = 0;
2593				CFTypeID itemTypeID = CFGetTypeID(itemRef);
2594				if (itemTypeID == SecIdentityGetTypeID() || itemTypeID == SecCertificateGetTypeID()) {
2595					// Identities and certificates have the same underlying item class
2596					itemClass = kSecCertificateItemClass;
2597				}
2598				else if (itemTypeID == SecKeychainItemGetTypeID()) {
2599					// Reference to item in a keychain
2600					status = SecKeychainItemCopyAttributesAndData(itemRef, NULL, &itemClass, NULL, NULL, NULL);
2601				}
2602				else if (itemTypeID == SecKeyGetTypeID()) {
2603					// SecKey that isn't stored in a keychain
2604					// %%% will need to change this code when SecKey is no longer CSSM-based %%%
2605					const CSSM_KEY *cssmKey;
2606					status = SecKeyGetCSSMKey((SecKeyRef)itemRef, &cssmKey);
2607					if (status == errSecSuccess) {
2608						if (cssmKey->KeyHeader.KeyClass == CSSM_KEYCLASS_PUBLIC_KEY)
2609							itemClass = kSecPublicKeyItemClass;
2610						else if (cssmKey->KeyHeader.KeyClass == CSSM_KEYCLASS_PRIVATE_KEY)
2611							itemClass = kSecPrivateKeyItemClass;
2612						else
2613							itemClass = kSecSymmetricKeyItemClass;
2614					}
2615				}
2616				CFRelease(itemRef);
2617				if (itemClass != 0) {
2618					if (result != 0 && result != itemClass) {
2619						return 0; // different item classes in list; bail out
2620					}
2621					result = itemClass;
2622				}
2623			}
2624		}
2625	}
2626	return result;
2627}
2628
2629// SecItemParams contains a validated set of input parameters, as well as a
2630// search reference and attribute list built from those parameters. It is
2631// designed to be allocated with _CreateSecItemParamsFromDictionary, and
2632// freed with _FreeSecItemParams.
2633
2634struct SecItemParams {
2635	CFDictionaryRef query;				// caller-supplied query
2636	int numResultTypes;					// number of result types requested
2637	int maxMatches;						// max number of matches to return
2638	uint32 keyUsage;					// key usage(s) requested
2639	Boolean returningAttributes;		// true if returning attributes dictionary
2640	Boolean returningData;				// true if returning item's data
2641	Boolean returningRef;				// true if returning item reference
2642	Boolean returningPersistentRef;		// true if returing a persistent reference
2643	Boolean returnAllMatches;			// true if we should return all matches
2644	Boolean returnIdentity;				// true if we are returning a SecIdentityRef
2645	Boolean trustedOnly;				// true if we only return trusted certs
2646	Boolean	issuerAndSNToMatch;			// true if both issuer and SN were provided
2647	SecItemClass itemClass;				// item class for this query
2648	SecPolicyRef policy;				// value for kSecMatchPolicy (may be NULL)
2649	SecKeychainRef keychain;			// value for kSecUseKeychain (may be NULL)
2650	CFArrayRef useItems;				// value for kSecUseItemList (may be NULL)
2651	CFArrayRef itemList;				// value for kSecMatchItemList (may be NULL)
2652	CFTypeRef searchList;				// value for kSecMatchSearchList (may be NULL)
2653	CFTypeRef matchLimit;				// value for kSecMatchLimit (may be NULL)
2654	CFTypeRef emailAddrToMatch;			// value for kSecMatchEmailAddressIfPresent (may be NULL)
2655	CFTypeRef validOnDate;				// value for kSecMatchValidOnDate (may be NULL)
2656	CFTypeRef keyClass;					// value for kSecAttrKeyClass (may be NULL)
2657	CFTypeRef service;					// value for kSecAttrService (may be NULL)
2658	CFTypeRef issuer;					// value for kSecAttrIssuer (may be NULL)
2659	CFTypeRef serialNumber;				// value for kSecAttrSerialNumber (may be NULL)
2660	CFTypeRef search;					// search reference for this query (SecKeychainSearchRef or SecIdentitySearchRef)
2661	CFTypeRef assumedKeyClass;			// if no kSecAttrKeyClass provided, holds the current class we're searching for
2662	CFIndex itemListIndex;				// if no search reference but we have itemList, holds index of next item to return
2663	SecKeychainAttributeList *attrList;	// attribute list for this query
2664	SecAccessRef access;				// access reference (for SecItemAdd only, not used to find items)
2665	CFDataRef itemData;					// item data (for SecItemAdd only, not used to find items)
2666	CFTypeRef itemRef;					// item reference (to find, add, update or delete, depending on context)
2667	SecIdentityRef identityRef;			// identity reference (input as kSecValueRef)
2668	CFDataRef itemPersistentRef;		// item persistent reference (to find, add, update or delete, depending on context)
2669};
2670
2671static OSStatus
2672_ValidateDictionaryEntry(CFDictionaryRef dict, CFTypeRef key, const void **value, CFTypeID expectedTypeID, CFTypeID altTypeID)
2673{
2674	if (!dict || !key || !value || !expectedTypeID)
2675		return errSecParam;
2676
2677	if (!CFDictionaryGetValueIfPresent(dict, key, value)) {
2678		// value was not provided for this key (not an error!)
2679		*value = NULL;
2680	}
2681	else if (!(*value)) {
2682		// provided value is NULL (also not an error!)
2683		return errSecSuccess;
2684	}
2685	else {
2686		CFTypeID actualTypeID = CFGetTypeID(*value);
2687		if (!((expectedTypeID == actualTypeID) || (altTypeID && altTypeID == actualTypeID))) {
2688			// provided value does not have the expected (or alternate) CF type ID
2689			if ((expectedTypeID == SecKeychainItemGetTypeID()) &&
2690				(actualTypeID == SecKeyGetTypeID() || actualTypeID == SecCertificateGetTypeID())) {
2691				// provided value is a "floating" reference which is not yet in a keychain
2692				CFRetain(*value);
2693				return errSecSuccess;
2694			}
2695			return errSecItemInvalidValue;
2696		}
2697		else {
2698			// provided value is OK; retain it
2699			CFRetain(*value);
2700		}
2701	}
2702	return errSecSuccess;
2703}
2704
2705static void
2706_FreeSecItemParams(SecItemParams *itemParams)
2707{
2708	if (!itemParams)
2709		return;
2710
2711	if (itemParams->query) CFRelease(itemParams->query);
2712	if (itemParams->policy) CFRelease(itemParams->policy);
2713	if (itemParams->keychain) CFRelease(itemParams->keychain);
2714	if (itemParams->useItems) CFRelease(itemParams->useItems);
2715	if (itemParams->itemList) CFRelease(itemParams->itemList);
2716	if (itemParams->searchList) CFRelease(itemParams->searchList);
2717	if (itemParams->matchLimit) CFRelease(itemParams->matchLimit);
2718	if (itemParams->emailAddrToMatch) CFRelease(itemParams->emailAddrToMatch);
2719	if (itemParams->validOnDate) CFRelease(itemParams->validOnDate);
2720	if (itemParams->keyClass) CFRelease(itemParams->keyClass);
2721	if (itemParams->service) CFRelease(itemParams->service);
2722	if (itemParams->issuer) CFRelease(itemParams->issuer);
2723	if (itemParams->serialNumber) CFRelease(itemParams->serialNumber);
2724	if (itemParams->search) CFRelease(itemParams->search);
2725	if (itemParams->access) CFRelease(itemParams->access);
2726	if (itemParams->itemData) CFRelease(itemParams->itemData);
2727	if (itemParams->itemRef) CFRelease(itemParams->itemRef);
2728	if (itemParams->identityRef) CFRelease(itemParams->identityRef);
2729	if (itemParams->itemPersistentRef) CFRelease(itemParams->itemPersistentRef);
2730
2731	_FreeAttrList(itemParams->attrList);
2732
2733	free(itemParams);
2734}
2735
2736static SecItemParams*
2737_CreateSecItemParamsFromDictionary(CFDictionaryRef dict, OSStatus *error)
2738{
2739	OSStatus status;
2740	CFTypeRef value = NULL;
2741	SecItemParams *itemParams = (SecItemParams *) malloc(sizeof(SecItemParams));
2742
2743	require_action(itemParams != NULL, error_exit, status = errSecAllocate);
2744	require_action(dict && (CFDictionaryGetTypeID() == CFGetTypeID(dict)), error_exit, status = errSecParam);
2745
2746	memset(itemParams, 0, sizeof(SecItemParams));
2747	itemParams->query = (CFDictionaryRef) CFRetain(dict);
2748
2749	// validate input search parameters
2750	require_noerr(status = _ValidateDictionaryEntry(dict, kSecMatchPolicy, (const void **)&itemParams->policy, SecPolicyGetTypeID(), NULL), error_exit);
2751	require_noerr(status = _ValidateDictionaryEntry(dict, kSecMatchSearchList, (const void **)&itemParams->searchList, CFArrayGetTypeID(), NULL), error_exit);
2752	require_noerr(status = _ValidateDictionaryEntry(dict, kSecMatchItemList, (const void **)&itemParams->itemList, CFArrayGetTypeID(), NULL), error_exit);
2753	require_noerr(status = _ValidateDictionaryEntry(dict, kSecMatchEmailAddressIfPresent, (const void **)&itemParams->emailAddrToMatch, CFStringGetTypeID(), NULL), error_exit);
2754	require_noerr(status = _ValidateDictionaryEntry(dict, kSecMatchValidOnDate, (const void **)&itemParams->validOnDate, CFDateGetTypeID(), CFNullGetTypeID()), error_exit);
2755	require_noerr(status = _ValidateDictionaryEntry(dict, kSecMatchLimit, (const void **)&itemParams->matchLimit, CFStringGetTypeID(), CFNumberGetTypeID()), error_exit);
2756
2757	require_noerr(status = _ValidateDictionaryEntry(dict, kSecUseItemList, (const void **)&itemParams->useItems, CFArrayGetTypeID(), NULL), error_exit);
2758	require_noerr(status = _ValidateDictionaryEntry(dict, kSecUseKeychain, (const void **)&itemParams->keychain, SecKeychainGetTypeID(), NULL), error_exit);
2759
2760	// validate a subset of input attributes (used to create an appropriate search reference)
2761	require_noerr(status = _ValidateDictionaryEntry(dict, kSecAttrIssuer, (const void **)&itemParams->issuer, CFDataGetTypeID(), NULL), error_exit);
2762	require_noerr(status = _ValidateDictionaryEntry(dict, kSecAttrSerialNumber, (const void **)&itemParams->serialNumber, CFDataGetTypeID(), NULL), error_exit);
2763	require_noerr(status = _ValidateDictionaryEntry(dict, kSecAttrService, (const void **)&itemParams->service, CFStringGetTypeID(), NULL), error_exit);
2764	require_noerr(status = _ValidateDictionaryEntry(dict, kSecAttrKeyClass, (const void **)&itemParams->keyClass, CFStringGetTypeID(), NULL), error_exit);
2765
2766	// validate the payload (password, key or certificate data), used for SecItemAdd but not for finding items
2767	require_noerr(status = _ValidateDictionaryEntry(dict, kSecValueData, (const void **)&itemParams->itemData, CFDataGetTypeID(), CFStringGetTypeID()), error_exit);
2768
2769	// validate item references
2770	require_noerr(status = _ValidateDictionaryEntry(dict, kSecValueRef, (const void **)&itemParams->itemRef, SecKeychainItemGetTypeID(), SecIdentityGetTypeID()), error_exit);
2771	if (itemParams->itemRef && (CFGetTypeID(itemParams->itemRef) == SecIdentityGetTypeID())) {
2772		itemParams->identityRef = (SecIdentityRef)itemParams->itemRef;
2773		itemParams->itemRef = NULL;
2774		SecIdentityCopyCertificate(itemParams->identityRef, (SecCertificateRef *)&itemParams->itemRef);
2775	}
2776	require_noerr(status = _ValidateDictionaryEntry(dict, kSecValuePersistentRef, (const void **)&itemParams->itemPersistentRef, CFDataGetTypeID(), NULL), error_exit);
2777	if (itemParams->itemRef || itemParams->itemPersistentRef) {
2778		// Caller is trying to add or find an item by reference.
2779		// The supported method for doing that is to provide a kSecUseItemList array
2780		// for SecItemAdd, or a kSecMatchItemList array for SecItemCopyMatching et al,
2781		// so add the item reference to those arrays here.
2782		if (itemParams->useItems) {
2783			CFArrayRef tmpItems = itemParams->useItems;
2784			itemParams->useItems = (CFArrayRef) CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, tmpItems);
2785			CFRelease(tmpItems);
2786		} else {
2787			itemParams->useItems = (CFArrayRef) CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
2788		}
2789		if (itemParams->itemRef) CFArrayAppendValue((CFMutableArrayRef)itemParams->useItems, itemParams->itemRef);
2790		if (itemParams->itemPersistentRef) CFArrayAppendValue((CFMutableArrayRef)itemParams->useItems, itemParams->itemPersistentRef);
2791
2792		if (itemParams->itemList) {
2793			CFArrayRef tmpItems = itemParams->itemList;
2794			itemParams->itemList = (CFArrayRef) CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, tmpItems);
2795			CFRelease(tmpItems);
2796		} else {
2797			itemParams->itemList = (CFArrayRef) CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
2798		}
2799		if (itemParams->itemRef) CFArrayAppendValue((CFMutableArrayRef)itemParams->itemList, itemParams->itemRef);
2800		if (itemParams->itemPersistentRef) CFArrayAppendValue((CFMutableArrayRef)itemParams->itemList, itemParams->itemPersistentRef);
2801	}
2802
2803	// must have an explicit item class, unless one of the following is true:
2804	//   - we have an item list to add or search (kSecUseItemList)
2805	//   - we have an item reference or persistent reference for the thing we want to look up
2806	// Note that both of these cases will set itemParams->useItems.
2807	// If we have an item list to match (kSecMatchItemList), that still requires an item class,
2808	// so we can perform a search and see if the results match items in the list.
2809	//
2810	if (!CFDictionaryGetValueIfPresent(dict, kSecClass, (const void**) &value) && !itemParams->useItems) {
2811		require_action(false, error_exit, status = errSecItemClassMissing);
2812	}
2813	else if (value) {
2814		itemParams->itemClass = _ConvertItemClass(value, itemParams->keyClass, &itemParams->returnIdentity);
2815		if (itemParams->itemClass == kSecSymmetricKeyItemClass && !itemParams->keyClass) {
2816			itemParams->assumedKeyClass = kSecAttrKeyClassSymmetric; // no key class specified, so start with symmetric key class; will search the others later
2817		}
2818		require_action(!(itemParams->itemClass == 0 && !itemParams->useItems), error_exit, status = errSecItemClassMissing);
2819	}
2820
2821	itemParams->keyUsage = _CssmKeyUsageFromQuery(dict);
2822	itemParams->trustedOnly = CFDictionaryGetValueIfPresent(dict, kSecMatchTrustedOnly, (const void **)&value) && value && CFEqual(kCFBooleanTrue, value);
2823	itemParams->issuerAndSNToMatch = (itemParams->issuer != NULL && itemParams->serialNumber != NULL);
2824
2825	// other input attributes, used for SecItemAdd but not for finding items
2826	require_noerr(status = _ValidateDictionaryEntry(dict, kSecAttrAccess, (const void **)&itemParams->access, SecAccessGetTypeID(), NULL), error_exit);
2827	if (itemParams->access == NULL) {
2828		// check for the old definition of kSecAttrAccess from SecItem-shim (see <rdar://7987447>)
2829		require_noerr(status = _ValidateDictionaryEntry(dict, CFSTR("kSecAttrAccess"), (const void **)&itemParams->access, SecAccessGetTypeID(), NULL), error_exit);
2830	}
2831
2832	// determine how to return the result
2833	itemParams->numResultTypes = 0;
2834	itemParams->returningRef = CFDictionaryGetValueIfPresent(dict, kSecReturnRef, (const void **)&value) && value && CFEqual(kCFBooleanTrue, value);
2835	if (itemParams->returningRef) ++itemParams->numResultTypes;
2836	itemParams->returningPersistentRef = CFDictionaryGetValueIfPresent(dict, kSecReturnPersistentRef, (const void **)&value) && value && CFEqual(kCFBooleanTrue, value);
2837	if (itemParams->returningPersistentRef) ++itemParams->numResultTypes;
2838	itemParams->returningAttributes = CFDictionaryGetValueIfPresent(dict, kSecReturnAttributes, (const void **)&value) && value && CFEqual(kCFBooleanTrue, value);
2839	if (itemParams->returningAttributes) ++itemParams->numResultTypes;
2840	itemParams->returningData = CFDictionaryGetValueIfPresent(dict, kSecReturnData, (const void **)&value) && value && CFEqual(kCFBooleanTrue, value);
2841	if (itemParams->returningData) ++itemParams->numResultTypes;
2842
2843	// default is kSecReturnRef if no result types were specified
2844	if (!itemParams->numResultTypes) {
2845		itemParams->returningRef = TRUE;
2846		itemParams->numResultTypes = 1;
2847	}
2848
2849	// determine if one, some or all matches should be returned (default is kSecMatchLimitOne)
2850	itemParams->maxMatches = 1;
2851	itemParams->returnAllMatches = FALSE;
2852	if (itemParams->matchLimit) {
2853		if (CFStringGetTypeID() == CFGetTypeID(itemParams->matchLimit)) {
2854			itemParams->returnAllMatches = CFEqual(kSecMatchLimitAll, itemParams->matchLimit);
2855		}
2856		else if (CFNumberGetTypeID() == CFGetTypeID(itemParams->matchLimit)) {
2857			CFNumberGetValue((CFNumberRef)itemParams->matchLimit, kCFNumberIntType, &itemParams->maxMatches);
2858			require_action(!(itemParams->maxMatches < 0), error_exit, status = errSecMatchLimitUnsupported);
2859		}
2860	}
2861	if (itemParams->returnAllMatches) {
2862		itemParams->maxMatches = INT32_MAX;
2863		// if we're returning all matches, then we don't support getting passwords as data (which could require authentication for each)
2864		if ((itemParams->itemClass==kSecInternetPasswordItemClass || itemParams->itemClass==kSecGenericPasswordItemClass) && itemParams->returningData)
2865			status = errSecReturnDataUnsupported;
2866		require_noerr(status, error_exit);
2867	}
2868
2869	// if we already have an item list (to add or find items in), we don't need an item class, attribute list or a search reference
2870	if (itemParams->useItems) {
2871		if (itemParams->itemClass == 0) {
2872			itemParams->itemClass = _ItemClassFromItemList(itemParams->useItems);
2873		}
2874		status = errSecSuccess;
2875		goto error_exit; // all done here
2876	}
2877
2878	// build a SecKeychainAttributeList from the query dictionary for the specified item class
2879	require_noerr(status = _CreateSecKeychainAttributeListFromDictionary(dict, itemParams->itemClass, &itemParams->attrList), error_exit);
2880
2881	// create a search reference (either a SecKeychainSearchRef or a SecIdentitySearchRef)
2882	if ((itemParams->itemClass == kSecCertificateItemClass) && itemParams->emailAddrToMatch) {
2883		// searching for certificates by email address
2884		char *nameBuf = (char*)malloc(MAXPATHLEN);
2885		if (!nameBuf) {
2886			status = errSecAllocate;
2887		}
2888		else if (CFStringGetCString((CFStringRef)itemParams->emailAddrToMatch, nameBuf, (CFIndex)MAXPATHLEN-1, kCFStringEncodingUTF8)) {
2889			status = SecKeychainSearchCreateForCertificateByEmail(itemParams->searchList, (const char *)nameBuf, (SecKeychainSearchRef*)&itemParams->search);
2890		}
2891		else {
2892			status = errSecItemInvalidValue;
2893		}
2894		if (nameBuf) free(nameBuf);
2895	}
2896	else if ((itemParams->itemClass == kSecCertificateItemClass) && itemParams->issuerAndSNToMatch) {
2897		// searching for certificates by issuer and serial number
2898		status = SecKeychainSearchCreateForCertificateByIssuerAndSN_CF(itemParams->searchList,
2899				(CFDataRef)itemParams->issuer,
2900				(CFDataRef)itemParams->serialNumber,
2901				(SecKeychainSearchRef*)&itemParams->search);
2902	}
2903	else if (itemParams->returnIdentity && itemParams->policy) {
2904		// searching for identities by policy
2905		status = SecIdentitySearchCreateWithPolicy(itemParams->policy,
2906				(CFStringRef)itemParams->service,
2907				itemParams->keyUsage,
2908				itemParams->searchList,
2909				itemParams->trustedOnly,
2910				(SecIdentitySearchRef*)&itemParams->search);
2911	}
2912	else if (itemParams->returnIdentity) {
2913		// searching for identities
2914		status = SecIdentitySearchCreate(itemParams->searchList,
2915				itemParams->keyUsage,
2916				(SecIdentitySearchRef*)&itemParams->search);
2917	}
2918	else {
2919		// normal keychain item search
2920		status = SecKeychainSearchCreateFromAttributes(itemParams->searchList,
2921				itemParams->itemClass,
2922				(itemParams->attrList->count == 0) ? NULL : itemParams->attrList,
2923				(SecKeychainSearchRef*)&itemParams->search);
2924	}
2925
2926error_exit:
2927	if (status) {
2928		_FreeSecItemParams(itemParams);
2929		itemParams = NULL;
2930	}
2931	if (error) {
2932		*error = status;
2933	}
2934	return itemParams;
2935}
2936
2937
2938static OSStatus
2939_ImportKey(
2940	SecKeyRef keyRef,
2941	SecKeychainRef keychainRef,
2942	SecAccessRef accessRef,
2943	SecKeychainAttributeList *attrList,
2944	SecKeychainItemRef *outItemRef)
2945{
2946    BEGIN_SECAPI
2947
2948		// We must specify the access, since a free-floating key won't have one yet by default
2949		SecPointer<Access> access;
2950		if (accessRef) {
2951			access = Access::required(accessRef);
2952		}
2953		else {
2954			CFStringRef descriptor = NULL;
2955			if (attrList) {
2956				for (UInt32 index=0; index < attrList->count; index++) {
2957					SecKeychainAttribute attr = attrList->attr[index];
2958					if (attr.tag == kSecKeyPrintName) {
2959						descriptor = CFStringCreateWithBytes(NULL, (const UInt8 *)attr.data, attr.length, kCFStringEncodingUTF8, FALSE);
2960						break;
2961					}
2962				}
2963			}
2964			if (descriptor == NULL) {
2965				descriptor = (CFStringRef) CFRetain(CFSTR("<unknown>"));
2966			}
2967			access = new Access(cfString(descriptor));
2968			CFRelease(descriptor);
2969		}
2970
2971		KeyItem *key = KeyItem::required(keyRef);
2972		Item item = key->importTo(Keychain::optional(keychainRef), access, attrList);
2973		if (outItemRef)
2974			*outItemRef = item->handle();
2975
2976	END_SECAPI
2977}
2978
2979static Boolean
2980_CanIgnoreLeafStatusCodes(CSSM_TP_APPLE_EVIDENCE_INFO *evidence)
2981{
2982	/* Check for ignorable status codes in leaf certificate's evidence */
2983	Boolean result = true;
2984	unsigned int i;
2985	for (i=0; i < evidence->NumStatusCodes; i++) {
2986		CSSM_RETURN scode = evidence->StatusCodes[i];
2987		if (scode == CSSMERR_APPLETP_INVALID_CA) {
2988			// the TP has rejected this CA cert because it's in the leaf position
2989			result = true;
2990		}
2991		else if (ignorableRevocationStatusCode(scode)) {
2992			result = true;
2993		}
2994		else {
2995			result = false;
2996			break;
2997		}
2998	}
2999	return result;
3000}
3001
3002static OSStatus
3003_FilterWithPolicy(SecPolicyRef policy, CFDateRef date, SecCertificateRef cert)
3004{
3005	CFDictionaryRef props = NULL;
3006	CFArrayRef keychains = NULL;
3007	CFArrayRef anchors = NULL;
3008	CFArrayRef certs = NULL;
3009	CFArrayRef chain = NULL;
3010	SecTrustRef trust = NULL;
3011
3012	SecTrustResultType	trustResult;
3013	CSSM_TP_APPLE_EVIDENCE_INFO *evidence = NULL;
3014	Boolean needChain = false;
3015	OSStatus status;
3016	if (!policy || !cert) return errSecParam;
3017
3018	certs = CFArrayCreate(NULL, (const void **)&cert, (CFIndex)1, &kCFTypeArrayCallBacks);
3019	status = SecTrustCreateWithCertificates(certs, policy, &trust);
3020	if(status) goto cleanup;
3021
3022	/* Set evaluation date, if specified (otherwise current date is implied) */
3023	if (date && (CFGetTypeID(date) == CFDateGetTypeID())) {
3024		status = SecTrustSetVerifyDate(trust, date);
3025		if(status) goto cleanup;
3026	}
3027
3028	/* Check whether this is the X509 Basic policy, which means chain building */
3029	props = SecPolicyCopyProperties(policy);
3030	if (props) {
3031		CFTypeRef oid = (CFTypeRef) CFDictionaryGetValue(props, kSecPolicyOid);
3032		if (oid && CFEqual(oid, kSecPolicyAppleX509Basic)) {
3033			needChain = true;
3034		}
3035	}
3036
3037	if (!needChain) {
3038		/* To make the evaluation as lightweight as possible, specify an empty array
3039		 * of keychains which will be searched for certificates.
3040		 */
3041		keychains = CFArrayCreate(NULL, NULL, 0, &kCFTypeArrayCallBacks);
3042		status = SecTrustSetKeychains(trust, keychains);
3043		if(status) goto cleanup;
3044
3045		/* To make the evaluation as lightweight as possible, specify an empty array
3046		 * of trusted anchors.
3047		 */
3048		anchors = CFArrayCreate(NULL, NULL, 0, &kCFTypeArrayCallBacks);
3049		status = SecTrustSetAnchorCertificates(trust, anchors);
3050		if(status) goto cleanup;
3051	}
3052
3053	/* All parameters are locked and loaded, ready to evaluate! */
3054	status = SecTrustEvaluate(trust, &trustResult);
3055	if(status) goto cleanup;
3056
3057	/* If we didn't provide trust anchors or a way to look for them,
3058	 * the evaluation will fail with kSecTrustResultRecoverableTrustFailure.
3059	 * However, we can tell whether the policy evaluation succeeded by
3060	 * looking at the per-cert status codes in the returned evidence.
3061	 */
3062	status = SecTrustGetResult(trust, &trustResult, &chain, &evidence);
3063	if(status) goto cleanup;
3064
3065	if (!(trustResult == kSecTrustResultProceed ||
3066		  trustResult == kSecTrustResultUnspecified ||
3067		  trustResult == kSecTrustResultRecoverableTrustFailure)) {
3068		/* The evaluation failed in a non-recoverable way */
3069		status = errSecCertificateCannotOperate;
3070		goto cleanup;
3071	}
3072
3073	/* If there are no per-cert policy status codes,
3074	 * and the cert has not expired, consider it valid for the policy.
3075	 */
3076	if((evidence != NULL) && _CanIgnoreLeafStatusCodes(evidence) &&
3077	   ((evidence[0].StatusBits & CSSM_CERT_STATUS_EXPIRED) == 0) &&
3078	   ((evidence[0].StatusBits & CSSM_CERT_STATUS_NOT_VALID_YET) == 0)) {
3079		status = errSecSuccess;
3080	}
3081	else {
3082		status = errSecCertificateCannotOperate;
3083	}
3084
3085cleanup:
3086	if(props) CFRelease(props);
3087	if(chain) CFRelease(chain);
3088	if(anchors) CFRelease(anchors);
3089	if(keychains) CFRelease(keychains);
3090	if(certs) CFRelease(certs);
3091	if(trust) CFRelease(trust);
3092
3093	return status;
3094}
3095
3096static OSStatus
3097_FilterWithDate(CFTypeRef validOnDate, SecCertificateRef cert)
3098{
3099	if (!validOnDate || !cert) return errSecParam;
3100
3101	CFAbsoluteTime at, nb, na;
3102	if (CFGetTypeID(validOnDate) == CFDateGetTypeID())
3103		at = CFDateGetAbsoluteTime((CFDateRef)validOnDate);
3104	else
3105		at = CFAbsoluteTimeGetCurrent();
3106
3107	OSStatus status = errSecSuccess;
3108	nb = SecCertificateNotValidBefore(cert);
3109	na = SecCertificateNotValidAfter(cert);
3110
3111	if (nb == 0 || na == 0 || nb == na)
3112		status = errSecCertificateCannotOperate;
3113	else if (at < nb)
3114		status = errSecCertificateNotValidYet;
3115	else if (at > na)
3116		status = errSecCertificateExpired;
3117
3118	return status;
3119}
3120
3121static OSStatus
3122_FilterWithTrust(Boolean trustedOnly, SecCertificateRef cert)
3123{
3124	if (!cert) return errSecParam;
3125	if (!trustedOnly) return errSecSuccess;
3126
3127	CFArrayRef certArray = CFArrayCreate(NULL, (const void**)&cert, 1, &kCFTypeArrayCallBacks);
3128	SecPolicyRef policy = SecPolicyCreateWithOID(kSecPolicyAppleX509Basic);
3129	OSStatus status = (policy == NULL) ? errSecPolicyNotFound : errSecSuccess;
3130
3131	if (!status) {
3132		SecTrustRef trust = NULL;
3133		status = SecTrustCreateWithCertificates(certArray, policy, &trust);
3134		if (!status) {
3135			SecTrustResultType trustResult;
3136			status = SecTrustEvaluate(trust, &trustResult);
3137			if (!status) {
3138				if (!(trustResult == kSecTrustResultProceed || trustResult == kSecTrustResultUnspecified)) {
3139					status = (trustResult == kSecTrustResultDeny) ? errSecTrustSettingDeny : errSecNotTrusted;
3140				}
3141			}
3142			CFRelease(trust);
3143		}
3144		CFRelease(policy);
3145	}
3146	if (certArray) {
3147		CFRelease(certArray);
3148	}
3149
3150	return status;
3151}
3152
3153static SecKeychainItemRef
3154CopyResolvedKeychainItem(CFTypeRef item)
3155{
3156	SecKeychainItemRef kcItem = NULL;
3157	OSStatus status;
3158	if (item) {
3159		if (CFGetTypeID(item) == CFDataGetTypeID()) {
3160			// persistent reference, resolve first
3161			status = SecKeychainItemCopyFromPersistentReference((CFDataRef)item, &kcItem);
3162		}
3163		else {
3164			// normal reference
3165			kcItem = (SecKeychainItemRef) CFRetain(item);
3166		}
3167		if (kcItem) {
3168			// ask for the item's class:
3169			// will return an error if the item has been deleted
3170			SecItemClass itemClass;
3171			SecKeychainItemRef certRef = NULL;
3172			if (CFGetTypeID(kcItem) == SecIdentityGetTypeID()) {
3173				status = SecIdentityCopyCertificate((SecIdentityRef)kcItem, (SecCertificateRef *)&certRef);
3174			}
3175			status = SecKeychainItemCopyAttributesAndData((certRef) ? certRef : kcItem, NULL, &itemClass, NULL, NULL, NULL);
3176			if (certRef) {
3177				CFRelease(certRef);
3178			}
3179			if (status) {
3180				CFRelease(kcItem);
3181				kcItem = NULL;
3182			}
3183		}
3184	}
3185	return kcItem;
3186}
3187
3188static OSStatus
3189UpdateKeychainSearchAndCopyNext(SecItemParams *params, CFTypeRef *item)
3190{
3191	// This function refreshes the search parameters in the specific case where
3192	// the caller is searching for kSecClassKey items but did not provide the
3193	// kSecAttrKeyClass. In that case, params->assumedKeyClass will be set, and
3194	// we must perform separate searches to obtain all results.
3195
3196	OSStatus status = errSecItemNotFound;
3197	if (!params || !params->assumedKeyClass || !params->query || !item)
3198		return status;
3199
3200	// Free the previous search reference and attribute list.
3201	if (params->search)
3202		CFRelease(params->search);
3203	params->search = NULL;
3204	_FreeAttrList(params->attrList);
3205	params->attrList = NULL;
3206
3207	// Make a copy of the query dictionary so we can set the key class parameter.
3208	CFMutableDictionaryRef dict = CFDictionaryCreateMutableCopy(NULL, 0, params->query);
3209	CFRelease(params->query);
3210	params->query = dict;
3211	CFDictionarySetValue(dict, kSecAttrKeyClass, params->assumedKeyClass);
3212
3213	// Determine the current item class for this search, and the next assumed key class.
3214	if (CFEqual(params->assumedKeyClass, kSecAttrKeyClassSymmetric)) {
3215		params->itemClass = kSecSymmetricKeyItemClass;
3216		params->assumedKeyClass = kSecAttrKeyClassPublic;
3217	} else if (CFEqual(params->assumedKeyClass, kSecAttrKeyClassPublic)) {
3218		params->itemClass = kSecPublicKeyItemClass;
3219		params->assumedKeyClass = kSecAttrKeyClassPrivate;
3220	} else {
3221		params->itemClass = kSecPrivateKeyItemClass;
3222		params->assumedKeyClass = NULL;
3223	}
3224
3225	// Rebuild the attribute list for the new key class.
3226	if (_CreateSecKeychainAttributeListFromDictionary(dict, params->itemClass, &params->attrList) == errSecSuccess) {
3227		// Create a new search reference for the new attribute list.
3228		if (SecKeychainSearchCreateFromAttributes(params->searchList,
3229			params->itemClass,
3230			(params->attrList->count == 0) ? NULL : params->attrList,
3231			(SecKeychainSearchRef*)&params->search) == errSecSuccess) {
3232			// Return the first matching item from the new search.
3233			// We won't come back here again until there are no more matching items for this search.
3234			status = SecKeychainSearchCopyNext((SecKeychainSearchRef)params->search, (SecKeychainItemRef*)item);
3235		}
3236	}
3237	return status;
3238}
3239
3240
3241static OSStatus
3242SecItemSearchCopyNext(SecItemParams *params, CFTypeRef *item)
3243{
3244	// Generic "copy next match" function for SecKeychainSearchRef or SecIdentitySearchRef.
3245	// Returns either a SecKeychainItemRef or a SecIdentityRef in the output parameter,
3246	// depending on the type of search reference.
3247
3248	OSStatus status;
3249	CFTypeRef search = (params) ? params->search : NULL;
3250	CFTypeID typeID = (search) ? CFGetTypeID(search) : 0;
3251	if (typeID == SecIdentitySearchGetTypeID()) {
3252		status = SecIdentitySearchCopyNext((SecIdentitySearchRef)search, (SecIdentityRef*)item);
3253	}
3254	else if (typeID == SecKeychainSearchGetTypeID()) {
3255		status = SecKeychainSearchCopyNext((SecKeychainSearchRef)search, (SecKeychainItemRef*)item);
3256		// Check if we need to refresh the search for the next key class
3257		while (status == errSecItemNotFound && params->assumedKeyClass != NULL)
3258			status = UpdateKeychainSearchAndCopyNext(params, item);
3259	}
3260	else if (typeID == 0 && (params->useItems || params->itemList)) {
3261		// No search available, but there is an item list available.
3262		// Return the next candidate item from the caller's item list
3263		CFArrayRef itemList = (params->useItems) ? params->useItems : params->itemList;
3264		CFIndex count = CFArrayGetCount(itemList);
3265		*item = (CFTypeRef) NULL;
3266		if (params->itemListIndex < count) {
3267			*item = (CFTypeRef)CFArrayGetValueAtIndex(itemList, params->itemListIndex++);
3268			if (*item) {
3269				// Potentially resolve persistent item references here, and
3270				// verify the item reference we're about to hand back is still
3271				// valid (it could have been deleted from the keychain while
3272				// our query was holding onto the itemList).
3273				*item = CopyResolvedKeychainItem(*item);
3274				if (*item && (CFGetTypeID(*item) == SecIdentityGetTypeID())) {
3275					// Persistent reference resolved to an identity, so return that type.
3276					params->returnIdentity = true;
3277				}
3278			}
3279		}
3280		status = (*item) ? errSecSuccess : errSecItemNotFound;
3281	}
3282	else {
3283		status = errSecItemNotFound;
3284	}
3285	return status;
3286}
3287
3288static OSStatus
3289FilterCandidateItem(CFTypeRef *item, SecItemParams *itemParams, SecIdentityRef *identity)
3290{
3291	if (!item || *item == NULL || !itemParams)
3292		return errSecItemNotFound;
3293
3294	OSStatus status;
3295	CFStringRef commonName = NULL;
3296	SecIdentityRef foundIdentity = NULL;
3297	if (CFGetTypeID(*item) == SecIdentityGetTypeID()) {
3298		// we found a SecIdentityRef, rather than a SecKeychainItemRef;
3299		// replace the found "item" with its associated certificate (which is the
3300		// item we actually want for purposes of getting attributes, data, or a
3301		// persistent data reference), and return the identity separately.
3302		SecCertificateRef certificate;
3303		status = SecIdentityCopyCertificate((SecIdentityRef) *item, &certificate);
3304		if (itemParams->returnIdentity) {
3305			foundIdentity = (SecIdentityRef) *item;
3306			if (identity) {
3307				*identity = foundIdentity;
3308			}
3309		}
3310		else {
3311			CFRelease(*item);
3312		}
3313		*item = (CFTypeRef)certificate;
3314	}
3315
3316	CFDictionaryRef query = itemParams->query;
3317
3318	if (itemParams->itemClass == kSecCertificateItemClass) {
3319		// perform string comparisons first
3320		CFStringCompareFlags flags = _StringCompareFlagsFromQuery(query);
3321		CFStringRef nameContains, nameStarts, nameEnds, nameExact;
3322		if (!CFDictionaryGetValueIfPresent(query, kSecMatchSubjectContains, (const void **)&nameContains))
3323			nameContains = NULL;
3324		if (!CFDictionaryGetValueIfPresent(query, kSecMatchSubjectStartsWith, (const void **)&nameStarts))
3325			nameStarts = NULL;
3326		if (!CFDictionaryGetValueIfPresent(query, kSecMatchSubjectEndsWith, (const void **)&nameEnds))
3327			nameEnds = NULL;
3328		if (!CFDictionaryGetValueIfPresent(query, kSecMatchSubjectWholeString, (const void **)&nameExact))
3329			nameExact = NULL;
3330		if (nameContains || nameStarts || nameEnds || nameExact) {
3331			status = SecCertificateCopyCommonName((SecCertificateRef)*item, &commonName);
3332			if (status || !commonName) goto filterOut;
3333		}
3334		if (nameContains) {
3335			CFRange range = CFStringFind(commonName, nameContains, flags);
3336			if (range.length < 1)
3337				goto filterOut;
3338			// certificate item contains string; proceed to next check
3339		}
3340		if (nameStarts) {
3341			CFRange range = CFStringFind(commonName, nameStarts, flags);
3342			if (range.length < 1 || range.location > 1)
3343				goto filterOut;
3344			// certificate item starts with string; proceed to next check
3345		}
3346		if (nameEnds) {
3347			CFRange range = CFStringFind(commonName, nameEnds, flags);
3348			if (range.length < 1 || range.location != (CFStringGetLength(commonName) - CFStringGetLength(nameEnds)))
3349				goto filterOut;
3350			// certificate item ends with string; proceed to next check
3351		}
3352		if (nameExact) {
3353			CFRange range = CFStringFind(commonName, nameExact, flags);
3354			if (range.length < 1 || (CFStringGetLength(commonName) != CFStringGetLength(nameExact)))
3355				goto filterOut;
3356			// certificate item exactly matches string; proceed to next check
3357		}
3358		if (itemParams->returnIdentity) {
3359			// if we already found and returned the identity, we can skip this
3360			if (!foundIdentity) {
3361				status = SecIdentityCreateWithCertificate(itemParams->searchList, (SecCertificateRef) *item, identity);
3362				if (status) goto filterOut;
3363			}
3364			// certificate item is part of an identity; proceed to next check
3365		}
3366		if (itemParams->policy) {
3367			status = _FilterWithPolicy(itemParams->policy, (CFDateRef)itemParams->validOnDate, (SecCertificateRef) *item);
3368			if (status) goto filterOut;
3369			// certificate item is valid for specified policy (and optionally specified date)
3370		}
3371		if (itemParams->validOnDate) {
3372			status = _FilterWithDate(itemParams->validOnDate, (SecCertificateRef) *item);
3373			if (status) goto filterOut;
3374			// certificate item is valid for specified date
3375		}
3376		if (itemParams->trustedOnly) {
3377			// if we are getting candidate items from a SecIdentitySearchCreateWithPolicy search,
3378			// their trust has already been validated and we can skip this part.
3379			if (!(foundIdentity && itemParams->returnIdentity && itemParams->policy)) {
3380				status = _FilterWithTrust(itemParams->trustedOnly, (SecCertificateRef) *item);
3381				if (status) goto filterOut;
3382			}
3383			// certificate item is trusted on this system
3384		}
3385	}
3386	if (itemParams->itemList) {
3387		Boolean foundMatch = FALSE;
3388		CFIndex idx, count = CFArrayGetCount(itemParams->itemList);
3389		for (idx=0; idx<count; idx++) {
3390			CFTypeRef anItem = (CFTypeRef) CFArrayGetValueAtIndex(itemParams->itemList, idx);
3391			SecKeychainItemRef realItem = NULL;
3392			SecCertificateRef aCert = NULL;
3393			if (anItem == NULL) {
3394				continue;
3395			}
3396			if (CFDataGetTypeID() == CFGetTypeID(anItem) &&
3397				errSecSuccess == SecKeychainItemCopyFromPersistentReference((CFDataRef)anItem, &realItem)) {
3398				anItem = realItem;
3399			}
3400			if (SecIdentityGetTypeID() == CFGetTypeID(anItem) &&
3401				errSecSuccess == SecIdentityCopyCertificate((SecIdentityRef)anItem, &aCert)) {
3402				anItem = aCert;
3403			}
3404			if (CFEqual(anItem, (CFTypeRef) *item)) {
3405				foundMatch = TRUE;
3406			}
3407			if (aCert) {
3408				CFRelease(aCert);
3409			}
3410			if (realItem) {
3411				CFRelease(realItem);
3412			}
3413			if (foundMatch) {
3414				break;
3415			}
3416		}
3417		if (!foundMatch) goto filterOut;
3418		// item was found on provided list
3419	}
3420
3421	if (foundIdentity && !identity) {
3422		CFRelease(foundIdentity);
3423	}
3424	if (commonName) {
3425		CFRelease(commonName);
3426	}
3427
3428	// if we get here, consider the item a match
3429	return errSecSuccess;
3430
3431filterOut:
3432	if (commonName) {
3433		CFRelease(commonName);
3434	}
3435	CFRelease(*item);
3436	*item = NULL;
3437	if (foundIdentity) {
3438		CFRelease(foundIdentity);
3439		if (identity) {
3440			*identity = NULL;
3441		}
3442	}
3443	return errSecItemNotFound;
3444}
3445
3446static OSStatus
3447AddItemResults(SecKeychainItemRef item,
3448	SecIdentityRef identity,
3449	SecItemParams *itemParams,
3450	CFAllocatorRef allocator,
3451	CFMutableArrayRef *items,
3452	CFTypeRef *result)
3453{
3454	// Given a found item (which may also be an identity), this function adds
3455	// the requested result types (specified in itemParams) to the appropriate
3456	// container as follows:
3457	//
3458	// 1. If there is only one result type (numResultTypes == 1) and only one
3459	//    match requested (maxMatches == 1), set *result directly.
3460	//
3461	// 2. If there are multiple result types (numResultTypes > 1), and only one
3462	//    match requested (maxMatches == 1), add each result type to itemDict
3463	//    and set itemDict as the value of *result.
3464	//
3465	// 3. If there is only one result type (numResultTypes == 1) and multiple
3466	//    possible matches (maxMatches > 1), add the result type to *items
3467	//    and set *items as the value of *result.
3468	//
3469	// 4. If there are multiple result types (numResultTypes > 1) and multiple
3470	//    possible matches (maxMatches > 1), add each result type to itemDict,
3471	//    add itemDict to *items, and set *items as the value of *result.
3472	//
3473	// Note that we allocate *items if needed.
3474
3475	if (!item || !itemParams || !result)
3476		return errSecParam;
3477
3478	if (itemParams->maxMatches > 1) {
3479		// if we can return more than one item, we must have an array
3480		if (!items)
3481			return errSecParam;
3482		else if (*items == NULL)
3483			*items = CFArrayCreateMutable(allocator, 0, &kCFTypeArrayCallBacks);
3484	}
3485
3486	OSStatus tmpStatus, status = errSecSuccess;
3487	CFMutableArrayRef itemArray = (items) ? *items : NULL;
3488	CFMutableDictionaryRef itemDict = NULL;
3489	if (itemParams->numResultTypes > 1) {
3490		// if we're returning more than one result type, each item we return must be a dictionary
3491		itemDict = CFDictionaryCreateMutable(allocator, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
3492	}
3493
3494	if (itemParams->returningRef) {
3495		const void* itemRef = (identity) ? (const void*)identity : (const void*)item;
3496		if (itemDict) {
3497			CFDictionaryAddValue(itemDict, kSecValueRef, itemRef);
3498		}
3499		else if (itemArray) {
3500			CFArrayAppendValue(itemArray, itemRef);
3501		}
3502		else {
3503			*result = CFRetain((CFTypeRef)itemRef);
3504		}
3505	}
3506
3507	if (itemParams->returningPersistentRef) {
3508		CFDataRef persistentRef;
3509		SecKeychainItemRef tmpItem = item;
3510		if (itemParams->identityRef) {
3511			tmpItem = (SecKeychainItemRef)itemParams->identityRef;
3512		}
3513		tmpStatus = SecKeychainItemCreatePersistentReference(tmpItem, &persistentRef);
3514		if (tmpStatus == errSecSuccess) {
3515			if (itemDict) {
3516				CFDictionaryAddValue(itemDict, kSecValuePersistentRef, persistentRef);
3517			}
3518			else if (itemArray) {
3519				CFArrayAppendValue(itemArray, persistentRef);
3520			}
3521			else {
3522				*result = CFRetain(persistentRef);
3523			}
3524			CFRelease(persistentRef);
3525		}
3526		else if (status == errSecSuccess) {
3527			status = tmpStatus;
3528		}
3529	}
3530
3531	if (itemParams->returningData) {
3532		UInt32 length;
3533		void *data;
3534		tmpStatus = SecKeychainItemCopyContent(item, NULL, NULL, &length, &data);
3535		if (tmpStatus == errSecSuccess) {
3536			CFDataRef dataRef = CFDataCreate(allocator, (UInt8 *)data, length);
3537			if (itemDict) {
3538				CFDictionaryAddValue(itemDict, kSecValueData, dataRef);
3539			}
3540			else if (itemArray) {
3541				CFArrayAppendValue(itemArray, dataRef);
3542			}
3543			else {
3544				*result = CFRetain(dataRef);
3545			}
3546			CFRelease(dataRef);
3547			(void) SecKeychainItemFreeContent(NULL, data);
3548		}
3549		else if (status == errSecSuccess) {
3550			status = tmpStatus;
3551		}
3552	}
3553
3554	if (itemParams->returningAttributes) {
3555		CFDictionaryRef attrsDict = NULL;
3556		SecItemClass itemClass;
3557		// since we have an item, allow its actual class to override the query-specified item class
3558		tmpStatus = SecKeychainItemCopyAttributesAndData(item, NULL, &itemClass, NULL, NULL, NULL);
3559		if (tmpStatus) {
3560			itemClass = itemParams->itemClass;
3561		}
3562		tmpStatus = _CreateAttributesDictionaryFromItem(allocator, itemClass, item, &attrsDict);
3563		if (attrsDict) {
3564			if (itemDict) {
3565				// add all keys and values from attrsDict to the item dictionary
3566				CFDictionaryApplyFunction(attrsDict, _AddDictValueToOtherDict, &itemDict);
3567			}
3568			else if (itemArray) {
3569				CFArrayAppendValue(itemArray, attrsDict);
3570			}
3571			else {
3572				*result = CFRetain(attrsDict);
3573			}
3574			CFRelease(attrsDict);
3575		}
3576		if (tmpStatus && (status == errSecSuccess)) {
3577			status = tmpStatus;
3578		}
3579	}
3580
3581	if (itemDict) {
3582		if (itemArray) {
3583			CFArrayAppendValue(itemArray, itemDict);
3584			CFRelease(itemDict);
3585			*result = itemArray;
3586		}
3587		else {
3588			*result = itemDict;
3589		}
3590	}
3591	else if (itemArray) {
3592		*result = itemArray;
3593	}
3594
3595	return status;
3596}
3597
3598CFDataRef _SecItemGetPersistentReference(CFTypeRef raw_item)
3599{
3600	try {
3601		Item item = ItemImpl::required((SecKeychainItemRef)raw_item);
3602		return item->getPersistentRef();
3603	} catch(...) {
3604		return NULL;
3605	}
3606}
3607
3608/******************************************************************************/
3609#pragma mark SecItem API functions
3610/******************************************************************************/
3611
3612//
3613// Approximate result of using iOS sec's copyNumber, 0 return could be zero, or error.
3614//
3615static SInt32 readNumber(CFTypeRef obj) {
3616    CFTypeID tid = CFGetTypeID(obj);
3617    SInt32 v = 0;
3618    if (tid == CFNumberGetTypeID()) {
3619        CFNumberGetValue((CFNumberRef)obj, kCFNumberSInt32Type, &v);
3620        return v;
3621    } else if (tid == CFBooleanGetTypeID()) {
3622        v = CFBooleanGetValue((CFBooleanRef)obj);
3623        return v;
3624    } else if (tid == CFStringGetTypeID()) {
3625        v = CFStringGetIntValue((CFStringRef)obj);
3626        CFStringRef t = CFStringCreateWithFormat(0, 0, CFSTR("%ld"), (long)v);
3627        /* If a string converted to an int isn't equal to the int printed as
3628         a string, return a CFStringRef instead. */
3629        if (!CFEqual(t, obj)) {
3630            CFRelease(t);
3631            return 0;
3632        }
3633        CFRelease(t);
3634        return v;
3635    } else
3636        return NULL;
3637}
3638
3639//
3640// Function to ensure the syncable keychain is unlocked.
3641// Currently, this means unlocking the login keychain,
3642// which will also unlock the keybag as a side effect.
3643//
3644static OSStatus SecItemUnlockSynchronizableKeychain()
3645{
3646	SecKeychainRef keychain = NULL;
3647	OSStatus status = SecKeychainCopyLogin(&keychain);
3648	if (!status) {
3649		status = SecKeychainUnlock(keychain, 0, NULL, false);
3650	}
3651	CFReleaseSafe(keychain);
3652	return status;
3653}
3654
3655//
3656// Function to check whether the kSecAttrSynchronizable flag is set in the query.
3657//
3658static Boolean SecItemSynchronizable(CFDictionaryRef query)
3659{
3660	CFTypeRef value = CFDictionaryGetValue(query, kSecAttrSynchronizable);
3661	Boolean result = (value && readNumber(value));
3662
3663	return result;
3664}
3665
3666//
3667// Function to check whether the kSecAttrSynchronizable flag is set in the query,
3668// and has the special value of kSecAttrSynchronizableAny.
3669//
3670static Boolean SecItemSynchronizableAny(CFDictionaryRef query)
3671{
3672	CFTypeRef value = CFDictionaryGetValue(query, kSecAttrSynchronizable);
3673	if (value) {
3674		return (CFGetTypeID(value) == CFStringGetTypeID() &&
3675				CFEqual(value, kSecAttrSynchronizableAny));
3676	}
3677	return false;
3678}
3679
3680//
3681// Function to check whether the kSecAttrSynchronizable attribute is being updated.
3682//
3683static Boolean SecItemHasSynchronizableUpdate(Boolean synchronizable, CFDictionaryRef changes)
3684{
3685	CFTypeRef newValue = CFDictionaryGetValue(changes, kSecAttrSynchronizable);
3686	if (!newValue)
3687		return false;
3688
3689	Boolean new_sync = readNumber(newValue);
3690	Boolean old_sync = synchronizable;
3691
3692	return (old_sync != new_sync);
3693}
3694
3695//
3696// Returns true if keychain syncing is globally enabled.
3697//
3698static Boolean SecItemSyncEnabled()
3699{
3700	static dispatch_once_t onceToken;
3701	static Boolean syncEnabled = true;
3702
3703	//sudo defaults write /Library/Preferences/com.apple.security SecItemSynchronizable -bool YES
3704	dispatch_once(&onceToken, ^{
3705			CFTypeRef sync = (CFNumberRef)CFPreferencesCopyValue(CFSTR("SecItemSynchronizable"), CFSTR("com.apple.security"), kCFPreferencesAnyUser, kCFPreferencesCurrentHost);
3706
3707			if (sync && CFGetTypeID(sync) == CFBooleanGetTypeID()) {
3708			syncEnabled = CFBooleanGetValue((CFBooleanRef)sync);
3709			CFRelease(sync);
3710			}
3711			});
3712
3713	return syncEnabled;
3714}
3715
3716//
3717// Function to check whether a synchronizable persistent reference was provided.
3718//
3719static Boolean SecItemHasSynchronizablePersistentReference(CFDictionaryRef query)
3720{
3721	CFTypeRef value = CFDictionaryGetValue(query, kSecValuePersistentRef);
3722	if (value) {
3723		/* Synchronizable persistent ref consists of the sqlite rowid and 4-byte class value */
3724		const CFIndex kSynchronizablePersistentRefLength = sizeof(int64_t) + 4;
3725		return (CFGetTypeID(value) == CFDataGetTypeID() &&
3726				CFDataGetLength((CFDataRef)value) == kSynchronizablePersistentRefLength);
3727	}
3728	return false;
3729}
3730
3731//
3732// Function to apply changes to a mutable dictionary.
3733// (CFDictionaryApplierFunction, called by CFDictionaryApplyFunction)
3734//
3735static void SecItemApplyChanges(const void *key, const void *value, void *context)
3736{
3737	CFMutableDictionaryRef dict = (CFMutableDictionaryRef) context;
3738	if (!dict) return;
3739
3740	CFDictionarySetValue(dict, key, value);
3741}
3742
3743//
3744// Function to change matching items from non-syncable to syncable
3745// (if toSyncable is true), otherwise from syncable to non-syncable.
3746// This currently moves items between keychain containers.
3747//
3748static OSStatus SecItemChangeSynchronizability(CFDictionaryRef query, CFDictionaryRef changes, Boolean toSyncable)
3749{
3750	// Note: the input query dictionary is a mutable copy of the query originally
3751	// provided by the caller as the first parameter to SecItemUpdate. It may not
3752	// specify returning attributes or data, but we will need both to make a copy.
3753	//
3754	CFDictionaryRemoveValue((CFMutableDictionaryRef)query, kSecReturnRef);
3755	CFDictionaryRemoveValue((CFMutableDictionaryRef)query, kSecReturnPersistentRef);
3756	CFDictionaryRemoveValue((CFMutableDictionaryRef)query, kSecReturnData);
3757	CFDictionarySetValue((CFMutableDictionaryRef)query, kSecReturnAttributes, kCFBooleanTrue);
3758	if (NULL == CFDictionaryGetValue(changes, kSecValueData))
3759		CFDictionarySetValue((CFMutableDictionaryRef)query, kSecReturnData, kCFBooleanTrue);
3760
3761	CFTypeRef result;
3762	OSStatus status;
3763	if (toSyncable)
3764		status = SecItemCopyMatching_osx(query, &result);
3765	else
3766		status = SecItemCopyMatching_ios(query, &result);
3767
3768	if (status)
3769		return status;
3770	if (!result)
3771		return errSecItemNotFound;
3772
3773	CFMutableArrayRef items;
3774	if (CFGetTypeID(result) != CFArrayGetTypeID()) {
3775		items = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
3776		CFArrayAppendValue(items, result);
3777		CFRelease(result);
3778	}
3779	else {
3780		items = (CFMutableArrayRef)result;
3781	}
3782
3783	CFIndex idx, count = (items) ? CFArrayGetCount(items) : 0;
3784	int priority = LOG_DEBUG;
3785	OSStatus err = 0;
3786	for (idx = 0; idx < count; idx++) {
3787		CFDictionaryRef dict = (CFDictionaryRef) CFArrayGetValueAtIndex(items, idx);
3788		CFMutableDictionaryRef item = (CFMutableDictionaryRef)
3789			SecItemCopyTranslatedAttributes(dict,
3790				CFDictionaryGetValue(query, kSecClass),
3791				(toSyncable) ? true : false /*iOSOut*/,
3792				true /*pruneMatch*/,
3793				true /*pruneSync*/,
3794				true /*pruneReturn*/,
3795				false /*pruneData*/,
3796				(toSyncable) ? true : false /*pruneAccess*/);
3797		// hold onto the query before applying changes, in case the item already exists.
3798		// note that we cannot include the creation or modification dates from our
3799		// found item in this query, as they may not match the item in the other keychain.
3800		CFMutableDictionaryRef itemQuery = CFDictionaryCreateMutableCopy(NULL, 0, item);
3801		CFDictionaryRemoveValue(itemQuery, kSecAttrCreationDate);
3802		CFDictionaryRemoveValue(itemQuery, kSecAttrModificationDate);
3803		// apply changes to the item dictionary that we will pass to SecItemAdd
3804		CFDictionaryApplyFunction(changes, SecItemApplyChanges, item);
3805		if (toSyncable) {
3806			CFDictionarySetValue(item, kSecAttrSynchronizable, kCFBooleanTrue);
3807			status = SecItemAdd_ios(item, NULL);
3808			secitemlog(priority, "ChangeSync: SecItemAdd_ios=%d", status);
3809			if (errSecDuplicateItem == status) {
3810				// find and apply changes to the existing syncable item.
3811				CFDictionarySetValue(itemQuery, kSecAttrSynchronizable, kCFBooleanTrue);
3812				status = SecItemUpdate_ios(itemQuery, changes);
3813				secitemlog(priority, "ChangeSync: SecItemUpdate_ios=%d", status);
3814			}
3815			if (errSecSuccess == status) {
3816				CFDictionarySetValue(itemQuery, kSecAttrSynchronizable, kCFBooleanFalse);
3817				status = SecItemDelete_osx(itemQuery);
3818				secitemlog(priority, "ChangeSync: SecItemDelete_osx=%d", status);
3819			}
3820		}
3821		else {
3822			CFDictionarySetValue(item, kSecAttrSynchronizable, kCFBooleanFalse);
3823			status = SecItemAdd_osx(item, NULL);
3824			secitemlog(priority, "ChangeSync: SecItemAdd_osx=%d", status);
3825			if (errSecDuplicateItem == status) {
3826				// find and apply changes to the existing non-syncable item.
3827				CFDictionarySetValue(itemQuery, kSecAttrSynchronizable, kCFBooleanFalse);
3828				status = SecItemUpdate_osx(itemQuery, changes);
3829				secitemlog(priority, "ChangeSync: SecItemUpdate_osx=%d", status);
3830			}
3831			if (errSecSuccess == status) {
3832				CFDictionarySetValue(itemQuery, kSecAttrSynchronizable, kCFBooleanTrue);
3833				status = SecItemDelete_ios(itemQuery);
3834				secitemlog(priority, "ChangeSync: SecItemDelete_ios=%d", status);
3835			}
3836		}
3837		CFReleaseSafe(item);
3838		CFReleaseSafe(itemQuery);
3839		if (status)
3840			err = status;
3841	}
3842	CFReleaseSafe(items);
3843
3844	return err;
3845}
3846
3847
3848extern "C" {
3849
3850CFTypeRef
3851SecItemCreateFromAttributeDictionary(CFDictionaryRef refAttributes) {
3852	CFTypeRef ref = NULL;
3853	CFStringRef key_class_string = (CFStringRef)CFDictionaryGetValue(refAttributes, kSecClass);
3854	SecItemClass key_class;
3855	bool key_class_found = false;
3856
3857	if (CFEqual(key_class_string, kSecClassGenericPassword)) {
3858		key_class = kSecGenericPasswordItemClass;
3859		key_class_found = true;
3860	}
3861	if (CFEqual(key_class_string, kSecClassInternetPassword)) {
3862		key_class = kSecInternetPasswordItemClass;
3863		key_class_found = true;
3864	}
3865
3866	if (key_class_found) {
3867		// we carry v_Data around here so the *_ios calls can find it and locate
3868		// their own data.   Putting things in the attribute list doesn't help as
3869		// the osx keychainitem and item calls bail when they don't see a keychain
3870		// object.   If we need to make them work we either have to bridge them, or
3871		// find a way to craft a workable keychain object.   #if'ed code left below
3872		// in case we need to go down that path.
3873
3874		struct SecKeychainAttributeList *attrs = (struct SecKeychainAttributeList *)malloc(sizeof(struct SecKeychainAttributeList) + sizeof(struct SecKeychainAttribute) * 0);
3875		attrs->attr = (struct SecKeychainAttribute *)(attrs + 1);
3876		attrs->count = 0;
3877		CFTypeRef v;
3878#if 0
3879		// The C++ string objects need to last at least as long as the attr struct.
3880		string account;
3881
3882		v = CFDictionaryGetValue(refAttributes, CFSTR("mdat"));
3883		if (v) {
3884			attrs->attr[attrs->count].tag = kSecModDateItemAttr;
3885			// XXX need to convert to YYYYMMDDhhmmSSZ
3886			attrs->attr[attrs->count].data = (void*)"19690223140232Z";
3887			attrs->attr[attrs->count].length = strlen((char*)(attrs->attr[attrs->count].data));
3888			attrs->count++;
3889		}
3890		v = CFDictionaryGetValue(refAttributes, CFSTR("cdat"));
3891		if (v) {
3892			attrs->attr[attrs->count].tag = kSecCreationDateItemAttr;
3893			// XXX need to convert to YYYYMMDDhhmmSSZ
3894			attrs->attr[attrs->count].data = (void*)"19690223140232Z";
3895			attrs->attr[attrs->count].length = strlen((char*)(attrs->attr[attrs->count].data));
3896			attrs->count++;
3897		}
3898
3899		v = CFDictionaryGetValue(refAttributes, CFSTR("acct"));
3900		if (v) {
3901			attrs->attr[attrs->count].tag = kSecAccountItemAttr;
3902			account = cfString((CFStringRef)v);
3903			attrs->attr[attrs->count].data = (void*)(account.c_str());
3904			attrs->attr[attrs->count].length = account.length();
3905			attrs->count++;
3906		}
3907
3908		// class isn't treated as an attribute by the creation API
3909
3910		v = CFDictionaryGetValue(refAttributes, CFSTR("svce"));
3911		if (v) {
3912			attrs->attr[attrs->count].tag = kSecServiceItemAttr;
3913			account = cfString((CFStringRef)v);
3914			attrs->attr[attrs->count].data = (void*)(account.c_str());
3915			attrs->attr[attrs->count].length = account.length();
3916			attrs->count++;
3917		}
3918
3919		v = CFDictionaryGetValue(refAttributes, CFSTR("acct"));
3920		if (v) {
3921			attrs->attr[attrs->count].tag = kSecLabelItemAttr;
3922			account = cfString((CFStringRef)v);
3923			attrs->attr[attrs->count].data = (void*)(account.c_str());
3924			attrs->attr[attrs->count].length = account.length();
3925			attrs->count++;
3926		}
3927#endif
3928		Item item = Item(key_class, attrs, 0, "");
3929		ItemImpl *real_item = item.get();
3930		v = CFDictionaryGetValue(refAttributes, kSecValuePersistentRef);
3931		if (v) {
3932			real_item->setPersistentRef((CFDataRef)v);
3933		}
3934		ref = real_item->handle();
3935	} else {
3936		// keys, certs, identities are not currently sync'able.
3937		ref = NULL;
3938	}
3939	return ref;
3940}
3941
3942/*
3943 * SecItemValidateAppleApplicationGroupAccess determines if the caller
3944 * is a member of the specified application group, and is signed by Apple.
3945 */
3946OSStatus
3947SecItemValidateAppleApplicationGroupAccess(CFStringRef group)
3948{
3949	SecTrustedApplicationRef app = NULL;
3950	SecRequirementRef requirement = NULL;
3951	SecCodeRef code = NULL;
3952	OSStatus status = errSecParam;
3953
3954	if (group) {
3955		CFIndex length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(group), kCFStringEncodingUTF8) + 1;
3956		char* buffer = (char*) malloc(length);
3957		if (buffer) {
3958			if (CFStringGetCString(group, buffer, length, kCFStringEncodingUTF8)) {
3959				status = SecTrustedApplicationCreateApplicationGroup(buffer, NULL, &app);
3960			}
3961			free(buffer);
3962		} else {
3963			status = errSecMemoryError;
3964		}
3965	}
3966	if (!status) {
3967		status = SecTrustedApplicationCopyRequirement(app, &requirement);
3968	}
3969	if (!status) {
3970		status = SecCodeCopySelf(kSecCSDefaultFlags, &code);
3971	}
3972	if (!status) {
3973		status = SecCodeCheckValidity(code, kSecCSDefaultFlags, requirement);
3974	}
3975
3976	CFReleaseSafe(code);
3977	CFReleaseSafe(requirement);
3978	CFReleaseSafe(app);
3979	return status;
3980}
3981
3982/*
3983 * SecItemCopyTranslatedAttributes accepts a user-provided attribute dictionary
3984 * and attempts to return a sanitized copy for passing to the underlying
3985 * platform-specific implementation code.
3986 *
3987 * If iOSOut is true, one or more translations may apply:
3988 *   - SecKeychain refs are removed, since there aren't multiple keychains
3989 *   - SecPolicy refs are removed, since they can't be externalized
3990 *   - SecAccess refs are removed, and potentially translated to entitlements
3991 *
3992 * If pruneMatch is true, kSecMatch* attributes are removed; this avoids
3993 * parameter errors due to strict input checks in secd, which only permits
3994 * these constants for calls to SecItemCopyMatching.
3995 *
3996 * If pruneSync is true, the kSecAttrSynchronizable attribute is removed.
3997 * This permits a query to be reused for non-synchronizable items, or to
3998 * resolve a search based on a persistent item reference for iOS.
3999 *
4000 * If pruneReturn is true, kSecReturn* attributes are removed; this avoids
4001 * parameter errors due to strict input checks in secd, which do not permit
4002 * these constants for calls to SecItemUpdate.
4003 */
4004CFDictionaryRef
4005SecItemCopyTranslatedAttributes(CFDictionaryRef inOSXDict, CFTypeRef itemClass,
4006	bool iOSOut, bool pruneMatch, bool pruneSync, bool pruneReturn, bool pruneData, bool pruneAccess)
4007{
4008	CFMutableDictionaryRef result = CFDictionaryCreateMutableCopy(NULL, 0, inOSXDict);
4009	if (result == NULL) {
4010		return result;
4011	}
4012
4013	if (pruneSync) {
4014		CFDictionaryRemoveValue(result, kSecAttrSynchronizable);
4015	}
4016
4017	if (pruneMatch) {
4018		/* Match constants are only supported on iOS for SecItemCopyMatching,
4019		 * and will generate an error if passed to other SecItem API functions;
4020		 * on OS X, they're just ignored if not applicable for the context.
4021		 */
4022		CFDictionaryRemoveValue(result, kSecMatchPolicy);
4023		CFDictionaryRemoveValue(result, kSecMatchItemList);
4024		CFDictionaryRemoveValue(result, kSecMatchSearchList);
4025		CFDictionaryRemoveValue(result, kSecMatchIssuers);
4026		CFDictionaryRemoveValue(result, kSecMatchEmailAddressIfPresent);
4027		CFDictionaryRemoveValue(result, kSecMatchSubjectContains);
4028		CFDictionaryRemoveValue(result, kSecMatchCaseInsensitive);
4029		CFDictionaryRemoveValue(result, kSecMatchTrustedOnly);
4030		CFDictionaryRemoveValue(result, kSecMatchValidOnDate);
4031		CFDictionaryRemoveValue(result, kSecMatchLimit);
4032		CFDictionaryRemoveValue(result, kSecMatchLimitOne);
4033		CFDictionaryRemoveValue(result, kSecMatchLimitAll);
4034	}
4035
4036	if (pruneReturn) {
4037		/* Return constants are not supported on iOS for SecItemUpdate,
4038		 * where they will generate an error; on OS X, they're just ignored
4039		 * if not applicable for the context.
4040		 */
4041		CFDictionaryRemoveValue(result, kSecReturnData);
4042		CFDictionaryRemoveValue(result, kSecReturnAttributes);
4043		CFDictionaryRemoveValue(result, kSecReturnRef);
4044		CFDictionaryRemoveValue(result, kSecReturnPersistentRef);
4045	}
4046
4047	if (pruneData) {
4048		/* Searching on data is not supported. */
4049		CFDictionaryRemoveValue(result, kSecValueData);
4050	}
4051
4052    if (pruneAccess) {
4053        /* Searching on access lists is not supported */
4054        CFDictionaryRemoveValue(result, kSecAttrAccess);
4055    }
4056
4057	if (iOSOut) {
4058		/* Remove kSecMatchSearchList (value is array of SecKeychainRef);
4059		 * cannot specify a keychain search list on iOS
4060		 */
4061		CFDictionaryRemoveValue(result, kSecMatchSearchList);
4062
4063		/* Remove kSecUseKeychain (value is a SecKeychainRef);
4064		 * cannot specify a keychain on iOS
4065		 */
4066		CFDictionaryRemoveValue(result, kSecUseKeychain);
4067
4068		/* Remove kSecMatchPolicy (value is a SecPolicyRef);
4069		 * TODO: need a way to externalize and restore a policy instance
4070		 */
4071		CFDictionaryRemoveValue(result, kSecMatchPolicy);
4072
4073		/* Potentially translate kSecAttrAccess (value is a SecAccessRef),
4074		 * unless kSecAttrAccessGroup has already been specified.
4075		 */
4076		SecAccessRef access = (SecAccessRef) CFDictionaryGetValue(result, kSecAttrAccess);
4077		CFStringRef accessGroup = (CFStringRef) CFDictionaryGetValue(result, kSecAttrAccessGroup);
4078		if (access != NULL && accessGroup == NULL) {
4079			/* Translate "InternetAccounts" application group to an access group */
4080			if (errSecSuccess == SecItemValidateAppleApplicationGroupAccess(CFSTR("InternetAccounts"))) {
4081				/* The caller is a valid member of the application group. */
4082				CFStringRef groupName = CFSTR("appleaccount");
4083				CFTypeRef value = CFDictionaryGetValue(result, kSecAttrAuthenticationType);
4084				if (value && CFEqual(value, kSecAttrAuthenticationTypeHTMLForm)) {
4085					groupName = CFSTR("com.apple.cfnetwork");
4086				}
4087				CFDictionarySetValue(result, kSecAttrAccessGroup, groupName);
4088			}
4089		}
4090		CFDictionaryRemoveValue(result, kSecAttrAccess);
4091
4092		/* If item is specified by direct reference, and this is an iOS search,
4093		 * replace it with a persistent reference.
4094		 */
4095        CFTypeRef directRef = CFDictionaryGetValue(result, kSecValueRef);
4096		if (directRef) {
4097			CFDataRef persistentRef = _SecItemGetPersistentReference(directRef);
4098			if (persistentRef) {
4099                CFDictionarySetValue(result, kSecValuePersistentRef, persistentRef);
4100			}
4101			CFDictionaryRemoveValue(result, kSecValueRef);
4102		}
4103
4104		/* If item is specified by persistent reference, and this is an iOS search,
4105		 * remove the synchronizable attribute as it will be rejected by secd.
4106		 */
4107		CFTypeRef persistentRef = CFDictionaryGetValue(result, kSecValuePersistentRef);
4108		if (persistentRef) {
4109			CFDictionaryRemoveValue(result, kSecAttrSynchronizable);
4110		}
4111
4112		/* Remove kSecAttrModificationDate; this should never be used as criteria
4113		 * for a search, or to add/modify an item. (If we are cloning an item
4114		 * and want to keep its modification date, we don't call this function.)
4115		 * It turns out that some clients are using the full attributes dictionary
4116		 * returned by SecItemCopyMatching as a query to find the same item later,
4117		 * which won't work once the item is updated.
4118		 */
4119		CFDictionaryRemoveValue(result, kSecAttrModificationDate);
4120    }
4121	else {
4122		/* iOS doesn't add the class attribute, so we must do it here. */
4123		if (itemClass)
4124			CFDictionarySetValue(result, kSecClass, itemClass);
4125
4126		/* Remove attributes which are not part of the OS X database schema. */
4127		CFDictionaryRemoveValue(result, kSecAttrAccessible);
4128		CFDictionaryRemoveValue(result, kSecAttrAccessGroup);
4129		CFDictionaryRemoveValue(result, kSecAttrSynchronizable);
4130		CFDictionaryRemoveValue(result, kSecAttrTombstone);
4131	}
4132
4133	return result;
4134}
4135
4136/*
4137 * SecItemCopyMergedResults takes two input objects, which may be containers,
4138 * and returns a retained object which merges the results. Merging depends on the
4139 * result type. If each result is valid and is not an array, then only one match was
4140 * requested; in that case, the syncable (ios) match is preferred.
4141 *
4142 * FIXME: There are some edge cases still to deal with; e.g. if the OSX search specified a
4143 * particular keychain to search, we do not want to merge in any IOS results. Also, may need
4144 * to filter out duplicates if two items differ only in the sync attribute.
4145 */
4146CFTypeRef
4147SecItemCopyMergedResults(CFDictionaryRef query, CFTypeRef result_osx, CFTypeRef result_ios)
4148{
4149	CFTypeID id_osx = (result_osx) ? CFGetTypeID(result_osx) : 0;
4150	CFTypeID id_ios = (result_ios) ? CFGetTypeID(result_ios) : 0;
4151	CFTypeID id_array = CFArrayGetTypeID();
4152	if ((id_osx == id_array) && (id_ios == id_array)) {
4153		// Fold the arrays into one.
4154		CFMutableArrayRef results = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
4155		CFArrayAppendArray(results, (CFArrayRef)result_ios, CFRangeMake(0, CFArrayGetCount((CFArrayRef)result_ios)));
4156		CFArrayAppendArray(results, (CFArrayRef)result_osx, CFRangeMake(0, CFArrayGetCount((CFArrayRef)result_osx)));
4157		return results;
4158	}
4159	// Result type is not an array, so only one match can be returned.
4160	return (id_ios) ? CFRetain(result_ios) : CFRetain(result_osx);
4161}
4162
4163} /* extern "C" */
4164
4165
4166OSStatus
4167SecItemCopyMatching(CFDictionaryRef query, CFTypeRef *result)
4168{
4169	secitemlog(LOG_NOTICE, "SecItemCopyMatching");
4170	if (!query) {
4171		return errSecParam;
4172	}
4173	secitemshow(query, "SecItemCopyMatching query:");
4174
4175	OSStatus status_osx = errSecItemNotFound, status_ios = errSecItemNotFound;
4176	CFTypeRef result_osx = NULL, result_ios = NULL;
4177	Boolean sync_enabled = SecItemSyncEnabled();
4178	Boolean search_ios = SecItemSynchronizable(query);
4179	Boolean merge_search = SecItemSynchronizableAny(query);
4180	Boolean persistref_ios = SecItemHasSynchronizablePersistentReference(query);
4181
4182	if (sync_enabled && (merge_search || persistref_ios || search_ios)) {
4183		CFDictionaryRef attrs_ios = SecItemCopyTranslatedAttributes(query,
4184			CFDictionaryGetValue(query, kSecClass), true, false, false, false, true, true);
4185		if (!attrs_ios) {
4186			status_ios = errSecParam;
4187		}
4188		else {
4189            SecItemUnlockSynchronizableKeychain();
4190            status_ios = SecItemCopyMatching_ios(attrs_ios, &result_ios);
4191			CFRelease(attrs_ios);
4192		}
4193		secitemlog(LOG_NOTICE, "SecItemCopyMatching_ios result: %d", status_ios);
4194		if (!merge_search || persistref_ios) {
4195			AssignOrReleaseResult(result_ios, result);
4196			return status_ios; // no need to search non-syncable keychains
4197		}
4198	}
4199
4200	CFDictionaryRef attrs_osx = SecItemCopyTranslatedAttributes(query,
4201		CFDictionaryGetValue(query, kSecClass), false, false, true, false, true, true);
4202	if (!attrs_osx) {
4203		status_osx = errSecParam;
4204	}
4205	else {
4206		status_osx = SecItemCopyMatching_osx(attrs_osx, &result_osx);
4207		CFRelease(attrs_osx);
4208	}
4209	secitemlog(LOG_NOTICE, "SecItemCopyMatching_osx result: %d", status_osx);
4210
4211	// If one of the searches failed to occur or produce results, we can eliminate it
4212	if (result_ios == NULL) {
4213		AssignOrReleaseResult(result_osx, result);
4214		return status_osx; // we can only have non-syncable results
4215	}
4216	if (result_osx == NULL) {
4217		AssignOrReleaseResult(result_ios, result);
4218		return status_ios; // we can only have syncable results
4219	}
4220
4221	// If we get here, need to merge results
4222	CFTypeRef result_merged = SecItemCopyMergedResults(query, result_osx, result_ios);
4223	CFReleaseSafe(result_osx);
4224	CFReleaseSafe(result_ios);
4225	AssignOrReleaseResult(result_merged, result);
4226
4227	if (status_osx == status_ios) {
4228		return status_osx; // both searches produced the same result
4229	}
4230	else if (!status_osx || !status_ios) {
4231		return errSecSuccess; // one of the searches succeeded
4232	}
4233	else if (status_osx == errSecItemNotFound) {
4234		return status_ios; // this failure was more interesting
4235	}
4236	return status_osx;
4237}
4238
4239OSStatus
4240SecItemAdd(CFDictionaryRef attributes, CFTypeRef *result)
4241{
4242	secitemlog(LOG_NOTICE, "SecItemAdd");
4243	if (!attributes) {
4244		return errSecParam;
4245	}
4246	else if (result) {
4247		*result = NULL;
4248	}
4249	secitemshow(attributes, "SecItemAdd attrs:");
4250
4251	OSStatus status_osx, status_ios;
4252	CFTypeRef result_osx = NULL, result_ios = NULL;
4253	Boolean sync_enabled = SecItemSyncEnabled();
4254	Boolean add_ios = SecItemSynchronizable(attributes);
4255
4256	if (sync_enabled && add_ios) {
4257		CFDictionaryRef attrs_ios = SecItemCopyTranslatedAttributes(attributes,
4258			NULL, true, true, false, false, false, false);
4259		if (!attrs_ios) {
4260			status_ios = errSecParam;
4261		}
4262		else {
4263            SecItemUnlockSynchronizableKeychain();
4264            status_ios = SecItemAdd_ios(attrs_ios, &result_ios);
4265			CFRelease(attrs_ios);
4266		}
4267		secitemlog(LOG_NOTICE, "SecItemAdd_ios result: %d", status_ios);
4268		if (result)
4269			*result = result_ios;
4270		else
4271			CFReleaseSafe(result_ios);
4272		return status_ios;
4273	}
4274
4275	CFDictionaryRef attrs_osx = SecItemCopyTranslatedAttributes(attributes,
4276		NULL, false, false, true, false, false, false);
4277	if (!attrs_osx) {
4278		status_osx = errSecParam;
4279	}
4280	else {
4281		status_osx = SecItemAdd_osx(attrs_osx, &result_osx);
4282		CFRelease(attrs_osx);
4283	}
4284	secitemlog(LOG_NOTICE, "SecItemAdd_osx result: %d", status_osx);
4285	if (result)
4286		*result = result_osx;
4287	else
4288		CFReleaseSafe(result_osx);
4289	return status_osx;
4290}
4291
4292OSStatus
4293SecItemUpdate(CFDictionaryRef query, CFDictionaryRef attributesToUpdate)
4294{
4295	secitemlog(LOG_NOTICE, "SecItemUpdate");
4296	if (!query || !attributesToUpdate) {
4297		return errSecParam;
4298	}
4299	secitemshow(query, "SecItemUpdate query:");
4300	secitemshow(attributesToUpdate, "SecItemUpdate attrs:");
4301
4302	OSStatus status_osx = errSecItemNotFound, status_ios = errSecItemNotFound;
4303	Boolean sync_enabled = SecItemSyncEnabled();
4304	Boolean search_ios = SecItemSynchronizable(query);
4305	Boolean merge_search = SecItemSynchronizableAny(query);
4306	Boolean persistref_ios = SecItemHasSynchronizablePersistentReference(query);
4307
4308	if (sync_enabled && (merge_search || persistref_ios || search_ios)) {
4309		CFDictionaryRef attrs_ios = SecItemCopyTranslatedAttributes(query,
4310			CFDictionaryGetValue(query, kSecClass), true, true, false, true, true, true);
4311		if (!attrs_ios) {
4312			status_ios = errSecParam;
4313		}
4314		else {
4315			SecItemUnlockSynchronizableKeychain();
4316            if (SecItemHasSynchronizableUpdate(true, attributesToUpdate))
4317                status_ios = SecItemChangeSynchronizability(attrs_ios, attributesToUpdate, false);
4318            else
4319                status_ios = SecItemUpdate_ios(attrs_ios, attributesToUpdate);
4320			CFRelease(attrs_ios);
4321		}
4322		secitemlog(LOG_NOTICE, "SecItemUpdate_ios result: %d", status_ios);
4323		if (!merge_search || persistref_ios)
4324			return status_ios;
4325	}
4326
4327	CFDictionaryRef attrs_osx = SecItemCopyTranslatedAttributes(query,
4328		CFDictionaryGetValue(query, kSecClass), false, false, true, true, true, true);
4329	if (!attrs_osx) {
4330		status_osx = errSecParam;
4331	}
4332	else {
4333		if (SecItemHasSynchronizableUpdate(false, attributesToUpdate))
4334			status_osx = SecItemChangeSynchronizability(attrs_osx, attributesToUpdate, true);
4335		else
4336			status_osx = SecItemUpdate_osx(attrs_osx, attributesToUpdate);
4337
4338		CFRelease(attrs_osx);
4339	}
4340	secitemlog(LOG_NOTICE, "SecItemUpdate_osx result: %d", status_osx);
4341	if (merge_search) {
4342		// Harmonize the result of the update attempts.
4343		if (status_osx == status_ios) {
4344			// both updates produced the same result
4345			return status_ios;
4346		}
4347		else if (!status_osx || !status_ios) {
4348			// one of the updates succeeded, but the other failed
4349			if (status_osx == errSecItemNotFound || status_ios == errSecItemNotFound)
4350				return errSecSuccess; // item only found in one keychain
4351			else
4352				return (status_osx) ? status_osx : status_ios; // return the error
4353		}
4354		else if (status_osx == errSecItemNotFound) {
4355			// both updates failed, status_ios failure is more interesting
4356			// since the item was actually found
4357			return status_ios;
4358		}
4359	}
4360	return status_osx;
4361}
4362
4363OSStatus
4364SecItemDelete(CFDictionaryRef query)
4365{
4366	secitemlog(LOG_NOTICE, "SecItemDelete");
4367	if (!query) {
4368		return errSecParam;
4369	}
4370	secitemshow(query, "SecItemDelete query:");
4371
4372	OSStatus status_osx = errSecItemNotFound, status_ios = errSecItemNotFound;
4373	Boolean sync_enabled = SecItemSyncEnabled();
4374	Boolean search_ios = SecItemSynchronizable(query);
4375	Boolean merge_search = SecItemSynchronizableAny(query);
4376	Boolean persistref_ios = SecItemHasSynchronizablePersistentReference(query);
4377
4378	if (sync_enabled && (merge_search || persistref_ios || search_ios)) {
4379		CFDictionaryRef attrs_ios = SecItemCopyTranslatedAttributes(query,
4380			NULL, true, true, false, true, true, true);
4381		if (!attrs_ios) {
4382			status_ios = errSecParam;
4383		}
4384		else {
4385            SecItemUnlockSynchronizableKeychain();
4386            status_ios = SecItemDelete_ios(attrs_ios);
4387			CFRelease(attrs_ios);
4388		}
4389		secitemlog(LOG_NOTICE, "SecItemDelete_ios result: %d", status_ios);
4390		if (!merge_search || persistref_ios)
4391			return status_ios;
4392	}
4393
4394	CFDictionaryRef attrs_osx = SecItemCopyTranslatedAttributes(query,
4395		NULL, false, false, true, true, true, true);
4396	if (!attrs_osx) {
4397		status_osx = errSecParam;
4398	}
4399	else {
4400		status_osx = SecItemDelete_osx(attrs_osx);
4401		CFRelease(attrs_osx);
4402	}
4403	secitemlog(LOG_NOTICE, "SecItemDelete_osx result: %d", status_osx);
4404
4405	if (merge_search) {
4406		// Harmonize the result of the delete attempts.
4407		if (status_osx == status_ios) {
4408			// both deletes produced the same result
4409			return status_ios;
4410		}
4411		else if (!status_osx || !status_ios) {
4412			// one of the deletes succeeded, but the other failed
4413			if (status_osx == errSecItemNotFound || status_ios == errSecItemNotFound)
4414				return errSecSuccess; // item only found in one keychain
4415			else
4416				return (status_osx) ? status_osx : status_ios; // return the error
4417		}
4418		else if (status_osx == errSecItemNotFound) {
4419			// both deletes failed, status_ios failure is more interesting
4420			// since the item was actually found
4421			return status_ios;
4422		}
4423	}
4424	return status_osx;
4425}
4426
4427OSStatus
4428SecItemCopyMatching_osx(
4429	CFDictionaryRef query,
4430	CFTypeRef *result)
4431{
4432	if (!query || !result)
4433		return errSecParam;
4434	else
4435		*result = NULL;
4436
4437	CFAllocatorRef allocator = CFGetAllocator(query);
4438	CFIndex matchCount = 0;
4439	CFMutableArrayRef itemArray = NULL;
4440	SecKeychainItemRef item = NULL;
4441	SecIdentityRef identity = NULL;
4442	OSStatus tmpStatus, status = errSecSuccess;
4443
4444	// validate input query parameters and create the search reference
4445	SecItemParams *itemParams = _CreateSecItemParamsFromDictionary(query, &status);
4446	require_action(itemParams != NULL, error_exit, itemParams = NULL);
4447
4448	// find the next match until we hit maxMatches, or no more matches found
4449	while ( !(!itemParams->returnAllMatches && matchCount >= itemParams->maxMatches) &&
4450			SecItemSearchCopyNext(itemParams, (CFTypeRef*)&item) == errSecSuccess) {
4451
4452		if (FilterCandidateItem((CFTypeRef*)&item, itemParams, &identity))
4453			continue; // move on to next item
4454
4455		++matchCount; // we have a match
4456
4457		tmpStatus = AddItemResults(item, identity, itemParams, allocator, &itemArray, result);
4458		if (tmpStatus && (status == errSecSuccess))
4459			status = tmpStatus;
4460
4461		if (item) {
4462			CFRelease(item);
4463			item = NULL;
4464		}
4465		if (identity) {
4466			CFRelease(identity);
4467			identity = NULL;
4468		}
4469	}
4470
4471	if (status == errSecSuccess)
4472		status = (matchCount > 0) ? errSecSuccess : errSecItemNotFound;
4473
4474error_exit:
4475	if (status != errSecSuccess && result != NULL && *result != NULL) {
4476		CFRelease(*result);
4477		*result = NULL;
4478	}
4479	_FreeSecItemParams(itemParams);
4480
4481	return status;
4482}
4483
4484OSStatus
4485SecItemCopyDisplayNames(
4486	CFArrayRef items,
4487	CFArrayRef *displayNames)
4488{
4489    BEGIN_SECAPI
4490	Required(items);
4491	Required(displayNames);
4492    //%%%TBI
4493    return errSecUnimplemented;
4494    END_SECAPI
4495}
4496
4497OSStatus
4498SecItemAdd_osx(
4499	CFDictionaryRef attributes,
4500	CFTypeRef *result)
4501{
4502	if (!attributes)
4503		return errSecParam;
4504	else if (result)
4505		*result = NULL;
4506
4507	CFAllocatorRef allocator = CFGetAllocator(attributes);
4508	CFMutableArrayRef itemArray = NULL;
4509	SecKeychainItemRef item = NULL;
4510	OSStatus tmpStatus, status = errSecSuccess;
4511
4512	// validate input attribute parameters
4513	SecItemParams *itemParams = _CreateSecItemParamsFromDictionary(attributes, &status);
4514	require_action(itemParams != NULL, error_exit, itemParams = NULL);
4515
4516	// currently, we don't support adding SecIdentityRef items (an aggregate item class),
4517	// since the private key should already be in a keychain by definition. We could support
4518	// this as a copy operation for the private key if a different keychain is specified,
4519	// but in any case it should try to add the certificate. See <rdar://8317887>.
4520	require_action(!itemParams->returnIdentity, error_exit, status = errSecItemInvalidValue);
4521
4522	if (!itemParams->useItems) {
4523		// create a single keychain item specified by the input attributes
4524		status = SecKeychainItemCreateFromContent(itemParams->itemClass,
4525			itemParams->attrList,
4526			(itemParams->itemData) ? (UInt32)CFDataGetLength(itemParams->itemData) : 0,
4527			(itemParams->itemData) ? CFDataGetBytePtrVoid(itemParams->itemData) : NULL,
4528			itemParams->keychain,
4529			itemParams->access,
4530			&item);
4531		require_noerr(status, error_exit);
4532
4533		// return results (if requested)
4534		if (result) {
4535			itemParams->maxMatches = 1; // in case kSecMatchLimit was set to > 1
4536			tmpStatus = AddItemResults(item, NULL, itemParams, allocator, &itemArray, result);
4537			if (tmpStatus && (status == errSecSuccess))
4538				status = tmpStatus;
4539		}
4540		CFRelease(item);
4541	}
4542	else {
4543		// add multiple items which are specified in the itemParams->useItems array.
4544		// -- SecCertificateRef or SecKeyRef items may or may not be in a keychain.
4545		// -- SecKeychainItemRef items are in a keychain (by definition), but may be copied to another keychain.
4546		// -- CFDataRef items are a persistent reference; the represented item may be copied to another keychain.
4547		//
4548		OSStatus aggregateStatus = errSecSuccess;
4549		CFIndex ix, count = CFArrayGetCount(itemParams->useItems);
4550		itemParams->maxMatches = (count > 1) ? (int)count : 2; // force results to always be returned as an array
4551		for (ix=0; ix < count; ix++) {
4552			CFTypeRef anItem = (CFTypeRef) CFArrayGetValueAtIndex(itemParams->useItems, ix);
4553			if (anItem) {
4554				if (SecCertificateGetTypeID() == CFGetTypeID(anItem)) {
4555					// SecCertificateRef item
4556					tmpStatus = SecCertificateAddToKeychain((SecCertificateRef)anItem, itemParams->keychain);
4557					if (!tmpStatus && result) {
4558						tmpStatus = AddItemResults((SecKeychainItemRef)anItem, NULL, itemParams, allocator, &itemArray, result);
4559					}
4560					aggregateStatus = _UpdateAggregateStatus(tmpStatus, aggregateStatus, errSecDuplicateItem);
4561				}
4562				else if (SecKeyGetTypeID() == CFGetTypeID(anItem)) {
4563					// SecKeyRef item
4564					SecKeychainRef itemKeychain = NULL;
4565					tmpStatus = SecKeychainItemCopyKeychain((SecKeychainItemRef)anItem, &itemKeychain);
4566					if (tmpStatus == errSecSuccess) {
4567						// key was in a keychain, so we can attempt to copy it
4568						SecKeychainItemRef itemCopy = NULL;
4569						tmpStatus = SecKeychainItemCreateCopy((SecKeychainItemRef)anItem, itemParams->keychain, itemParams->access, &itemCopy);
4570						if (!tmpStatus && result) {
4571							tmpStatus = AddItemResults(itemCopy, NULL, itemParams, allocator, &itemArray, result);
4572						}
4573						if (itemCopy) {
4574							CFRelease(itemCopy);
4575						}
4576					}
4577					else {
4578						// key was not in any keychain, so must be imported
4579						SecKeychainItemRef keyItem = NULL;
4580						tmpStatus = _ImportKey((SecKeyRef)anItem, itemParams->keychain, itemParams->access, itemParams->attrList, &keyItem);
4581						if (!tmpStatus && result) {
4582							tmpStatus = AddItemResults(keyItem, NULL, itemParams, allocator, &itemArray, result);
4583						}
4584						if (keyItem) {
4585							CFRelease(keyItem);
4586						}
4587					}
4588					if (itemKeychain) {
4589						CFRelease(itemKeychain);
4590					}
4591					aggregateStatus = _UpdateAggregateStatus(tmpStatus, aggregateStatus, errSecDuplicateItem);
4592				}
4593				else if (SecKeychainItemGetTypeID() == CFGetTypeID(anItem)) {
4594					// SecKeychainItemRef item
4595					SecKeychainItemRef itemCopy = NULL;
4596					tmpStatus = SecKeychainItemCreateCopy((SecKeychainItemRef)anItem, itemParams->keychain, itemParams->access, &itemCopy);
4597					if (!tmpStatus && result) {
4598						tmpStatus = AddItemResults(itemCopy, NULL, itemParams, allocator, &itemArray, result);
4599					}
4600					if (itemCopy) {
4601						CFRelease(itemCopy);
4602					}
4603					aggregateStatus = _UpdateAggregateStatus(tmpStatus, aggregateStatus, errSecDuplicateItem);
4604				}
4605				else if (CFDataGetTypeID() == CFGetTypeID(anItem)) {
4606					// CFDataRef item (persistent reference)
4607					SecKeychainItemRef realItem = NULL;
4608					tmpStatus = SecKeychainItemCopyFromPersistentReference((CFDataRef)anItem, &realItem);
4609					if (tmpStatus == errSecSuccess) {
4610						// persistent reference resolved to a keychain item, so we can attempt to copy it
4611						SecKeychainItemRef itemCopy = NULL;
4612						tmpStatus = SecKeychainItemCreateCopy(realItem, itemParams->keychain, itemParams->access, &itemCopy);
4613						if (!tmpStatus && result) {
4614							tmpStatus = AddItemResults(itemCopy, NULL, itemParams, allocator, &itemArray, result);
4615						}
4616						if (itemCopy) {
4617							CFRelease(itemCopy);
4618						}
4619					}
4620					if (realItem) {
4621						CFRelease(realItem);
4622					}
4623					aggregateStatus = _UpdateAggregateStatus(tmpStatus, aggregateStatus, errSecDuplicateItem);
4624				}
4625			}
4626		} // end of itemList array loop
4627		status = aggregateStatus;
4628	} // end processing multiple items
4629
4630error_exit:
4631	if (status != errSecSuccess && result != NULL && *result != NULL) {
4632		CFRelease(*result);
4633		*result = NULL;
4634	}
4635	_FreeSecItemParams(itemParams);
4636
4637	return status;
4638}
4639
4640OSStatus
4641SecItemUpdate_osx(
4642	CFDictionaryRef query,
4643	CFDictionaryRef attributesToUpdate)
4644{
4645	if (!query || !attributesToUpdate)
4646		return errSecParam;
4647
4648	// run the provided query to get a list of items to update
4649	CFTypeRef results = NULL;
4650	OSStatus status = SecItemCopyMatching(query, &results);
4651	if (status != errSecSuccess)
4652		return status; // nothing was matched, or the query was bad
4653
4654	CFArrayRef items = NULL;
4655	if (CFArrayGetTypeID() == CFGetTypeID(results)) {
4656		items = (CFArrayRef) results;
4657	}
4658	else {
4659		items = CFArrayCreate(NULL, &results, 1, &kCFTypeArrayCallBacks);
4660		CFRelease(results);
4661	}
4662
4663	OSStatus result = errSecSuccess;
4664	CFIndex ix, count = CFArrayGetCount(items);
4665	for (ix=0; ix < count; ix++) {
4666		CFTypeRef anItem = (CFTypeRef) CFArrayGetValueAtIndex(items, ix);
4667		if (anItem) {
4668			status = _UpdateKeychainItem(anItem, attributesToUpdate);
4669			result = _UpdateAggregateStatus(status, result, errSecSuccess);
4670		}
4671	}
4672
4673	if (items) {
4674		CFRelease(items);
4675	}
4676	return result;
4677}
4678
4679OSStatus
4680SecItemDelete_osx(
4681	CFDictionaryRef query)
4682{
4683	if (!query)
4684		return errSecParam;
4685
4686	// run the provided query to get a list of items to delete
4687	CFTypeRef results = NULL;
4688	OSStatus status = SecItemCopyMatching_osx(query, &results);
4689	if (status != errSecSuccess)
4690		return status; // nothing was matched, or the query was bad
4691
4692	CFArrayRef items = NULL;
4693	if (CFArrayGetTypeID() == CFGetTypeID(results)) {
4694		items = (CFArrayRef) results;
4695	}
4696	else {
4697		items = CFArrayCreate(NULL, &results, 1, &kCFTypeArrayCallBacks);
4698		CFRelease(results);
4699	}
4700
4701	OSStatus result = errSecSuccess;
4702	CFIndex ix, count = CFArrayGetCount(items);
4703	for (ix=0; ix < count; ix++) {
4704		CFTypeRef anItem = (CFTypeRef) CFArrayGetValueAtIndex(items, ix);
4705		if (anItem) {
4706			if (SecIdentityGetTypeID() == CFGetTypeID(anItem)) {
4707				status = _DeleteIdentity((SecIdentityRef)anItem);
4708			}
4709			else {
4710				status = _DeleteKeychainItem(anItem);
4711			}
4712			result = _UpdateAggregateStatus(status, result, errSecSuccess);
4713		}
4714	}
4715
4716	if (items)
4717		CFRelease(items);
4718
4719	return result;
4720}
4721