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