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