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