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