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