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