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