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