]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_keychain/lib/SecItem.cpp
Security-57740.31.2.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 = 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(*(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
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;
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 }
2496
2497 // get the password
2498 // (if the caller is not updating the password, this value will be NULL)
2499 theData = (CFDataRef)CFDictionaryGetValue(changedAttributes, kSecValueData);
2500 if (theData != NULL) {
2501 require_action(CFDataGetTypeID() == CFGetTypeID(theData), update_failed, status = errSecParam);
2502 }
2503 // update item
2504 status = SecKeychainItemModifyContent(itemToUpdate,
2505 (changeAttrList->count == 0) ? NULL : changeAttrList,
2506 (theData != NULL) ? (UInt32)CFDataGetLength(theData) : 0,
2507 (theData != NULL) ? CFDataGetBytePtrVoid(theData) : NULL);
2508 require_noerr(status, update_failed);
2509
2510 // one more thing... update access?
2511 if (CFDictionaryGetValueIfPresent(changedAttributes, kSecAttrAccess, (const void **)&access)) {
2512 status = SecKeychainItemSetAccess(itemToUpdate, access);
2513 }
2514
2515 update_failed:
2516 if (status == errSecVerifyFailed &&
2517 (itemClass == kSecInternetPasswordItemClass || itemClass == kSecGenericPasswordItemClass)) {
2518 // if we got a cryptographic failure updating a password item, it needs to be replaced
2519 status = _ReplaceKeychainItem(itemToUpdate,
2520 (changeAttrList->count == 0) ? NULL : changeAttrList,
2521 theData);
2522 }
2523 if (itemToUpdate)
2524 CFRelease(itemToUpdate);
2525 _FreeAttrList(changeAttrList);
2526 return status;
2527 }
2528
2529 static OSStatus
2530 _DeleteKeychainItem(CFTypeRef item)
2531 {
2532 // This function deletes a single keychain item, which may be specified as
2533 // a reference, persistent reference or attribute dictionary. It will not
2534 // delete non-keychain items or aggregate items (such as a SecIdentityRef);
2535 // it is assumed that the caller will pass identity components separately.
2536
2537 OSStatus status = errSecSuccess;
2538 if (!item) {
2539 return errSecParam;
2540 }
2541
2542 SecKeychainItemRef itemToDelete = NULL;
2543 CFTypeID itemType = CFGetTypeID(item);
2544 if (SecKeychainItemGetTypeID() == itemType ||
2545 SecCertificateGetTypeID() == itemType ||
2546 SecKeyGetTypeID() == itemType) {
2547 // item is already a reference, retain it
2548 itemToDelete = (SecKeychainItemRef) CFRetain(item);
2549 }
2550 else if (CFDataGetTypeID() == itemType) {
2551 // item is a persistent reference, must convert it
2552 status = SecKeychainItemCopyFromPersistentReference((CFDataRef)item, &itemToDelete);
2553 }
2554 else if (CFDictionaryGetTypeID() == itemType) {
2555 // item is a dictionary
2556 CFTypeRef value = NULL;
2557 if (CFDictionaryGetValueIfPresent((CFDictionaryRef)item, kSecValueRef, &value)) {
2558 // kSecValueRef value is a SecKeychainItemRef, retain it
2559 itemToDelete = (SecKeychainItemRef) CFRetain(value);
2560 }
2561 else if (CFDictionaryGetValueIfPresent((CFDictionaryRef)item, kSecValuePersistentRef, &value)) {
2562 // kSecValuePersistentRef value is a persistent reference, must convert it
2563 status = SecKeychainItemCopyFromPersistentReference((CFDataRef)value, &itemToDelete);
2564 }
2565 }
2566
2567 if (itemToDelete) {
2568 if (!status) {
2569 status = _SafeSecKeychainItemDelete(itemToDelete);
2570 }
2571 CFRelease(itemToDelete);
2572 }
2573
2574 return status;
2575 }
2576
2577 static OSStatus
2578 _DeleteIdentity(SecIdentityRef identity)
2579 {
2580 OSStatus status, result = errSecSuccess;
2581 SecKeyRef privateKey = NULL;
2582 SecCertificateRef certificate = NULL;
2583
2584 status = SecIdentityCopyPrivateKey(identity, &privateKey);
2585 if (!status) {
2586 SecKeyRef publicKey = NULL;
2587 status = _SecIdentityCopyPublicKey(identity, &publicKey);
2588 if (!status) {
2589 status = _DeleteKeychainItem(publicKey);
2590 CFRelease(publicKey);
2591 }
2592 status = _DeleteKeychainItem(privateKey);
2593 }
2594
2595 if (privateKey) CFRelease(privateKey);
2596 if (status) result = status;
2597
2598 status = SecIdentityCopyCertificate(identity, &certificate);
2599 if (!status) {
2600 status = _DeleteKeychainItem(certificate);
2601 }
2602
2603 if (certificate) CFRelease(certificate);
2604 if (status) result = status;
2605
2606 return result;
2607 }
2608
2609 static OSStatus
2610 _UpdateAggregateStatus(OSStatus newStatus, OSStatus curStatus, OSStatus baseStatus)
2611 {
2612 // This function is used when atomically processing multiple items,
2613 // where an overall error result must be returned for the entire operation.
2614 // When newStatus is something other than errSecSuccess, we want to keep the "most
2615 // interesting" status (which usually will be newStatus, unless curStatus is
2616 // already set; in that case, newStatus can trump curStatus only by being
2617 // something different than baseStatus.)
2618
2619 OSStatus result = curStatus;
2620
2621 if (newStatus != errSecSuccess) {
2622 result = newStatus;
2623 if (curStatus != errSecSuccess) {
2624 result = (newStatus != baseStatus) ? newStatus : curStatus;
2625 }
2626 }
2627 return result;
2628 }
2629
2630 static void
2631 _AddDictValueToOtherDict(const void *key, const void *value, void *context)
2632 {
2633 // CFDictionaryApplierFunction
2634 // This function just takes the given key/value pair,
2635 // and adds it to another dictionary supplied in the context argument.
2636
2637 CFMutableDictionaryRef dict = *((CFMutableDictionaryRef*) context);
2638 if (key && value) {
2639 CFDictionaryAddValue(dict, key, value);
2640 }
2641 }
2642
2643 static CFStringCompareFlags
2644 _StringCompareFlagsFromQuery(CFDictionaryRef query)
2645 {
2646 CFTypeRef value;
2647 CFStringCompareFlags flags = 0;
2648 if (!query) return flags;
2649
2650 if (CFDictionaryGetValueIfPresent(query, kSecMatchSubjectStartsWith, (const void **)&value) ||
2651 CFDictionaryGetValueIfPresent(query, kSecMatchSubjectEndsWith, (const void **)&value))
2652 flags |= kCFCompareAnchored;
2653
2654 if (CFDictionaryGetValueIfPresent(query, kSecMatchSubjectEndsWith, (const void **)&value))
2655 flags |= kCFCompareBackwards;
2656
2657 if (CFDictionaryGetValueIfPresent(query, kSecMatchCaseInsensitive, (const void **)&value) && CFEqual(kCFBooleanTrue, value))
2658 flags |= kCFCompareCaseInsensitive;
2659
2660 if (CFDictionaryGetValueIfPresent(query, kSecMatchDiacriticInsensitive, (const void **)&value) && CFEqual(kCFBooleanTrue, value))
2661 flags |= kCFCompareDiacriticInsensitive;
2662
2663 if (CFDictionaryGetValueIfPresent(query, kSecMatchWidthInsensitive, (const void **)&value) && CFEqual(kCFBooleanTrue, value))
2664 flags |= kCFCompareWidthInsensitive;
2665
2666 return flags;
2667 }
2668
2669 static uint32
2670 _CssmKeyUsageFromQuery(CFDictionaryRef query)
2671 {
2672 CFTypeRef value;
2673 uint32 keyUsage = 0;
2674 if (!query) return keyUsage;
2675
2676 if (CFDictionaryGetValueIfPresent(query, kSecAttrCanEncrypt, (const void **)&value) && CFEqual(kCFBooleanTrue, value))
2677 keyUsage |= CSSM_KEYUSE_ENCRYPT;
2678
2679 if (CFDictionaryGetValueIfPresent(query, kSecAttrCanDecrypt, (const void **)&value) && CFEqual(kCFBooleanTrue, value))
2680 keyUsage |= CSSM_KEYUSE_DECRYPT;
2681
2682 if (CFDictionaryGetValueIfPresent(query, kSecAttrCanSign, (const void **)&value) && CFEqual(kCFBooleanTrue, value))
2683 keyUsage |= CSSM_KEYUSE_SIGN;
2684
2685 if (CFDictionaryGetValueIfPresent(query, kSecAttrCanVerify, (const void **)&value) && CFEqual(kCFBooleanTrue, value))
2686 keyUsage |= CSSM_KEYUSE_VERIFY;
2687
2688 if (CFDictionaryGetValueIfPresent(query, kSecAttrCanWrap, (const void **)&value) && CFEqual(kCFBooleanTrue, value))
2689 keyUsage |= CSSM_KEYUSE_WRAP;
2690
2691 if (CFDictionaryGetValueIfPresent(query, kSecAttrCanUnwrap, (const void **)&value) && CFEqual(kCFBooleanTrue, value))
2692 keyUsage |= CSSM_KEYUSE_UNWRAP;
2693
2694 if (CFDictionaryGetValueIfPresent(query, kSecAttrCanDerive, (const void **)&value) && CFEqual(kCFBooleanTrue, value))
2695 keyUsage |= CSSM_KEYUSE_DERIVE;
2696
2697 return keyUsage;
2698 }
2699
2700 static SecItemClass
2701 _ConvertItemClass(const void* item, const void* keyClass, Boolean *isIdentity)
2702 {
2703 SecItemClass itemClass = (SecItemClass) 0;
2704 if (isIdentity) *isIdentity = false;
2705
2706 if (CFEqual(item, kSecClassGenericPassword)) {
2707 itemClass = kSecGenericPasswordItemClass;
2708 }
2709 else if (CFEqual(item, kSecClassInternetPassword)) {
2710 itemClass = kSecInternetPasswordItemClass;
2711 }
2712 else if (CFEqual(item, kSecClassCertificate)) {
2713 itemClass = kSecCertificateItemClass;
2714 }
2715 else if (CFEqual(item, kSecClassIdentity)) {
2716 // will perform a certificate lookup
2717 itemClass = kSecCertificateItemClass;
2718 if (isIdentity) *isIdentity = true;
2719 }
2720 else if (CFEqual(item, kSecClassKey)) {
2721 // examine second parameter to determine type of key
2722 if (!keyClass || CFEqual(keyClass, kSecAttrKeyClassSymmetric)) {
2723 itemClass = kSecSymmetricKeyItemClass;
2724 }
2725 else if (keyClass && CFEqual(keyClass, kSecAttrKeyClassPublic)) {
2726 itemClass = kSecPublicKeyItemClass;
2727 }
2728 else if (keyClass && CFEqual(keyClass, kSecAttrKeyClassPrivate)) {
2729 itemClass = kSecPrivateKeyItemClass;
2730 }
2731 }
2732
2733 return itemClass;
2734 }
2735
2736 static SecItemClass
2737 _ItemClassFromItemList(CFArrayRef itemList)
2738 {
2739 // Given a list of items (standard or persistent references),
2740 // determine whether they all have the same item class. Returns
2741 // the item class, or 0 if multiple classes in list.
2742 SecItemClass result = 0;
2743 CFIndex index, count = (itemList) ? CFArrayGetCount(itemList) : 0;
2744 for (index=0; index < count; index++) {
2745 CFTypeRef item = (CFTypeRef) CFArrayGetValueAtIndex(itemList, index);
2746 if (item) {
2747 SecKeychainItemRef itemRef = NULL;
2748 OSStatus status;
2749 if (CFGetTypeID(item) == CFDataGetTypeID()) {
2750 // persistent reference, resolve first
2751 status = SecKeychainItemCopyFromPersistentReference((CFDataRef)item, &itemRef);
2752 }
2753 else {
2754 itemRef = (SecKeychainItemRef) CFRetain(item);
2755 }
2756 if (itemRef) {
2757 SecItemClass itemClass = 0;
2758 CFTypeID itemTypeID = CFGetTypeID(itemRef);
2759 if (itemTypeID == SecIdentityGetTypeID() || itemTypeID == SecCertificateGetTypeID()) {
2760 // Identities and certificates have the same underlying item class
2761 itemClass = kSecCertificateItemClass;
2762 }
2763 else if (itemTypeID == SecKeychainItemGetTypeID()) {
2764 // Reference to item in a keychain
2765 status = SecKeychainItemCopyAttributesAndData(itemRef, NULL, &itemClass, NULL, NULL, NULL);
2766 }
2767 else if (itemTypeID == SecKeyGetTypeID()) {
2768 // SecKey that isn't stored in a keychain
2769 // %%% will need to change this code when SecKey is no longer CSSM-based %%%
2770 const CSSM_KEY *cssmKey;
2771 status = SecKeyGetCSSMKey((SecKeyRef)itemRef, &cssmKey);
2772 if (status == errSecSuccess) {
2773 if (cssmKey->KeyHeader.KeyClass == CSSM_KEYCLASS_PUBLIC_KEY)
2774 itemClass = kSecPublicKeyItemClass;
2775 else if (cssmKey->KeyHeader.KeyClass == CSSM_KEYCLASS_PRIVATE_KEY)
2776 itemClass = kSecPrivateKeyItemClass;
2777 else
2778 itemClass = kSecSymmetricKeyItemClass;
2779 }
2780 }
2781 CFRelease(itemRef);
2782 if (itemClass != 0) {
2783 if (result != 0 && result != itemClass) {
2784 return 0; // different item classes in list; bail out
2785 }
2786 result = itemClass;
2787 }
2788 }
2789 }
2790 }
2791 return result;
2792 }
2793
2794 // SecItemParams contains a validated set of input parameters, as well as a
2795 // search reference and attribute list built from those parameters. It is
2796 // designed to be allocated with _CreateSecItemParamsFromDictionary, and
2797 // freed with _FreeSecItemParams.
2798
2799 struct SecItemParams {
2800 CFDictionaryRef query; // caller-supplied query
2801 int numResultTypes; // number of result types requested
2802 int maxMatches; // max number of matches to return
2803 uint32 keyUsage; // key usage(s) requested
2804 Boolean returningAttributes; // true if returning attributes dictionary
2805 Boolean returningData; // true if returning item's data
2806 Boolean returningRef; // true if returning item reference
2807 Boolean returningPersistentRef; // true if returing a persistent reference
2808 Boolean returnAllMatches; // true if we should return all matches
2809 Boolean returnIdentity; // true if we are returning a SecIdentityRef
2810 Boolean trustedOnly; // true if we only return trusted certs
2811 Boolean issuerAndSNToMatch; // true if both issuer and SN were provided
2812 SecItemClass itemClass; // item class for this query
2813 SecPolicyRef policy; // value for kSecMatchPolicy (may be NULL)
2814 SecKeychainRef keychain; // value for kSecUseKeychain (may be NULL)
2815 CFArrayRef useItems; // value for kSecUseItemList (may be NULL)
2816 CFArrayRef itemList; // value for kSecMatchItemList (may be NULL)
2817 CFTypeRef searchList; // value for kSecMatchSearchList (may be NULL)
2818 CFTypeRef matchLimit; // value for kSecMatchLimit (may be NULL)
2819 CFTypeRef emailAddrToMatch; // value for kSecMatchEmailAddressIfPresent (may be NULL)
2820 CFTypeRef validOnDate; // value for kSecMatchValidOnDate (may be NULL)
2821 CFTypeRef keyClass; // value for kSecAttrKeyClass (may be NULL)
2822 CFTypeRef service; // value for kSecAttrService (may be NULL)
2823 CFTypeRef issuer; // value for kSecAttrIssuer (may be NULL)
2824 CFTypeRef serialNumber; // value for kSecAttrSerialNumber (may be NULL)
2825 CFTypeRef search; // search reference for this query (SecKeychainSearchRef or SecIdentitySearchRef)
2826 CFTypeRef assumedKeyClass; // if no kSecAttrKeyClass provided, holds the current class we're searching for
2827 CFIndex itemListIndex; // if no search reference but we have itemList, holds index of next item to return
2828 SecKeychainAttributeList *attrList; // attribute list for this query
2829 SecAccessRef access; // access reference (for SecItemAdd only, not used to find items)
2830 CFDataRef itemData; // item data (for SecItemAdd only, not used to find items)
2831 CFTypeRef itemRef; // item reference (to find, add, update or delete, depending on context)
2832 SecIdentityRef identityRef; // identity reference (input as kSecValueRef)
2833 CFDataRef itemPersistentRef; // item persistent reference (to find, add, update or delete, depending on context)
2834 Boolean isPCSItem; // true if this query is for a Protected Cloud Storage item
2835 };
2836
2837 static OSStatus
2838 _ValidateDictionaryEntry(CFDictionaryRef dict, CFTypeRef key, const void **value, CFTypeID expectedTypeID, CFTypeID altTypeID)
2839 {
2840 if (!dict || !key || !value || !expectedTypeID)
2841 return errSecParam;
2842
2843 if (!CFDictionaryGetValueIfPresent(dict, key, value)) {
2844 // value was not provided for this key (not an error!)
2845 *value = NULL;
2846 }
2847 else if (!(*value)) {
2848 // provided value is NULL (also not an error!)
2849 return errSecSuccess;
2850 }
2851 else {
2852 CFTypeID actualTypeID = CFGetTypeID(*value);
2853 if (!((expectedTypeID == actualTypeID) || (altTypeID && altTypeID == actualTypeID))) {
2854 // provided value does not have the expected (or alternate) CF type ID
2855 if ((expectedTypeID == SecKeychainItemGetTypeID()) &&
2856 (actualTypeID == SecKeyGetTypeID() || actualTypeID == SecCertificateGetTypeID())) {
2857 // provided value is a "floating" reference which is not yet in a keychain
2858 CFRetain(*value);
2859 return errSecSuccess;
2860 }
2861 return errSecItemInvalidValue;
2862 }
2863 else {
2864 // provided value is OK; retain it
2865 CFRetain(*value);
2866 }
2867 }
2868 return errSecSuccess;
2869 }
2870
2871 static void
2872 _EnsureUserDefaultKeychainIsSearched(SecItemParams *itemParams)
2873 {
2874 OSStatus status;
2875 CFArrayRef tmpList = (CFArrayRef) itemParams->searchList;
2876 if (tmpList) {
2877 // search list exists; make it mutable
2878 itemParams->searchList = (CFArrayRef) CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, tmpList);
2879 CFRelease(tmpList);
2880 } else {
2881 // no search list; start with default list
2882 status = SecKeychainCopySearchList(&tmpList);
2883 if (!status && tmpList) {
2884 itemParams->searchList = (CFArrayRef) CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, tmpList);
2885 CFRelease(tmpList);
2886 }
2887 else {
2888 itemParams->searchList = (CFArrayRef) CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
2889 }
2890 }
2891
2892 SecKeychainRef userKeychain = NULL;
2893 status = SecKeychainCopyDomainDefault(kSecPreferencesDomainUser, &userKeychain);
2894 if (!status && userKeychain) {
2895 if (!CFArrayContainsValue((CFArrayRef)itemParams->searchList,
2896 CFRangeMake(0, CFArrayGetCount((CFArrayRef)itemParams->searchList)), userKeychain)) {
2897 // user's default keychain isn't currently in the search list, so append it
2898 CFArrayAppendValue((CFMutableArrayRef)itemParams->searchList, userKeychain);
2899 }
2900 CFRelease(userKeychain);
2901 }
2902 }
2903
2904 static void
2905 _EnsureUserDefaultKeychainIsTargeted(SecItemParams *itemParams)
2906 {
2907 if (itemParams->keychain) {
2908 return; // keychain is already explicitly specified, assume it's correct
2909 }
2910 SecKeychainRef userKeychain = NULL;
2911 OSStatus status = SecKeychainCopyDomainDefault(kSecPreferencesDomainUser, &userKeychain);
2912 if (!status && userKeychain) {
2913 itemParams->keychain = userKeychain;
2914 }
2915 }
2916
2917 static void
2918 _FreeSecItemParams(SecItemParams *itemParams)
2919 {
2920 if (!itemParams)
2921 return;
2922
2923 if (itemParams->query) CFRelease(itemParams->query);
2924 if (itemParams->policy) CFRelease(itemParams->policy);
2925 if (itemParams->keychain) CFRelease(itemParams->keychain);
2926 if (itemParams->useItems) CFRelease(itemParams->useItems);
2927 if (itemParams->itemList) CFRelease(itemParams->itemList);
2928 if (itemParams->searchList) CFRelease(itemParams->searchList);
2929 if (itemParams->matchLimit) CFRelease(itemParams->matchLimit);
2930 if (itemParams->emailAddrToMatch) CFRelease(itemParams->emailAddrToMatch);
2931 if (itemParams->validOnDate) CFRelease(itemParams->validOnDate);
2932 if (itemParams->keyClass) CFRelease(itemParams->keyClass);
2933 if (itemParams->service) CFRelease(itemParams->service);
2934 if (itemParams->issuer) CFRelease(itemParams->issuer);
2935 if (itemParams->serialNumber) CFRelease(itemParams->serialNumber);
2936 if (itemParams->search) CFRelease(itemParams->search);
2937 if (itemParams->access) CFRelease(itemParams->access);
2938 if (itemParams->itemData) CFRelease(itemParams->itemData);
2939 if (itemParams->itemRef) CFRelease(itemParams->itemRef);
2940 if (itemParams->identityRef) CFRelease(itemParams->identityRef);
2941 if (itemParams->itemPersistentRef) CFRelease(itemParams->itemPersistentRef);
2942
2943 _FreeAttrList(itemParams->attrList);
2944
2945 free(itemParams);
2946 }
2947
2948 static SecItemParams*
2949 _CreateSecItemParamsFromDictionary(CFDictionaryRef dict, OSStatus *error)
2950 {
2951 OSStatus status;
2952 CFTypeRef value = NULL;
2953 SecItemParams *itemParams = (SecItemParams *)calloc(1, sizeof(struct SecItemParams));
2954
2955 require_action(itemParams != NULL, error_exit, status = errSecAllocate);
2956 require_action(dict && (CFDictionaryGetTypeID() == CFGetTypeID(dict)), error_exit, status = errSecParam);
2957
2958 itemParams->query = (CFDictionaryRef) CFRetain(dict);
2959
2960 // validate input search parameters
2961 require_noerr(status = _ValidateDictionaryEntry(dict, kSecMatchPolicy, (const void **)&itemParams->policy, SecPolicyGetTypeID(), NULL), error_exit);
2962 require_noerr(status = _ValidateDictionaryEntry(dict, kSecMatchSearchList, (const void **)&itemParams->searchList, CFArrayGetTypeID(), NULL), error_exit);
2963 require_noerr(status = _ValidateDictionaryEntry(dict, kSecMatchItemList, (const void **)&itemParams->itemList, CFArrayGetTypeID(), NULL), error_exit);
2964 require_noerr(status = _ValidateDictionaryEntry(dict, kSecMatchEmailAddressIfPresent, (const void **)&itemParams->emailAddrToMatch, CFStringGetTypeID(), NULL), error_exit);
2965 require_noerr(status = _ValidateDictionaryEntry(dict, kSecMatchValidOnDate, (const void **)&itemParams->validOnDate, CFDateGetTypeID(), CFNullGetTypeID()), error_exit);
2966 require_noerr(status = _ValidateDictionaryEntry(dict, kSecMatchLimit, (const void **)&itemParams->matchLimit, CFStringGetTypeID(), CFNumberGetTypeID()), error_exit);
2967
2968 require_noerr(status = _ValidateDictionaryEntry(dict, kSecUseItemList, (const void **)&itemParams->useItems, CFArrayGetTypeID(), NULL), error_exit);
2969 require_noerr(status = _ValidateDictionaryEntry(dict, kSecUseKeychain, (const void **)&itemParams->keychain, SecKeychainGetTypeID(), NULL), error_exit);
2970
2971 // validate a subset of input attributes (used to create an appropriate search reference)
2972 require_noerr(status = _ValidateDictionaryEntry(dict, kSecAttrIssuer, (const void **)&itemParams->issuer, CFDataGetTypeID(), NULL), error_exit);
2973 require_noerr(status = _ValidateDictionaryEntry(dict, kSecAttrSerialNumber, (const void **)&itemParams->serialNumber, CFDataGetTypeID(), NULL), error_exit);
2974 require_noerr(status = _ValidateDictionaryEntry(dict, kSecAttrService, (const void **)&itemParams->service, CFStringGetTypeID(), NULL), error_exit);
2975 require_noerr(status = _ValidateDictionaryEntry(dict, kSecAttrKeyClass, (const void **)&itemParams->keyClass, CFStringGetTypeID(), NULL), error_exit);
2976
2977 if (itemParams->service && CFStringHasPrefix((CFStringRef)itemParams->service, CFSTR("ProtectedCloudStorage"))) {
2978 itemParams->isPCSItem = true;
2979 if (!SecItemSynchronizable(dict)) {
2980 _EnsureUserDefaultKeychainIsSearched(itemParams); // for SecItemCopyMatching, SecItemUpdate, SecItemDelete
2981 _EnsureUserDefaultKeychainIsTargeted(itemParams); // for SecItemAdd
2982 }
2983 }
2984
2985 // validate the payload (password, key or certificate data), used for SecItemAdd but not for finding items
2986 require_noerr(status = _ValidateDictionaryEntry(dict, kSecValueData, (const void **)&itemParams->itemData, CFDataGetTypeID(), CFStringGetTypeID()), error_exit);
2987 if (itemParams->itemData && CFGetTypeID(itemParams->itemData) == CFStringGetTypeID()) {
2988 /* If we got a string, convert it into a data object */
2989 CFStringRef string = (CFStringRef)itemParams->itemData;
2990 CFIndex maxLength = CFStringGetMaximumSizeForEncoding(CFStringGetLength(string), kCFStringEncodingUTF8) + 1;
2991 CFMutableDataRef data = CFDataCreateMutable(NULL, maxLength);
2992 require_action(data, error_exit, status = errSecAllocate);
2993
2994 CFDataSetLength(data, maxLength);
2995
2996 if (!CFStringGetCString(string, (char *)CFDataGetMutableBytePtr(data), maxLength, kCFStringEncodingUTF8)) {
2997 CFRelease(data);
2998 status = errSecAllocate;
2999 goto error_exit;
3000 }
3001
3002 CFDataSetLength(data, strlen((const char *)CFDataGetBytePtr(data))); /* dont include NUL in string */
3003 itemParams->itemData = data;
3004 CFRelease(string);
3005 }
3006
3007 // validate item references
3008 require_noerr(status = _ValidateDictionaryEntry(dict, kSecValueRef, (const void **)&itemParams->itemRef, SecKeychainItemGetTypeID(), SecIdentityGetTypeID()), error_exit);
3009 if (itemParams->itemRef && (CFGetTypeID(itemParams->itemRef) == SecIdentityGetTypeID())) {
3010 itemParams->identityRef = (SecIdentityRef)itemParams->itemRef;
3011 itemParams->itemRef = NULL;
3012 SecIdentityCopyCertificate(itemParams->identityRef, (SecCertificateRef *)&itemParams->itemRef);
3013 }
3014 require_noerr(status = _ValidateDictionaryEntry(dict, kSecValuePersistentRef, (const void **)&itemParams->itemPersistentRef, CFDataGetTypeID(), NULL), error_exit);
3015 if (itemParams->itemRef || itemParams->itemPersistentRef) {
3016 // Caller is trying to add or find an item by reference.
3017 // The supported method for doing that is to provide a kSecUseItemList array
3018 // for SecItemAdd, or a kSecMatchItemList array for SecItemCopyMatching et al,
3019 // so add the item reference to those arrays here.
3020 if (itemParams->useItems) {
3021 CFArrayRef tmpItems = itemParams->useItems;
3022 itemParams->useItems = (CFArrayRef) CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, tmpItems);
3023 CFRelease(tmpItems);
3024 } else {
3025 itemParams->useItems = (CFArrayRef) CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
3026 }
3027 if (itemParams->itemRef) CFArrayAppendValue((CFMutableArrayRef)itemParams->useItems, itemParams->itemRef);
3028 if (itemParams->itemPersistentRef) CFArrayAppendValue((CFMutableArrayRef)itemParams->useItems, itemParams->itemPersistentRef);
3029
3030 if (itemParams->itemList) {
3031 CFArrayRef tmpItems = itemParams->itemList;
3032 itemParams->itemList = (CFArrayRef) CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, tmpItems);
3033 CFRelease(tmpItems);
3034 } else {
3035 itemParams->itemList = (CFArrayRef) CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
3036 }
3037 if (itemParams->itemRef) CFArrayAppendValue((CFMutableArrayRef)itemParams->itemList, itemParams->itemRef);
3038 if (itemParams->itemPersistentRef) CFArrayAppendValue((CFMutableArrayRef)itemParams->itemList, itemParams->itemPersistentRef);
3039 }
3040
3041 // must have an explicit item class, unless one of the following is true:
3042 // - we have an item list to add or search (kSecUseItemList)
3043 // - we have an item reference or persistent reference for the thing we want to look up
3044 // Note that both of these cases will set itemParams->useItems.
3045 // If we have an item list to match (kSecMatchItemList), that still requires an item class,
3046 // so we can perform a search and see if the results match items in the list.
3047 //
3048 if (!CFDictionaryGetValueIfPresent(dict, kSecClass, (const void**) &value) && !itemParams->useItems) {
3049 require_action(false, error_exit, status = errSecItemClassMissing);
3050 }
3051 else if (value) {
3052 itemParams->itemClass = _ConvertItemClass(value, itemParams->keyClass, &itemParams->returnIdentity);
3053 if (itemParams->itemClass == kSecSymmetricKeyItemClass && !itemParams->keyClass) {
3054 // no key class specified, so start with symmetric key class; will search the others later in UpdateKeychainSearchAndCopyNext
3055 itemParams->itemClass = kSecSymmetricKeyItemClass;
3056 itemParams->assumedKeyClass = kSecAttrKeyClassPublic;
3057 }
3058 require_action(!(itemParams->itemClass == 0 && !itemParams->useItems), error_exit, status = errSecItemClassMissing);
3059 }
3060
3061 itemParams->keyUsage = _CssmKeyUsageFromQuery(dict);
3062 itemParams->trustedOnly = CFDictionaryGetValueIfPresent(dict, kSecMatchTrustedOnly, (const void **)&value) && value && CFEqual(kCFBooleanTrue, value);
3063 itemParams->issuerAndSNToMatch = (itemParams->issuer != NULL && itemParams->serialNumber != NULL);
3064
3065 // other input attributes, used for SecItemAdd but not for finding items
3066 require_noerr(status = _ValidateDictionaryEntry(dict, kSecAttrAccess, (const void **)&itemParams->access, SecAccessGetTypeID(), NULL), error_exit);
3067 if (itemParams->access == NULL) {
3068 // check for the old definition of kSecAttrAccess from SecItem-shim (see <rdar://7987447>)
3069 require_noerr(status = _ValidateDictionaryEntry(dict, CFSTR("kSecAttrAccess"), (const void **)&itemParams->access, SecAccessGetTypeID(), NULL), error_exit);
3070 }
3071
3072 // determine how to return the result
3073 itemParams->numResultTypes = 0;
3074 itemParams->returningRef = CFDictionaryGetValueIfPresent(dict, kSecReturnRef, (const void **)&value) && value && CFEqual(kCFBooleanTrue, value);
3075 if (itemParams->returningRef) ++itemParams->numResultTypes;
3076 itemParams->returningPersistentRef = CFDictionaryGetValueIfPresent(dict, kSecReturnPersistentRef, (const void **)&value) && value && CFEqual(kCFBooleanTrue, value);
3077 if (itemParams->returningPersistentRef) ++itemParams->numResultTypes;
3078 itemParams->returningAttributes = CFDictionaryGetValueIfPresent(dict, kSecReturnAttributes, (const void **)&value) && value && CFEqual(kCFBooleanTrue, value);
3079 if (itemParams->returningAttributes) ++itemParams->numResultTypes;
3080 itemParams->returningData = CFDictionaryGetValueIfPresent(dict, kSecReturnData, (const void **)&value) && value && CFEqual(kCFBooleanTrue, value);
3081 if (itemParams->returningData) ++itemParams->numResultTypes;
3082
3083 // default is kSecReturnRef if no result types were specified
3084 if (!itemParams->numResultTypes) {
3085 itemParams->returningRef = TRUE;
3086 itemParams->numResultTypes = 1;
3087 }
3088
3089 // determine if one, some or all matches should be returned (default is kSecMatchLimitOne)
3090 itemParams->maxMatches = 1;
3091 itemParams->returnAllMatches = FALSE;
3092 if (itemParams->matchLimit) {
3093 if (CFStringGetTypeID() == CFGetTypeID(itemParams->matchLimit)) {
3094 itemParams->returnAllMatches = CFEqual(kSecMatchLimitAll, itemParams->matchLimit);
3095 }
3096 else if (CFNumberGetTypeID() == CFGetTypeID(itemParams->matchLimit)) {
3097 CFNumberGetValue((CFNumberRef)itemParams->matchLimit, kCFNumberIntType, &itemParams->maxMatches);
3098 require_action(!(itemParams->maxMatches < 0), error_exit, status = errSecMatchLimitUnsupported);
3099 }
3100 }
3101 if (itemParams->returnAllMatches) {
3102 itemParams->maxMatches = INT32_MAX;
3103 // if we're returning all matches, then we don't support getting passwords as data (which could require authentication for each)
3104 if ((itemParams->itemClass==kSecInternetPasswordItemClass || itemParams->itemClass==kSecGenericPasswordItemClass) && itemParams->returningData)
3105 status = errSecReturnDataUnsupported;
3106 require_noerr(status, error_exit);
3107 }
3108
3109 // 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
3110 if (itemParams->useItems) {
3111 if (itemParams->itemClass == 0) {
3112 itemParams->itemClass = _ItemClassFromItemList(itemParams->useItems);
3113 }
3114 status = errSecSuccess;
3115 goto error_exit; // all done here
3116 }
3117
3118 // build a SecKeychainAttributeList from the query dictionary for the specified item class
3119 require_noerr(status = _CreateSecKeychainAttributeListFromDictionary(dict, itemParams->itemClass, &itemParams->attrList), error_exit);
3120
3121 // if policy is a SMIME policy, copy email address in policy into emailAddrToMatch parameter
3122 if(itemParams->policy) {
3123 CFDictionaryRef policyDict = SecPolicyCopyProperties(itemParams->policy);
3124 CFStringRef oidStr = (CFStringRef) CFDictionaryGetValue(policyDict, kSecPolicyOid);
3125 if(oidStr && CFStringCompare(kSecPolicyAppleSMIME,oidStr,0) == 0) {
3126 require_noerr(status = _ValidateDictionaryEntry(policyDict, kSecPolicyName, (const void **)&itemParams->emailAddrToMatch, CFStringGetTypeID(), NULL), error_exit);
3127 }
3128 CFRelease(policyDict);
3129 }
3130
3131 // create a search reference (either a SecKeychainSearchRef or a SecIdentitySearchRef)
3132 if ((itemParams->itemClass == kSecCertificateItemClass) && itemParams->emailAddrToMatch) {
3133 // searching for certificates by email address
3134 char *nameBuf = (char*)malloc(MAXPATHLEN);
3135 if (!nameBuf) {
3136 status = errSecAllocate;
3137 }
3138 else if (CFStringGetCString((CFStringRef)itemParams->emailAddrToMatch, nameBuf, (CFIndex)MAXPATHLEN-1, kCFStringEncodingUTF8)) {
3139 status = SecKeychainSearchCreateForCertificateByEmail(itemParams->searchList, (const char *)nameBuf, (SecKeychainSearchRef*)&itemParams->search);
3140 }
3141 else {
3142 status = errSecItemInvalidValue;
3143 }
3144 if (nameBuf) free(nameBuf);
3145 }
3146 else if ((itemParams->itemClass == kSecCertificateItemClass) && itemParams->issuerAndSNToMatch) {
3147 // searching for certificates by issuer and serial number
3148 status = SecKeychainSearchCreateForCertificateByIssuerAndSN_CF(itemParams->searchList,
3149 (CFDataRef)itemParams->issuer,
3150 (CFDataRef)itemParams->serialNumber,
3151 (SecKeychainSearchRef*)&itemParams->search);
3152 }
3153 else if (itemParams->returnIdentity && itemParams->policy) {
3154 // searching for identities by policy
3155 status = SecIdentitySearchCreateWithPolicy(itemParams->policy,
3156 (CFStringRef)itemParams->service,
3157 itemParams->keyUsage,
3158 itemParams->searchList,
3159 itemParams->trustedOnly,
3160 (SecIdentitySearchRef*)&itemParams->search);
3161 }
3162 else if (itemParams->returnIdentity) {
3163 // searching for identities
3164 status = SecIdentitySearchCreate(itemParams->searchList,
3165 itemParams->keyUsage,
3166 (SecIdentitySearchRef*)&itemParams->search);
3167 }
3168 else {
3169 // normal keychain item search
3170 status = SecKeychainSearchCreateFromAttributes(itemParams->searchList,
3171 itemParams->itemClass,
3172 (itemParams->attrList->count == 0) ? NULL : itemParams->attrList,
3173 (SecKeychainSearchRef*)&itemParams->search);
3174 }
3175
3176 error_exit:
3177 if (status) {
3178 _FreeSecItemParams(itemParams);
3179 itemParams = NULL;
3180 }
3181 if (error) {
3182 *error = status;
3183 }
3184 return itemParams;
3185 }
3186
3187
3188 static OSStatus
3189 _ImportKey(
3190 SecKeyRef keyRef,
3191 SecKeychainRef keychainRef,
3192 SecAccessRef accessRef,
3193 SecKeychainAttributeList *attrList,
3194 SecKeychainItemRef *outItemRef)
3195 {
3196 BEGIN_SECAPI
3197
3198 // We must specify the access, since a free-floating key won't have one yet by default
3199 SecPointer<Access> access;
3200 if (accessRef) {
3201 access = Access::required(accessRef);
3202 }
3203 else {
3204 CFStringRef descriptor = NULL;
3205 if (attrList) {
3206 for (UInt32 index=0; index < attrList->count; index++) {
3207 SecKeychainAttribute attr = attrList->attr[index];
3208 if (attr.tag == kSecKeyPrintName) {
3209 descriptor = CFStringCreateWithBytes(NULL, (const UInt8 *)attr.data, attr.length, kCFStringEncodingUTF8, FALSE);
3210 break;
3211 }
3212 }
3213 }
3214 if (descriptor == NULL) {
3215 descriptor = (CFStringRef) CFRetain(CFSTR("<unknown>"));
3216 }
3217 access = new Access(cfString(descriptor));
3218 CFRelease(descriptor);
3219 }
3220
3221 KeyItem *key = KeyItem::required(keyRef);
3222 Item item = key->importTo(Keychain::optional(keychainRef), access, attrList);
3223 if (outItemRef)
3224 *outItemRef = item->handle();
3225
3226 END_SECAPI
3227 }
3228
3229 #if !SECTRUST_OSX
3230 static Boolean
3231 _CanIgnoreLeafStatusCodes(CSSM_TP_APPLE_EVIDENCE_INFO *evidence)
3232 {
3233 /* Check for ignorable status codes in leaf certificate's evidence */
3234 Boolean result = true;
3235 unsigned int i;
3236 for (i=0; i < evidence->NumStatusCodes; i++) {
3237 CSSM_RETURN scode = evidence->StatusCodes[i];
3238 if (scode == CSSMERR_APPLETP_INVALID_CA) {
3239 // the TP has rejected this CA cert because it's in the leaf position
3240 result = true;
3241 }
3242 else if (ignorableRevocationStatusCode(scode)) {
3243 result = true;
3244 }
3245 else {
3246 result = false;
3247 break;
3248 }
3249 }
3250 return result;
3251 }
3252 #endif
3253
3254 static OSStatus
3255 _FilterWithPolicy(SecPolicyRef policy, CFDateRef date, SecCertificateRef cert)
3256 {
3257 CFDictionaryRef props = NULL;
3258 CFArrayRef keychains = NULL;
3259 CFArrayRef anchors = NULL;
3260 CFArrayRef certs = NULL;
3261 CFArrayRef chain = NULL;
3262 SecTrustRef trust = NULL;
3263
3264 SecTrustResultType trustResult;
3265 #if !SECTRUST_OSX
3266 CSSM_TP_APPLE_EVIDENCE_INFO *evidence = NULL;
3267 #endif
3268 Boolean needChain = false;
3269 OSStatus status;
3270 if (!policy || !cert) return errSecParam;
3271
3272 certs = CFArrayCreate(NULL, (const void **)&cert, (CFIndex)1, &kCFTypeArrayCallBacks);
3273 status = SecTrustCreateWithCertificates(certs, policy, &trust);
3274 if(status) goto cleanup;
3275
3276 /* Set evaluation date, if specified (otherwise current date is implied) */
3277 if (date && (CFGetTypeID(date) == CFDateGetTypeID())) {
3278 status = SecTrustSetVerifyDate(trust, date);
3279 if(status) goto cleanup;
3280 }
3281
3282 /* Check whether this is the X509 Basic policy, which means chain building */
3283 props = SecPolicyCopyProperties(policy);
3284 if (props) {
3285 CFTypeRef oid = (CFTypeRef) CFDictionaryGetValue(props, kSecPolicyOid);
3286 if (oid && (CFEqual(oid, kSecPolicyAppleX509Basic) ||
3287 CFEqual(oid, kSecPolicyAppleRevocation))) {
3288 needChain = true;
3289 }
3290 }
3291
3292 if (!needChain) {
3293 #if !SECTRUST_OSX
3294 /* To make the evaluation as lightweight as possible, specify an empty array
3295 * of keychains which will be searched for certificates.
3296 */
3297 keychains = CFArrayCreate(NULL, NULL, 0, &kCFTypeArrayCallBacks);
3298 status = SecTrustSetKeychains(trust, keychains);
3299 if(status) goto cleanup;
3300
3301 /* To make the evaluation as lightweight as possible, specify an empty array
3302 * of trusted anchors.
3303 */
3304 anchors = CFArrayCreate(NULL, NULL, 0, &kCFTypeArrayCallBacks);
3305 status = SecTrustSetAnchorCertificates(trust, anchors);
3306 if(status) goto cleanup;
3307 #else
3308 status = SecTrustEvaluateLeafOnly(trust, &trustResult);
3309 } else {
3310 status = SecTrustEvaluate(trust, &trustResult);
3311 #endif
3312 }
3313
3314 #if !SECTRUST_OSX
3315 /* All parameters are locked and loaded, ready to evaluate! */
3316 status = SecTrustEvaluate(trust, &trustResult);
3317 if(status) goto cleanup;
3318
3319 /* If we didn't provide trust anchors or a way to look for them,
3320 * the evaluation will fail with kSecTrustResultRecoverableTrustFailure.
3321 * However, we can tell whether the policy evaluation succeeded by
3322 * looking at the per-cert status codes in the returned evidence.
3323 */
3324 status = SecTrustGetResult(trust, &trustResult, &chain, &evidence);
3325 if(status) goto cleanup;
3326 #endif
3327
3328 if (!(trustResult == kSecTrustResultProceed ||
3329 trustResult == kSecTrustResultUnspecified ||
3330 trustResult == kSecTrustResultRecoverableTrustFailure)) {
3331 /* The evaluation failed in a non-recoverable way */
3332 status = errSecCertificateCannotOperate;
3333 goto cleanup;
3334 }
3335
3336 /* If there are no per-cert policy status codes,
3337 * and the cert has not expired, consider it valid for the policy.
3338 */
3339 #if SECTRUST_OSX
3340 if (true) {
3341 (void)SecTrustGetCssmResultCode(trust, &status);
3342 #else
3343 if((evidence != NULL) && _CanIgnoreLeafStatusCodes(evidence) &&
3344 ((evidence[0].StatusBits & CSSM_CERT_STATUS_EXPIRED) == 0) &&
3345 ((evidence[0].StatusBits & CSSM_CERT_STATUS_NOT_VALID_YET) == 0)) {
3346 status = errSecSuccess;
3347 #endif
3348 }
3349 else {
3350 status = errSecCertificateCannotOperate;
3351 }
3352
3353 cleanup:
3354 if(props) CFRelease(props);
3355 if(chain) CFRelease(chain);
3356 if(anchors) CFRelease(anchors);
3357 if(keychains) CFRelease(keychains);
3358 if(certs) CFRelease(certs);
3359 if(trust) CFRelease(trust);
3360
3361 return status;
3362 }
3363
3364 static OSStatus
3365 _FilterWithDate(CFTypeRef validOnDate, SecCertificateRef cert)
3366 {
3367 if (!validOnDate || !cert) return errSecParam;
3368
3369 CFAbsoluteTime at, nb, na;
3370 if (CFGetTypeID(validOnDate) == CFDateGetTypeID())
3371 at = CFDateGetAbsoluteTime((CFDateRef)validOnDate);
3372 else
3373 at = CFAbsoluteTimeGetCurrent();
3374
3375 OSStatus status = errSecSuccess;
3376 nb = SecCertificateNotValidBefore(cert);
3377 na = SecCertificateNotValidAfter(cert);
3378
3379 if (nb == 0 || na == 0 || nb == na)
3380 status = errSecCertificateCannotOperate;
3381 else if (at < nb)
3382 status = errSecCertificateNotValidYet;
3383 else if (at > na)
3384 status = errSecCertificateExpired;
3385
3386 return status;
3387 }
3388
3389 static OSStatus
3390 _FilterWithTrust(Boolean trustedOnly, SecCertificateRef cert)
3391 {
3392 if (!cert) return errSecParam;
3393 if (!trustedOnly) return errSecSuccess;
3394
3395 CFArrayRef certArray = CFArrayCreate(NULL, (const void**)&cert, 1, &kCFTypeArrayCallBacks);
3396 SecPolicyRef policy = SecPolicyCreateWithOID(kSecPolicyAppleX509Basic);
3397 OSStatus status = (policy == NULL) ? errSecPolicyNotFound : errSecSuccess;
3398
3399 if (!status) {
3400 SecTrustRef trust = NULL;
3401 status = SecTrustCreateWithCertificates(certArray, policy, &trust);
3402 if (!status) {
3403 SecTrustResultType trustResult;
3404 status = SecTrustEvaluate(trust, &trustResult);
3405 if (!status) {
3406 if (!(trustResult == kSecTrustResultProceed || trustResult == kSecTrustResultUnspecified)) {
3407 status = (trustResult == kSecTrustResultDeny) ? errSecTrustSettingDeny : errSecNotTrusted;
3408 }
3409 }
3410 CFRelease(trust);
3411 }
3412 CFRelease(policy);
3413 }
3414 if (certArray) {
3415 CFRelease(certArray);
3416 }
3417
3418 return status;
3419 }
3420
3421 static SecKeychainItemRef
3422 CopyResolvedKeychainItem(CFTypeRef item)
3423 {
3424 SecKeychainItemRef kcItem = NULL;
3425 OSStatus status = errSecSuccess;
3426 if (item) {
3427 if (CFGetTypeID(item) == CFDataGetTypeID()) {
3428 // persistent reference, resolve first
3429 status = SecKeychainItemCopyFromPersistentReference((CFDataRef)item, &kcItem);
3430 }
3431 else {
3432 // normal reference
3433 kcItem = (SecKeychainItemRef) CFRetain(item);
3434 }
3435 if (kcItem) {
3436 // ask for the item's class:
3437 // will return an error if the item has been deleted
3438 SecItemClass itemClass;
3439 SecCertificateRef certRef = NULL;
3440 CFTypeID itemTypeID = CFGetTypeID(kcItem);
3441 if (itemTypeID == SecIdentityGetTypeID()) {
3442 status = SecIdentityCopyCertificate((SecIdentityRef)kcItem, &certRef);
3443 }
3444 else if (itemTypeID == SecCertificateGetTypeID()) {
3445 certRef = (SecCertificateRef) CFRetain(kcItem);
3446 }
3447 if (certRef) {
3448 // can't call SecKeychainItemCopyAttributesAndData on a SecCertificateRef
3449 itemClass = kSecCertificateItemClass;
3450 }
3451 else {
3452 status = SecKeychainItemCopyAttributesAndData(kcItem, NULL, &itemClass, NULL, NULL, NULL);
3453 }
3454 if (certRef) {
3455 CFRelease(certRef);
3456 }
3457 if (status) {
3458 CFRelease(kcItem);
3459 kcItem = NULL;
3460 }
3461 }
3462 }
3463 return kcItem;
3464 }
3465
3466 static OSStatus
3467 UpdateKeychainSearchAndCopyNext(SecItemParams *params, CFTypeRef *item)
3468 {
3469 // This function refreshes the search parameters in the specific case where
3470 // the caller is searching for kSecClassKey items but did not provide the
3471 // kSecAttrKeyClass. In that case, params->assumedKeyClass will be set, and
3472 // we must perform separate searches to obtain all results.
3473
3474 OSStatus status = errSecItemNotFound;
3475 if (!params || !params->assumedKeyClass || !params->query || !item)
3476 return status;
3477
3478 // Free the previous search reference and attribute list.
3479 if (params->search)
3480 CFRelease(params->search);
3481 params->search = NULL;
3482 _FreeAttrList(params->attrList);
3483 params->attrList = NULL;
3484
3485 // Make a copy of the query dictionary so we can set the key class parameter.
3486 CFMutableDictionaryRef dict = CFDictionaryCreateMutableCopy(NULL, 0, params->query);
3487 CFRelease(params->query);
3488 params->query = dict;
3489 CFDictionarySetValue(dict, kSecAttrKeyClass, params->assumedKeyClass);
3490
3491 // Determine the current item class for this search, and the next assumed key class.
3492 if (CFEqual(params->assumedKeyClass, kSecAttrKeyClassSymmetric)) {
3493 params->itemClass = kSecSymmetricKeyItemClass;
3494 params->assumedKeyClass = kSecAttrKeyClassPublic;
3495 } else if (CFEqual(params->assumedKeyClass, kSecAttrKeyClassPublic)) {
3496 params->itemClass = kSecPublicKeyItemClass;
3497 params->assumedKeyClass = kSecAttrKeyClassPrivate;
3498 } else {
3499 params->itemClass = kSecPrivateKeyItemClass;
3500 params->assumedKeyClass = NULL;
3501 }
3502
3503 // Rebuild the attribute list for the new key class.
3504 if (_CreateSecKeychainAttributeListFromDictionary(dict, params->itemClass, &params->attrList) == errSecSuccess) {
3505 // Create a new search reference for the new attribute list.
3506 if (SecKeychainSearchCreateFromAttributes(params->searchList,
3507 params->itemClass,
3508 (params->attrList->count == 0) ? NULL : params->attrList,
3509 (SecKeychainSearchRef*)&params->search) == errSecSuccess) {
3510 // Return the first matching item from the new search.
3511 // We won't come back here again until there are no more matching items for this search.
3512 status = SecKeychainSearchCopyNext((SecKeychainSearchRef)params->search, (SecKeychainItemRef*)item);
3513 }
3514 }
3515 return status;
3516 }
3517
3518
3519 static OSStatus
3520 SecItemSearchCopyNext(SecItemParams *params, CFTypeRef *item)
3521 {
3522 // Generic "copy next match" function for SecKeychainSearchRef or SecIdentitySearchRef.
3523 // Returns either a SecKeychainItemRef or a SecIdentityRef in the output parameter,
3524 // depending on the type of search reference.
3525
3526 OSStatus status;
3527 CFTypeRef search = (params) ? params->search : NULL;
3528 CFTypeID typeID = (search) ? CFGetTypeID(search) : 0;
3529 if (typeID == SecIdentitySearchGetTypeID()) {
3530 status = SecIdentitySearchCopyNext((SecIdentitySearchRef)search, (SecIdentityRef*)item);
3531 }
3532 else if (typeID == SecKeychainSearchGetTypeID()) {
3533 status = SecKeychainSearchCopyNext((SecKeychainSearchRef)search, (SecKeychainItemRef*)item);
3534 // Check if we need to refresh the search for the next key class
3535 while (status == errSecItemNotFound && params->assumedKeyClass != NULL)
3536 status = UpdateKeychainSearchAndCopyNext(params, item);
3537 }
3538 else if (typeID == 0 && (params->useItems || params->itemList)) {
3539 // No search available, but there is an item list available.
3540 // Return the next candidate item from the caller's item list
3541 CFArrayRef itemList = (params->useItems) ? params->useItems : params->itemList;
3542 CFIndex count = CFArrayGetCount(itemList);
3543 *item = (CFTypeRef) NULL;
3544 if (params->itemListIndex < count) {
3545 *item = (CFTypeRef)CFArrayGetValueAtIndex(itemList, params->itemListIndex++);
3546 if (*item) {
3547 // Potentially resolve persistent item references here, and
3548 // verify the item reference we're about to hand back is still
3549 // valid (it could have been deleted from the keychain while
3550 // our query was holding onto the itemList).
3551 *item = CopyResolvedKeychainItem(*item);
3552 if (*item && (CFGetTypeID(*item) == SecIdentityGetTypeID())) {
3553 // Persistent reference resolved to an identity, so return that type.
3554 params->returnIdentity = true;
3555 }
3556 }
3557 }
3558 status = (*item) ? errSecSuccess : errSecItemNotFound;
3559 }
3560 else {
3561 status = errSecItemNotFound;
3562 }
3563 return status;
3564 }
3565
3566 static OSStatus
3567 FilterCandidateItem(CFTypeRef *item, SecItemParams *itemParams, SecIdentityRef *identity)
3568 {
3569 if (!item || *item == NULL || !itemParams)
3570 return errSecItemNotFound;
3571
3572 OSStatus status;
3573 CFStringRef commonName = NULL;
3574 SecIdentityRef foundIdentity = NULL;
3575 if (CFGetTypeID(*item) == SecIdentityGetTypeID()) {
3576 // we found a SecIdentityRef, rather than a SecKeychainItemRef;
3577 // replace the found "item" with its associated certificate (which is the
3578 // item we actually want for purposes of getting attributes, data, or a
3579 // persistent data reference), and return the identity separately.
3580 SecCertificateRef certificate;
3581 status = SecIdentityCopyCertificate((SecIdentityRef) *item, &certificate);
3582 if (itemParams->returnIdentity) {
3583 foundIdentity = (SecIdentityRef) *item;
3584 if (identity) {
3585 *identity = foundIdentity;
3586 }
3587 }
3588 else {
3589 CFRelease(*item);
3590 }
3591 *item = (CFTypeRef)certificate;
3592 }
3593
3594 CFDictionaryRef query = itemParams->query;
3595
3596 if (itemParams->itemClass == kSecCertificateItemClass) {
3597 // perform string comparisons first
3598 CFStringCompareFlags flags = _StringCompareFlagsFromQuery(query);
3599 CFStringRef nameContains, nameStarts, nameEnds, nameExact;
3600 if (!CFDictionaryGetValueIfPresent(query, kSecMatchSubjectContains, (const void **)&nameContains))
3601 nameContains = NULL;
3602 if (!CFDictionaryGetValueIfPresent(query, kSecMatchSubjectStartsWith, (const void **)&nameStarts))
3603 nameStarts = NULL;
3604 if (!CFDictionaryGetValueIfPresent(query, kSecMatchSubjectEndsWith, (const void **)&nameEnds))
3605 nameEnds = NULL;
3606 if (!CFDictionaryGetValueIfPresent(query, kSecMatchSubjectWholeString, (const void **)&nameExact))
3607 nameExact = NULL;
3608 if (nameContains || nameStarts || nameEnds || nameExact) {
3609 status = SecCertificateCopyCommonName((SecCertificateRef)*item, &commonName);
3610 if (status || !commonName) goto filterOut;
3611 }
3612 if (nameContains) {
3613 CFRange range = CFStringFind(commonName, nameContains, flags);
3614 if (range.length < 1)
3615 goto filterOut;
3616 // certificate item contains string; proceed to next check
3617 }
3618 if (nameStarts) {
3619 CFRange range = CFStringFind(commonName, nameStarts, flags);
3620 if (range.length < 1 || range.location > 1)
3621 goto filterOut;
3622 // certificate item starts with string; proceed to next check
3623 }
3624 if (nameEnds) {
3625 CFRange range = CFStringFind(commonName, nameEnds, flags);
3626 if (range.length < 1 || range.location != (CFStringGetLength(commonName) - CFStringGetLength(nameEnds)))
3627 goto filterOut;
3628 // certificate item ends with string; proceed to next check
3629 }
3630 if (nameExact) {
3631 CFRange range = CFStringFind(commonName, nameExact, flags);
3632 if (range.length < 1 || (CFStringGetLength(commonName) != CFStringGetLength(nameExact)))
3633 goto filterOut;
3634 // certificate item exactly matches string; proceed to next check
3635 }
3636 if (itemParams->returnIdentity) {
3637 // if we already found and returned the identity, we can skip this
3638 if (!foundIdentity) {
3639 status = SecIdentityCreateWithCertificate(itemParams->searchList, (SecCertificateRef) *item, identity);
3640 if (status) goto filterOut;
3641 }
3642 // certificate item is part of an identity; proceed to next check
3643 }
3644 if (itemParams->policy) {
3645 status = _FilterWithPolicy(itemParams->policy, (CFDateRef)itemParams->validOnDate, (SecCertificateRef) *item);
3646 if (status) goto filterOut;
3647 // certificate item is valid for specified policy (and optionally specified date)
3648 }
3649 if (itemParams->validOnDate) {
3650 status = _FilterWithDate(itemParams->validOnDate, (SecCertificateRef) *item);
3651 if (status) goto filterOut;
3652 // certificate item is valid for specified date
3653 }
3654 if (itemParams->trustedOnly) {
3655 // if we are getting candidate items from a SecIdentitySearchCreateWithPolicy search,
3656 // their trust has already been validated and we can skip this part.
3657 if (!(foundIdentity && itemParams->returnIdentity && itemParams->policy)) {
3658 status = _FilterWithTrust(itemParams->trustedOnly, (SecCertificateRef) *item);
3659 if (status) goto filterOut;
3660 }
3661 // certificate item is trusted on this system
3662 }
3663 }
3664 if (itemParams->itemList) {
3665 Boolean foundMatch = FALSE;
3666 CFIndex idx, count = CFArrayGetCount(itemParams->itemList);
3667 for (idx=0; idx<count; idx++) {
3668 CFTypeRef anItem = (CFTypeRef) CFArrayGetValueAtIndex(itemParams->itemList, idx);
3669 SecKeychainItemRef realItem = NULL;
3670 SecCertificateRef aCert = NULL;
3671 if (anItem == NULL) {
3672 continue;
3673 }
3674 if (CFDataGetTypeID() == CFGetTypeID(anItem) &&
3675 errSecSuccess == SecKeychainItemCopyFromPersistentReference((CFDataRef)anItem, &realItem)) {
3676 anItem = realItem;
3677 }
3678 if (SecIdentityGetTypeID() == CFGetTypeID(anItem) &&
3679 errSecSuccess == SecIdentityCopyCertificate((SecIdentityRef)anItem, &aCert)) {
3680 anItem = aCert;
3681 }
3682 if (CFEqual(anItem, (CFTypeRef) *item)) {
3683 foundMatch = TRUE;
3684 }
3685 if (aCert) {
3686 CFRelease(aCert);
3687 }
3688 if (realItem) {
3689 CFRelease(realItem);
3690 }
3691 if (foundMatch) {
3692 break;
3693 }
3694 }
3695 if (!foundMatch) goto filterOut;
3696 // item was found on provided list
3697 }
3698
3699 if (foundIdentity && !identity) {
3700 CFRelease(foundIdentity);
3701 }
3702 if (commonName) {
3703 CFRelease(commonName);
3704 }
3705
3706 // if we get here, consider the item a match
3707 return errSecSuccess;
3708
3709 filterOut:
3710 if (commonName) {
3711 CFRelease(commonName);
3712 }
3713 CFRelease(*item);
3714 *item = NULL;
3715 if (foundIdentity) {
3716 CFRelease(foundIdentity);
3717 if (identity) {
3718 *identity = NULL;
3719 }
3720 }
3721 return errSecItemNotFound;
3722 }
3723
3724 static OSStatus
3725 AddItemResults(SecKeychainItemRef item,
3726 SecIdentityRef identity,
3727 SecItemParams *itemParams,
3728 CFAllocatorRef allocator,
3729 CFMutableArrayRef *items,
3730 CFTypeRef *result)
3731 {
3732 // Given a found item (which may also be an identity), this function adds
3733 // the requested result types (specified in itemParams) to the appropriate
3734 // container as follows:
3735 //
3736 // 1. If there is only one result type (numResultTypes == 1) and only one
3737 // match requested (maxMatches == 1), set *result directly.
3738 //
3739 // 2. If there are multiple result types (numResultTypes > 1), and only one
3740 // match requested (maxMatches == 1), add each result type to itemDict
3741 // and set itemDict as the value of *result.
3742 //
3743 // 3. If there is only one result type (numResultTypes == 1) and multiple
3744 // possible matches (maxMatches > 1), add the result type to *items
3745 // and set *items as the value of *result.
3746 //
3747 // 4. If there are multiple result types (numResultTypes > 1) and multiple
3748 // possible matches (maxMatches > 1), add each result type to itemDict,
3749 // add itemDict to *items, and set *items as the value of *result.
3750 //
3751 // Note that we allocate *items if needed.
3752
3753 if (!item || !itemParams || !result)
3754 return errSecParam;
3755
3756 if (itemParams->maxMatches > 1) {
3757 // if we can return more than one item, we must have an array
3758 if (!items)
3759 return errSecParam;
3760 else if (*items == NULL)
3761 *items = CFArrayCreateMutable(allocator, 0, &kCFTypeArrayCallBacks);
3762 }
3763
3764 OSStatus tmpStatus, status = errSecSuccess;
3765 CFMutableArrayRef itemArray = (items) ? *items : NULL;
3766 CFMutableDictionaryRef itemDict = NULL;
3767 if (itemParams->numResultTypes > 1) {
3768 // if we're returning more than one result type, each item we return must be a dictionary
3769 itemDict = CFDictionaryCreateMutable(allocator, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
3770 }
3771
3772 if (itemParams->returningRef) {
3773 const void* itemRef = (identity) ? (const void*)identity : (const void*)item;
3774 if (itemDict) {
3775 CFDictionaryAddValue(itemDict, kSecValueRef, itemRef);
3776 }
3777 else if (itemArray) {
3778 CFArrayAppendValue(itemArray, itemRef);
3779 }
3780 else {
3781 *result = CFRetain((CFTypeRef)itemRef);
3782 }
3783 }
3784
3785 if (itemParams->returningPersistentRef) {
3786 CFDataRef persistentRef;
3787 SecKeychainItemRef tmpItem = item;
3788 if (itemParams->identityRef) {
3789 tmpItem = (SecKeychainItemRef)itemParams->identityRef;
3790 }
3791 tmpStatus = SecKeychainItemCreatePersistentReference(tmpItem, &persistentRef);
3792 if (tmpStatus == errSecSuccess) {
3793 if (itemDict) {
3794 CFDictionaryAddValue(itemDict, kSecValuePersistentRef, persistentRef);
3795 }
3796 else if (itemArray) {
3797 CFArrayAppendValue(itemArray, persistentRef);
3798 }
3799 else {
3800 *result = CFRetain(persistentRef);
3801 }
3802 CFRelease(persistentRef);
3803 }
3804 else if (status == errSecSuccess) {
3805 status = tmpStatus;
3806 }
3807 }
3808
3809 if (itemParams->returningData) {
3810 // Use SecCertificateCopyData if we have a SecCertificateRef item.
3811 // Note that a SecCertificateRef may not actually be a SecKeychainItem,
3812 // in which case SecKeychainItemCopyContent will not obtain its data.
3813
3814 if (CFGetTypeID(item) == SecCertificateGetTypeID()) {
3815 CFDataRef dataRef = SecCertificateCopyData((SecCertificateRef)item);
3816 if (dataRef) {
3817 if (itemDict) {
3818 CFDictionaryAddValue(itemDict, kSecValueData, dataRef);
3819 }
3820 else if (itemArray) {
3821 CFArrayAppendValue(itemArray, dataRef);
3822 }
3823 else {
3824 *result = CFRetain(dataRef);
3825 }
3826 CFRelease(dataRef);
3827 status = errSecSuccess;
3828 }
3829 else {
3830 status = errSecAllocate;
3831 }
3832 }
3833 else {
3834 UInt32 length;
3835 void *data;
3836 tmpStatus = SecKeychainItemCopyContent(item, NULL, NULL, &length, &data);
3837 if (tmpStatus == errSecSuccess) {
3838 CFDataRef dataRef = CFDataCreate(allocator, (UInt8 *)data, length);
3839 if (itemDict) {
3840 CFDictionaryAddValue(itemDict, kSecValueData, dataRef);
3841 }
3842 else if (itemArray) {
3843 CFArrayAppendValue(itemArray, dataRef);
3844 }
3845 else {
3846 *result = CFRetain(dataRef);
3847 }
3848 CFRelease(dataRef);
3849 (void) SecKeychainItemFreeContent(NULL, data);
3850 }
3851 else if (status == errSecSuccess) {
3852 status = tmpStatus;
3853 }
3854 }
3855 }
3856
3857 if (itemParams->returningAttributes) {
3858 CFDictionaryRef attrsDict = NULL;
3859 SecItemClass itemClass;
3860 // since we have an item, allow its actual class to override the query-specified item class
3861 tmpStatus = SecKeychainItemCopyAttributesAndData(item, NULL, &itemClass, NULL, NULL, NULL);
3862 if (tmpStatus) {
3863 itemClass = itemParams->itemClass;
3864 }
3865 tmpStatus = _CreateAttributesDictionaryFromItem(allocator, itemClass, item, &attrsDict);
3866 if (attrsDict) {
3867 if (itemDict) {
3868 // add all keys and values from attrsDict to the item dictionary
3869 CFDictionaryApplyFunction(attrsDict, _AddDictValueToOtherDict, &itemDict);
3870 }
3871 else if (itemArray) {
3872 CFArrayAppendValue(itemArray, attrsDict);
3873 }
3874 else {
3875 *result = CFRetain(attrsDict);
3876 }
3877 CFRelease(attrsDict);
3878 }
3879 if (tmpStatus && (status == errSecSuccess)) {
3880 status = tmpStatus;
3881 }
3882 }
3883
3884 if (itemDict) {
3885 if (itemArray) {
3886 CFArrayAppendValue(itemArray, itemDict);
3887 CFRelease(itemDict);
3888 *result = itemArray;
3889 }
3890 else {
3891 *result = itemDict;
3892 }
3893 }
3894 else if (itemArray) {
3895 *result = itemArray;
3896 }
3897
3898 return status;
3899 }
3900
3901 CFDataRef _SecItemGetPersistentReference(CFTypeRef raw_item)
3902 {
3903 try {
3904 Item item = ItemImpl::required((SecKeychainItemRef)raw_item);
3905 return item->getPersistentRef();
3906 } catch(...) {
3907 return NULL;
3908 }
3909 }
3910
3911 /******************************************************************************/
3912 #pragma mark SecItem API functions
3913 /******************************************************************************/
3914
3915 //
3916 // Approximate result of using iOS sec's copyNumber, 0 return could be zero, or error.
3917 //
3918 static SInt32 readNumber(CFTypeRef obj) {
3919 CFTypeID tid = CFGetTypeID(obj);
3920 SInt32 v = 0;
3921 if (tid == CFNumberGetTypeID()) {
3922 CFNumberGetValue((CFNumberRef)obj, kCFNumberSInt32Type, &v);
3923 return v;
3924 } else if (tid == CFBooleanGetTypeID()) {
3925 v = CFBooleanGetValue((CFBooleanRef)obj);
3926 return v;
3927 } else if (tid == CFStringGetTypeID()) {
3928 v = CFStringGetIntValue((CFStringRef)obj);
3929 CFStringRef t = CFStringCreateWithFormat(0, 0, CFSTR("%ld"), (long)v);
3930 /* If a string converted to an int isn't equal to the int printed as
3931 a string, return a CFStringRef instead. */
3932 if (!CFEqual(t, obj)) {
3933 CFRelease(t);
3934 return 0;
3935 }
3936 CFRelease(t);
3937 return v;
3938 } else
3939 return NULL;
3940 }
3941
3942 //
3943 // Function to check whether the kSecAttrSynchronizable flag is set in the query.
3944 //
3945 static Boolean SecItemSynchronizable(CFDictionaryRef query)
3946 {
3947 CFTypeRef value = CFDictionaryGetValue(query, kSecAttrSynchronizable);
3948 Boolean result = (value && readNumber(value));
3949
3950 return result;
3951 }
3952
3953 //
3954 // Function to check whether a synchronizable persistent reference was provided.
3955 //
3956 static Boolean SecItemIsIOSPersistentReference(CFTypeRef value)
3957 {
3958 if (value) {
3959 /* Synchronizable persistent ref consists of the sqlite rowid and 4-byte class value */
3960 const CFIndex kSynchronizablePersistentRefLength = sizeof(int64_t) + 4;
3961 return (CFGetTypeID(value) == CFDataGetTypeID() &&
3962 CFDataGetLength((CFDataRef)value) == kSynchronizablePersistentRefLength);
3963 }
3964 return false;
3965 }
3966
3967 extern "C" Boolean SecKeyIsCDSAKey(SecKeyRef ref);
3968
3969 //
3970 // Function to find out which keychains are targetted by the query.
3971 //
3972 static OSStatus SecItemCategorizeQuery(CFDictionaryRef query, bool &can_target_ios, bool &can_target_osx)
3973 {
3974 // By default, target both keychain.
3975 can_target_osx = can_target_ios = true;
3976
3977 // Check no-legacy flag.
3978 CFTypeRef value = CFDictionaryGetValue(query, kSecAttrNoLegacy);
3979 if (value != NULL) {
3980 can_target_ios = readNumber(value) != 0;
3981 can_target_osx = !can_target_ios;
3982 return errSecSuccess;
3983 }
3984
3985 // Check whether the query contains kSecValueRef and modify can_ flags according to the kind and type of the value.
3986 value = CFDictionaryGetValue(query, kSecValueRef);
3987 if (value != NULL) {
3988 CFTypeID typeID = CFGetTypeID(value);
3989 if (typeID == SecKeyGetTypeID()) {
3990 can_target_osx = SecKeyIsCDSAKey((SecKeyRef)value);
3991 can_target_ios = !can_target_osx;
3992 } else if (typeID == SecCertificateGetTypeID()) {
3993 // All types of certificates can target OSX keychains, but OSX certificates won't work on iOS
3994 can_target_ios &= !SecCertificateIsItemImplInstance((SecCertificateRef)value);
3995 } else if (typeID == SecKeychainItemGetTypeID()) {
3996 // SecKeychainItemRef can target iOS keychain only when it has attached iOS-style persistent reference.
3997 if (_SecItemGetPersistentReference(value) == NULL) {
3998 can_target_ios = false;
3999 }
4000 }
4001 }
4002
4003 // Check presence of kSecAttrTokenID and kSecAttrAccessControl; they are not defined for CDSA keychain.
4004 if (CFDictionaryContainsKey(query, kSecAttrTokenID) || CFDictionaryContainsKey(query, kSecAttrAccessControl)) {
4005 can_target_osx = false;
4006 }
4007
4008 // Check for special token access groups. If present, redirect query to iOS keychain.
4009 value = CFDictionaryGetValue(query, kSecAttrAccessGroup);
4010 if (value != NULL && CFEqual(value, kSecAttrAccessGroupToken)) {
4011 can_target_osx = false;
4012 }
4013
4014 // Synchronizable items should go to iOS keychain only.
4015 if (SecItemSynchronizable(query)) {
4016 can_target_osx = false;
4017 }
4018
4019 value = CFDictionaryGetValue(query, kSecValuePersistentRef);
4020 if (value != NULL) {
4021 if (SecItemIsIOSPersistentReference(value)) {
4022 can_target_osx = false;
4023 } else {
4024 // Non-iOS-style persistent references should not be fed to iOS keychain queries.
4025 can_target_ios = false;
4026 }
4027 }
4028
4029 // Presence of following atributes means that query is OSX-only.
4030 static const CFStringRef *osx_only_items[] = {
4031 &kSecMatchItemList,
4032 &kSecMatchSearchList,
4033 &kSecMatchSubjectStartsWith,
4034 &kSecMatchSubjectEndsWith,
4035 &kSecMatchSubjectWholeString,
4036 &kSecMatchDiacriticInsensitive,
4037 &kSecMatchWidthInsensitive,
4038 &kSecUseItemList,
4039 &kSecUseKeychain,
4040 &kSecAttrAccess,
4041 &kSecAttrPRF,
4042 &kSecAttrSalt,
4043 &kSecAttrRounds,
4044 };
4045
4046 for (CFIndex i = 0; i < array_size(osx_only_items); i++) {
4047 can_target_ios = can_target_ios && !CFDictionaryContainsKey(query, *osx_only_items[i]);
4048 }
4049
4050 return (can_target_ios || can_target_osx) ? errSecSuccess : errSecParam;
4051 }
4052
4053 //
4054 // Function to check whether the kSecAttrSynchronizable attribute is being updated.
4055 //
4056 static Boolean SecItemHasSynchronizableUpdate(Boolean synchronizable, CFDictionaryRef changes)
4057 {
4058 CFTypeRef newValue = CFDictionaryGetValue(changes, kSecAttrSynchronizable);
4059 if (!newValue)
4060 return false;
4061
4062 Boolean new_sync = readNumber(newValue);
4063 Boolean old_sync = synchronizable;
4064
4065 return (old_sync != new_sync);
4066 }
4067
4068 //
4069 // Function to apply changes to a mutable dictionary.
4070 // (CFDictionaryApplierFunction, called by CFDictionaryApplyFunction)
4071 //
4072 static void SecItemApplyChanges(const void *key, const void *value, void *context)
4073 {
4074 CFMutableDictionaryRef dict = (CFMutableDictionaryRef) context;
4075 if (!dict) return;
4076
4077 CFDictionarySetValue(dict, key, value);
4078 }
4079
4080 //
4081 // Function to change matching items from non-syncable to syncable
4082 // (if toSyncable is true), otherwise from syncable to non-syncable.
4083 // This currently moves items between keychain containers.
4084 //
4085 static OSStatus SecItemChangeSynchronizability(CFDictionaryRef query, CFDictionaryRef changes, Boolean toSyncable)
4086 {
4087 // Note: the input query dictionary is a mutable copy of the query originally
4088 // provided by the caller as the first parameter to SecItemUpdate. It may not
4089 // specify returning attributes or data, but we will need both to make a copy.
4090 //
4091 CFDictionaryRemoveValue((CFMutableDictionaryRef)query, kSecReturnRef);
4092 CFDictionaryRemoveValue((CFMutableDictionaryRef)query, kSecReturnPersistentRef);
4093 CFDictionaryRemoveValue((CFMutableDictionaryRef)query, kSecReturnData);
4094 CFDictionarySetValue((CFMutableDictionaryRef)query, kSecReturnAttributes, kCFBooleanTrue);
4095 if (NULL == CFDictionaryGetValue(changes, kSecValueData))
4096 CFDictionarySetValue((CFMutableDictionaryRef)query, kSecReturnData, kCFBooleanTrue);
4097
4098 CFTypeRef result;
4099 OSStatus status;
4100 if (toSyncable)
4101 status = SecItemCopyMatching_osx(query, &result);
4102 else
4103 status = SecItemCopyMatching_ios(query, &result);
4104
4105 if (status)
4106 return status;
4107 if (!result)
4108 return errSecItemNotFound;
4109
4110 CFMutableArrayRef items;
4111 if (CFGetTypeID(result) != CFArrayGetTypeID()) {
4112 items = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
4113 CFArrayAppendValue(items, result);
4114 CFRelease(result);
4115 }
4116 else {
4117 items = (CFMutableArrayRef)result;
4118 }
4119
4120 CFIndex idx, count = (items) ? CFArrayGetCount(items) : 0;
4121 int priority = LOG_DEBUG;
4122 OSStatus err = 0;
4123 for (idx = 0; idx < count; idx++) {
4124 CFDictionaryRef dict = (CFDictionaryRef) CFArrayGetValueAtIndex(items, idx);
4125 CFMutableDictionaryRef item = (CFMutableDictionaryRef)
4126 SecItemCopyTranslatedAttributes(dict,
4127 CFDictionaryGetValue(query, kSecClass),
4128 (toSyncable) ? true : false /*iOSOut*/,
4129 true /*pruneMatch*/,
4130 true /*pruneSync*/,
4131 true /*pruneReturn*/,
4132 false /*pruneData*/,
4133 (toSyncable) ? true : false /*pruneAccess*/);
4134 // hold onto the query before applying changes, in case the item already exists.
4135 // note that we cannot include the creation or modification dates from our
4136 // found item in this query, as they may not match the item in the other keychain.
4137 CFMutableDictionaryRef itemQuery = CFDictionaryCreateMutableCopy(NULL, 0, item);
4138 CFDictionaryRemoveValue(itemQuery, kSecAttrCreationDate);
4139 CFDictionaryRemoveValue(itemQuery, kSecAttrModificationDate);
4140 // apply changes to the item dictionary that we will pass to SecItemAdd
4141 CFDictionaryApplyFunction(changes, SecItemApplyChanges, item);
4142 if (toSyncable) {
4143 CFDictionarySetValue(item, kSecAttrSynchronizable, kCFBooleanTrue);
4144 status = SecItemAdd_ios(item, NULL);
4145 secitemlog(priority, "ChangeSync: SecItemAdd_ios=%d", status);
4146 if (errSecDuplicateItem == status) {
4147 // find and apply changes to the existing syncable item.
4148 CFDictionarySetValue(itemQuery, kSecAttrSynchronizable, kCFBooleanTrue);
4149 status = SecItemUpdate_ios(itemQuery, changes);
4150 secitemlog(priority, "ChangeSync: SecItemUpdate_ios=%d", status);
4151 }
4152 if (errSecSuccess == status) {
4153 CFDictionarySetValue(itemQuery, kSecAttrSynchronizable, kCFBooleanFalse);
4154 status = SecItemDelete_osx(itemQuery);
4155 secitemlog(priority, "ChangeSync: SecItemDelete_osx=%d", status);
4156 }
4157 }
4158 else {
4159 CFDictionarySetValue(item, kSecAttrSynchronizable, kCFBooleanFalse);
4160 status = SecItemAdd_osx(item, NULL);
4161 secitemlog(priority, "ChangeSync: SecItemAdd_osx=%d", status);
4162 if (errSecDuplicateItem == status) {
4163 // find and apply changes to the existing non-syncable item.
4164 CFDictionarySetValue(itemQuery, kSecAttrSynchronizable, kCFBooleanFalse);
4165 status = SecItemUpdate_osx(itemQuery, changes);
4166 secitemlog(priority, "ChangeSync: SecItemUpdate_osx=%d", status);
4167 }
4168 if (errSecSuccess == status) {
4169 CFDictionarySetValue(itemQuery, kSecAttrSynchronizable, kCFBooleanTrue);
4170 status = SecItemDelete_ios(itemQuery);
4171 secitemlog(priority, "ChangeSync: SecItemDelete_ios=%d", status);
4172 }
4173 }
4174 CFReleaseSafe(item);
4175 CFReleaseSafe(itemQuery);
4176 if (status)
4177 err = status;
4178 }
4179 CFReleaseSafe(items);
4180
4181 return err;
4182 }
4183
4184
4185 extern "C" {
4186
4187 CFTypeRef
4188 SecItemCreateFromAttributeDictionary_osx(CFDictionaryRef refAttributes) {
4189 CFTypeRef ref = NULL;
4190 CFStringRef item_class_string = (CFStringRef)CFDictionaryGetValue(refAttributes, kSecClass);
4191 SecItemClass item_class = 0;
4192
4193 if (CFEqual(item_class_string, kSecClassGenericPassword)) {
4194 item_class = kSecGenericPasswordItemClass;
4195 } else if (CFEqual(item_class_string, kSecClassInternetPassword)) {
4196 item_class = kSecInternetPasswordItemClass;
4197 }
4198
4199 if (item_class != 0) {
4200 // we carry v_Data around here so the *_ios calls can find it and locate
4201 // their own data. Putting things in the attribute list doesn't help as
4202 // the osx keychainitem and item calls bail when they don't see a keychain
4203 // object. If we need to make them work we either have to bridge them, or
4204 // find a way to craft a workable keychain object. #if'ed code left below
4205 // in case we need to go down that path.
4206
4207 SecKeychainAttributeList attrs = {};
4208 SecKeychainAttribute attr = {};
4209
4210 attrs.attr = &attr;
4211 attrs.count = 0;
4212 CFTypeRef v;
4213
4214 Item item = Item(item_class, &attrs, 0, "");
4215 v = CFDictionaryGetValue(refAttributes, kSecValuePersistentRef);
4216 if (v) {
4217 item->setPersistentRef((CFDataRef)v);
4218 }
4219 ref = item->handle();
4220 }
4221
4222 return ref;
4223 }
4224
4225 /*
4226 * SecItemValidateAppleApplicationGroupAccess determines if the caller
4227 * is a member of the specified application group, and is signed by Apple.
4228 */
4229 OSStatus
4230 SecItemValidateAppleApplicationGroupAccess(CFStringRef group)
4231 {
4232 SecTrustedApplicationRef app = NULL;
4233 SecRequirementRef requirement = NULL;
4234 SecCodeRef code = NULL;
4235 OSStatus status = errSecParam;
4236
4237 if (group) {
4238 CFIndex length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(group), kCFStringEncodingUTF8) + 1;
4239 char* buffer = (char*) malloc(length);
4240 if (buffer) {
4241 if (CFStringGetCString(group, buffer, length, kCFStringEncodingUTF8)) {
4242 status = SecTrustedApplicationCreateApplicationGroup(buffer, NULL, &app);
4243 }
4244 free(buffer);
4245 } else {
4246 status = errSecMemoryError;
4247 }
4248 }
4249 if (!status) {
4250 status = SecTrustedApplicationCopyRequirement(app, &requirement);
4251 }
4252 if (!status) {
4253 status = SecCodeCopySelf(kSecCSDefaultFlags, &code);
4254 }
4255 if (!status) {
4256 status = SecCodeCheckValidity(code, kSecCSDefaultFlags, requirement);
4257 }
4258
4259 CFReleaseSafe(code);
4260 CFReleaseSafe(requirement);
4261 CFReleaseSafe(app);
4262 return status;
4263 }
4264
4265 static Mutex gParentCertCacheLock;
4266 static CFMutableDictionaryRef gParentCertCache;
4267 static CFMutableArrayRef gParentCertCacheList;
4268 #define PARENT_CACHE_SIZE 100
4269
4270 void SecItemParentCachePurge() {
4271 StLock<Mutex> _(gParentCertCacheLock);
4272 CFReleaseNull(gParentCertCache);
4273 CFReleaseNull(gParentCertCacheList);
4274 }
4275
4276 static CFArrayRef parentCacheRead(SecCertificateRef certificate) {
4277 CFArrayRef parents = NULL;
4278 CFIndex ix;
4279 CFDataRef digest = SecCertificateGetSHA1Digest(certificate);
4280 if (!digest) return NULL;
4281
4282 StLock<Mutex> _(gParentCertCacheLock);
4283 if (gParentCertCache && gParentCertCacheList) {
4284 if (0 <= (ix = CFArrayGetFirstIndexOfValue(gParentCertCacheList,
4285 CFRangeMake(0, CFArrayGetCount(gParentCertCacheList)),
4286 digest))) {
4287 // Cache hit. Get value and move entry to the top of the list.
4288 parents = (CFArrayRef)CFDictionaryGetValue(gParentCertCache, digest);
4289 CFArrayRemoveValueAtIndex(gParentCertCacheList, ix);
4290 CFArrayAppendValue(gParentCertCacheList, digest);
4291 }
4292 }
4293 CFRetainSafe(parents);
4294 return parents;
4295 }
4296
4297 static void parentCacheWrite(SecCertificateRef certificate, CFArrayRef parents) {
4298 CFDataRef digest = SecCertificateGetSHA1Digest(certificate);
4299 if (!digest) return;
4300
4301 StLock<Mutex> _(gParentCertCacheLock);
4302 if (!gParentCertCache || !gParentCertCacheList) {
4303 CFReleaseNull(gParentCertCache);
4304 gParentCertCache = makeCFMutableDictionary();
4305 CFReleaseNull(gParentCertCacheList);
4306 gParentCertCacheList = makeCFMutableArray(0);
4307 }
4308
4309 if (gParentCertCache && gParentCertCacheList) {
4310 // check to make sure another thread didn't add this entry to the cache already
4311 if (0 > CFArrayGetFirstIndexOfValue(gParentCertCacheList,
4312 CFRangeMake(0, CFArrayGetCount(gParentCertCacheList)),
4313 digest)) {
4314 CFDictionaryAddValue(gParentCertCache, digest, parents);
4315 if (PARENT_CACHE_SIZE <= CFArrayGetCount(gParentCertCacheList)) {
4316 // Remove least recently used cache entry.
4317 CFArrayRemoveValueAtIndex(gParentCertCacheList, 0);
4318 }
4319 CFArrayAppendValue(gParentCertCacheList, digest);
4320 }
4321 }
4322 }
4323
4324 /*
4325 * SecItemCopyParentCertificates returns an array of zero of more possible
4326 * issuer certificates for the provided certificate. No cryptographic validation
4327 * of the signature is performed in this function; its purpose is only to
4328 * provide a list of candidate certificates.
4329 */
4330 CFArrayRef
4331 SecItemCopyParentCertificates(SecCertificateRef certificate, void *context)
4332 {
4333 #pragma unused (context) /* for now; in future this can reference a container object */
4334 /* Check for parents in keychain cache */
4335 CFArrayRef parents = parentCacheRead(certificate);
4336 if (parents) {
4337 return parents;
4338 }
4339
4340 /* Cache miss. Query for parents. */
4341 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
4342 CFDataRef normalizedIssuer = SecCertificateCopyNormalizedIssuerContent(certificate, NULL);
4343 #else
4344 CFDataRef normalizedIssuer = SecCertificateGetNormalizedIssuerContent(certificate);
4345 CFRetainSafe(normalizedIssuer);
4346 #endif
4347 OSStatus status;
4348 CFMutableArrayRef combinedSearchList = NULL;
4349
4350 /* Define the array of keychains which will be searched for parents. */
4351 CFArrayRef searchList = NULL;
4352 status = SecKeychainCopySearchList(&searchList);
4353 if (searchList) {
4354 combinedSearchList = CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, searchList);
4355 CFRelease(searchList);
4356 } else {
4357 combinedSearchList = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
4358 }
4359 SecKeychainRef rootStoreKeychain = NULL;
4360 status = SecKeychainOpen(SYSTEM_ROOT_STORE_PATH, &rootStoreKeychain);
4361 if (rootStoreKeychain) {
4362 if (combinedSearchList) {
4363 CFArrayAppendValue(combinedSearchList, rootStoreKeychain);
4364 }
4365 CFRelease(rootStoreKeychain);
4366 }
4367
4368 /* Create and populate a fixed-size query dictionary. */
4369 CFMutableDictionaryRef query = CFDictionaryCreateMutable(NULL, 5,
4370 &kCFTypeDictionaryKeyCallBacks,
4371 &kCFTypeDictionaryValueCallBacks);
4372 CFDictionaryAddValue(query, kSecClass, kSecClassCertificate);
4373 CFDictionaryAddValue(query, kSecReturnData, kCFBooleanTrue);
4374 CFDictionaryAddValue(query, kSecMatchLimit, kSecMatchLimitAll);
4375 if (combinedSearchList) {
4376 CFDictionaryAddValue(query, kSecMatchSearchList, combinedSearchList);
4377 CFRelease(combinedSearchList);
4378 }
4379
4380 CFTypeRef results = NULL;
4381 if (normalizedIssuer) {
4382 /* Look up certs whose subject is the same as this cert's issuer. */
4383 CFDictionaryAddValue(query, kSecAttrSubject, normalizedIssuer);
4384 status = SecItemCopyMatching_osx(query, &results);
4385 }
4386 else {
4387 /* Cannot match anything without an issuer! */
4388 status = errSecItemNotFound;
4389 }
4390
4391 if ((status != errSecSuccess) && (status != errSecItemNotFound)) {
4392 secitemlog(LOG_WARNING, "SecItemCopyParentCertificates: %d", (int)status);
4393 }
4394 CFRelease(query);
4395
4396 CFMutableArrayRef result = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
4397 CFTypeID resultType = (results) ? CFGetTypeID(results) : 0;
4398 if (resultType == CFArrayGetTypeID()) {
4399 CFIndex index, count = CFArrayGetCount((CFArrayRef)results);
4400 for (index = 0; index < count; index++) {
4401 CFDataRef data = (CFDataRef) CFArrayGetValueAtIndex((CFArrayRef)results, index);
4402 if (data) {
4403 SecCertificateRef cert = SecCertificateCreateWithData(kCFAllocatorDefault, data);
4404 if (cert) {
4405 CFArrayAppendValue(result, cert);
4406 CFRelease(cert);
4407 }
4408 }
4409 }
4410 } else if (resultType == CFDataGetTypeID()) {
4411 SecCertificateRef cert = SecCertificateCreateWithData(kCFAllocatorDefault, (CFDataRef)results);
4412 if (cert) {
4413 CFArrayAppendValue(result, cert);
4414 CFRelease(cert);
4415 }
4416 }
4417 CFReleaseSafe(results);
4418 CFReleaseSafe(normalizedIssuer);
4419
4420 /* Add to cache. */
4421 parentCacheWrite(certificate, result);
4422
4423 return result;
4424 }
4425
4426 SecCertificateRef SecItemCopyStoredCertificate(SecCertificateRef certificate, void *context)
4427 {
4428 #pragma unused (context) /* for now; in future this can reference a container object */
4429
4430 /* Certificates are unique by issuer and serial number. */
4431 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
4432 CFDataRef serialNumber = SecCertificateCopySerialNumber(certificate, NULL);
4433 CFDataRef normalizedIssuer = SecCertificateCopyNormalizedIssuerContent(certificate, NULL);
4434 #else
4435 CFDataRef serialNumber = SecCertificateCopySerialNumber(certificate);
4436 CFDataRef normalizedIssuer = SecCertificateGetNormalizedIssuerContent(certificate);
4437 CFRetainSafe(normalizedIssuer);
4438 #endif
4439
4440 const void *keys[] = {
4441 kSecClass,
4442 kSecMatchLimit,
4443 kSecAttrIssuer,
4444 kSecAttrSerialNumber,
4445 kSecReturnRef
4446 },
4447 *values[] = {
4448 kSecClassCertificate,
4449 kSecMatchLimitOne,
4450 normalizedIssuer,
4451 serialNumber,
4452 kCFBooleanTrue
4453 };
4454 CFDictionaryRef query = CFDictionaryCreate(NULL, keys, values, 5, NULL, NULL);
4455 CFTypeRef result = NULL;
4456
4457 OSStatus status = SecItemCopyMatching_osx(query, &result);
4458 if ((status != errSecSuccess) && (status != errSecItemNotFound)) {
4459 secitemlog(LOG_WARNING, "SecItemCopyStoredCertificate: %d", (int)status);
4460 CFReleaseNull(result);
4461 }
4462 CFReleaseSafe(query);
4463 CFReleaseSafe(serialNumber);
4464 CFReleaseSafe(normalizedIssuer);
4465
4466 return (SecCertificateRef)result;
4467 }
4468
4469 /*
4470 * SecItemCopyTranslatedAttributes accepts a user-provided attribute dictionary
4471 * and attempts to return a sanitized copy for passing to the underlying
4472 * platform-specific implementation code.
4473 *
4474 * If iOSOut is true, one or more translations may apply:
4475 * - SecKeychain refs are removed, since there aren't multiple keychains
4476 * - SecPolicy refs are removed, since they can't be externalized
4477 * - SecAccess refs are removed, and potentially translated to entitlements
4478 *
4479 * If pruneMatch is true, kSecMatch* attributes are removed; this avoids
4480 * parameter errors due to strict input checks in secd, which only permits
4481 * these constants for calls to SecItemCopyMatching.
4482 *
4483 * If pruneSync is true, the kSecAttrSynchronizable attribute is removed.
4484 * This permits a query to be reused for non-synchronizable items, or to
4485 * resolve a search based on a persistent item reference for iOS.
4486 *
4487 * If pruneReturn is true, kSecReturn* attributes are removed; this avoids
4488 * parameter errors due to strict input checks in secd, which do not permit
4489 * these constants for calls to SecItemUpdate.
4490 */
4491 CFDictionaryRef
4492 SecItemCopyTranslatedAttributes(CFDictionaryRef inOSXDict, CFTypeRef itemClass,
4493 bool iOSOut, bool pruneMatch, bool pruneSync, bool pruneReturn, bool pruneData, bool pruneAccess)
4494 {
4495 CFMutableDictionaryRef result = CFDictionaryCreateMutableCopy(NULL, 0, inOSXDict);
4496 if (result == NULL) {
4497 return result;
4498 }
4499
4500 if (pruneSync) {
4501 CFDictionaryRemoveValue(result, kSecAttrSynchronizable);
4502 }
4503
4504 if (pruneMatch) {
4505 /* Match constants are only supported on iOS for SecItemCopyMatching,
4506 * and will generate an error if passed to other SecItem API functions;
4507 * on OS X, they're just ignored if not applicable for the context.
4508 */
4509 CFDictionaryRemoveValue(result, kSecMatchPolicy);
4510 CFDictionaryRemoveValue(result, kSecMatchItemList);
4511 CFDictionaryRemoveValue(result, kSecMatchSearchList);
4512 CFDictionaryRemoveValue(result, kSecMatchIssuers);
4513 CFDictionaryRemoveValue(result, kSecMatchEmailAddressIfPresent);
4514 CFDictionaryRemoveValue(result, kSecMatchSubjectContains);
4515 CFDictionaryRemoveValue(result, kSecMatchCaseInsensitive);
4516 CFDictionaryRemoveValue(result, kSecMatchTrustedOnly);
4517 CFDictionaryRemoveValue(result, kSecMatchValidOnDate);
4518 CFDictionaryRemoveValue(result, kSecMatchLimit);
4519 CFDictionaryRemoveValue(result, kSecMatchLimitOne);
4520 CFDictionaryRemoveValue(result, kSecMatchLimitAll);
4521 }
4522
4523 if (pruneReturn) {
4524 /* Return constants are not supported on iOS for SecItemUpdate,
4525 * where they will generate an error; on OS X, they're just ignored
4526 * if not applicable for the context.
4527 */
4528 CFDictionaryRemoveValue(result, kSecReturnData);
4529 CFDictionaryRemoveValue(result, kSecReturnAttributes);
4530 CFDictionaryRemoveValue(result, kSecReturnRef);
4531 CFDictionaryRemoveValue(result, kSecReturnPersistentRef);
4532 }
4533
4534 if (pruneData) {
4535 /* Searching on data is not supported. */
4536 CFDictionaryRemoveValue(result, kSecValueData);
4537 }
4538
4539 if (pruneAccess) {
4540 /* Searching on access lists is not supported */
4541 CFDictionaryRemoveValue(result, kSecAttrAccess);
4542 }
4543
4544 if (iOSOut) {
4545 /* Remove kSecMatchSearchList (value is array of SecKeychainRef);
4546 * cannot specify a keychain search list on iOS
4547 */
4548 CFDictionaryRemoveValue(result, kSecMatchSearchList);
4549
4550 /* Remove kSecUseKeychain (value is a SecKeychainRef);
4551 * cannot specify a keychain on iOS
4552 */
4553 CFDictionaryRemoveValue(result, kSecUseKeychain);
4554
4555 /* Potentially translate kSecAttrAccess (value is a SecAccessRef),
4556 * unless kSecAttrAccessGroup has already been specified.
4557 */
4558 SecAccessRef access = (SecAccessRef) CFDictionaryGetValue(result, kSecAttrAccess);
4559 CFStringRef accessGroup = (CFStringRef) CFDictionaryGetValue(result, kSecAttrAccessGroup);
4560 if (access != NULL && accessGroup == NULL) {
4561 /* Translate "InternetAccounts" application group to an access group */
4562 if (errSecSuccess == SecItemValidateAppleApplicationGroupAccess(CFSTR("InternetAccounts"))) {
4563 /* The caller is a valid member of the application group. */
4564 CFStringRef groupName = CFSTR("appleaccount");
4565 CFTypeRef value = CFDictionaryGetValue(result, kSecAttrAuthenticationType);
4566 if (value && CFEqual(value, kSecAttrAuthenticationTypeHTMLForm)) {
4567 groupName = CFSTR("com.apple.cfnetwork");
4568 }
4569 CFDictionarySetValue(result, kSecAttrAccessGroup, groupName);
4570 }
4571 }
4572 CFDictionaryRemoveValue(result, kSecAttrAccess);
4573
4574 /* If item is specified by direct reference, and this is an iOS search,
4575 * replace it with a persistent reference, if it was recorded inside ItemImpl.
4576 */
4577 CFTypeRef directRef = CFDictionaryGetValue(result, kSecValueRef);
4578 if (directRef != NULL) {
4579 CFTypeID typeID = CFGetTypeID(directRef);
4580 if ((typeID != SecKeyGetTypeID() || SecKeyIsCDSAKey((SecKeyRef)directRef)) &&
4581 (typeID != SecCertificateGetTypeID() || SecCertificateIsItemImplInstance((SecCertificateRef)directRef)) &&
4582 (typeID != SecIdentityGetTypeID())) {
4583 CFDataRef persistentRef = _SecItemGetPersistentReference(directRef);
4584 if (persistentRef) {
4585 CFDictionarySetValue(result, kSecValuePersistentRef, persistentRef);
4586 CFDictionaryRemoveValue(result, kSecValueRef);
4587 }
4588 }
4589 }
4590
4591 /* If item is specified by persistent reference, and this is an iOS search,
4592 * remove the synchronizable attribute as it will be rejected by secd.
4593 */
4594 CFTypeRef persistentRef = CFDictionaryGetValue(result, kSecValuePersistentRef);
4595 if (persistentRef) {
4596 CFDictionaryRemoveValue(result, kSecAttrSynchronizable);
4597 }
4598
4599 /* Remove kSecAttrModificationDate; this should never be used as criteria
4600 * for a search, or to add/modify an item. (If we are cloning an item
4601 * and want to keep its modification date, we don't call this function.)
4602 * It turns out that some clients are using the full attributes dictionary
4603 * returned by SecItemCopyMatching as a query to find the same item later,
4604 * which won't work once the item is updated.
4605 */
4606 CFDictionaryRemoveValue(result, kSecAttrModificationDate);
4607 }
4608 else {
4609 /* iOS doesn't add the class attribute, so we must do it here. */
4610 if (itemClass)
4611 CFDictionarySetValue(result, kSecClass, itemClass);
4612
4613 /* Remove attributes which are not part of the OS X database schema. */
4614 CFDictionaryRemoveValue(result, kSecAttrAccessible);
4615 CFDictionaryRemoveValue(result, kSecAttrAccessControl);
4616 CFDictionaryRemoveValue(result, kSecAttrAccessGroup);
4617 CFDictionaryRemoveValue(result, kSecAttrSynchronizable);
4618 CFDictionaryRemoveValue(result, kSecAttrTombstone);
4619 }
4620
4621 /* This attribute is consumed by the bridge itself. */
4622 CFDictionaryRemoveValue(result, kSecAttrNoLegacy);
4623
4624 return result;
4625 }
4626
4627 } /* extern "C" */
4628
4629 static OSStatus
4630 SecItemMergeResults(bool can_target_ios, OSStatus status_ios, CFTypeRef result_ios,
4631 bool can_target_osx, OSStatus status_osx, CFTypeRef result_osx,
4632 CFTypeRef *result) {
4633 // When querying both keychains and iOS keychain fails because of missing
4634 // entitlements, completely ignore iOS keychain result. This is to keep
4635 // backward compatibility with applications which know nothing about iOS keychain
4636 // and use SecItem API to access OSX keychain which does not need any entitlements.
4637 if (can_target_osx && can_target_ios && status_ios == errSecMissingEntitlement) {
4638 can_target_ios = false;
4639 }
4640
4641 if (can_target_osx && can_target_ios) {
4642 // If both keychains were targetted, examine returning statuses and decide what to do.
4643 if (status_ios != errSecSuccess) {
4644 // iOS keychain failed to produce results because of some error, go with results from OSX keychain.
4645 AssignOrReleaseResult(result_osx, result);
4646 return status_osx;
4647 } else if (status_osx != errSecSuccess) {
4648 if (status_osx != errSecItemNotFound) {
4649 // OSX failed to produce results with some failure mode (else than not_found), but iOS produced results.
4650 // We have to either return OSX failure result and discard iOS results, or vice versa. For now, we just
4651 // ignore OSX error and return just iOS results.
4652 secitemlog(LOG_NOTICE, "SecItemMergeResults: osx_result=%d, ignoring it, iOS succeeded fine", status_osx);
4653 }
4654
4655 // OSX failed to produce results, but we have success from iOS keychain; go with results from iOS keychain.
4656 AssignOrReleaseResult(result_ios, result);
4657 return errSecSuccess;
4658 } else {
4659 // Both searches succeeded, merge results.
4660 if (result != NULL) {
4661 CFTypeID id_osx = (result_osx) ? CFGetTypeID(result_osx) : 0;
4662 CFTypeID id_ios = (result_ios) ? CFGetTypeID(result_ios) : 0;
4663 CFTypeID id_array = CFArrayGetTypeID();
4664 if ((id_osx == id_array) && (id_ios == id_array)) {
4665 // Fold the arrays into one.
4666 *result = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
4667 CFArrayAppendArray((CFMutableArrayRef)*result, (CFArrayRef)result_ios,
4668 CFRangeMake(0, CFArrayGetCount((CFArrayRef)result_ios)));
4669 CFArrayAppendArray((CFMutableArrayRef)*result, (CFArrayRef)result_osx,
4670 CFRangeMake(0, CFArrayGetCount((CFArrayRef)result_osx)));
4671 } else {
4672 // Result type is not an array, so only one match can be returned.
4673 *result = (id_ios) ? result_ios : result_osx;
4674 CFRetainSafe(*result);
4675 }
4676 }
4677 CFReleaseSafe(result_osx);
4678 CFReleaseSafe(result_ios);
4679 return errSecSuccess;
4680 }
4681 } else if (can_target_ios) {
4682 // Only iOS keychain was targetted.
4683 AssignOrReleaseResult(result_ios, result);
4684 return status_ios;
4685 } else if (can_target_osx) {
4686 // Only OSX keychain was targetted.
4687 AssignOrReleaseResult(result_osx, result);
4688 return status_osx;
4689 } else {
4690 // Query could not run at all?
4691 return errSecParam;
4692 }
4693 }
4694
4695 static bool
4696 ShouldTryUnlockKeybag(OSErr status)
4697 {
4698 static typeof(SASSessionStateForUser) *soft_SASSessionStateForUser = NULL;
4699 static dispatch_once_t onceToken;
4700 static void *framework;
4701
4702 if (status != errSecInteractionNotAllowed)
4703 return false;
4704
4705 dispatch_once(&onceToken, ^{
4706 framework = dlopen("/System/Library/PrivateFrameworks/login.framework/login", RTLD_LAZY);
4707 if (framework == NULL)
4708 return;
4709 soft_SASSessionStateForUser = (typeof(soft_SASSessionStateForUser)) dlsym(framework, "SASSessionStateForUser");
4710 });
4711
4712 if (soft_SASSessionStateForUser == NULL)
4713 return false;
4714
4715 SessionAgentState sessionState = soft_SASSessionStateForUser(getuid());
4716 if(sessionState != kSA_state_desktopshowing)
4717 return false;
4718
4719 return true;
4720 }
4721
4722 OSStatus
4723 SecItemCopyMatching(CFDictionaryRef query, CFTypeRef *result)
4724 {
4725 secitemlog(LOG_NOTICE, "SecItemCopyMatching");
4726 if (!query) {
4727 return errSecParam;
4728 }
4729 secitemshow(query, "SecItemCopyMatching query:");
4730
4731 OSStatus status_osx = errSecItemNotFound, status_ios = errSecItemNotFound;
4732 CFTypeRef result_osx = NULL, result_ios = NULL;
4733 bool can_target_ios, can_target_osx;
4734 OSStatus status = SecItemCategorizeQuery(query, can_target_ios, can_target_osx);
4735 if (status != errSecSuccess) {
4736 return status;
4737 }
4738
4739 if (can_target_ios) {
4740 CFDictionaryRef attrs_ios = SecItemCopyTranslatedAttributes(query,
4741 CFDictionaryGetValue(query, kSecClass), true, false, false, false, true, true);
4742 if (!attrs_ios) {
4743 status_ios = errSecParam;
4744 }
4745 else {
4746 status_ios = SecItemCopyMatching_ios(attrs_ios, &result_ios);
4747 if(ShouldTryUnlockKeybag(status_ios)) {
4748 // The keybag is locked. Attempt to unlock it...
4749 secitemlog(LOG_WARNING, "SecItemCopyMatching triggering SecurityAgent");
4750 if(errSecSuccess == SecKeychainVerifyKeyStorePassphrase(1)) {
4751 CFReleaseNull(result_ios);
4752 status_ios = SecItemCopyMatching_ios(attrs_ios, &result_ios);
4753 }
4754 }
4755 CFRelease(attrs_ios);
4756 }
4757 secitemlog(LOG_NOTICE, "SecItemCopyMatching_ios result: %d", status_ios);
4758 }
4759
4760 if (can_target_osx) {
4761 CFDictionaryRef attrs_osx = SecItemCopyTranslatedAttributes(query,
4762 CFDictionaryGetValue(query, kSecClass), false, false, true, false, true, true);
4763 if (!attrs_osx) {
4764 status_osx = errSecParam;
4765 }
4766 else {
4767 status_osx = SecItemCopyMatching_osx(attrs_osx, &result_osx);
4768 CFRelease(attrs_osx);
4769 }
4770 secitemlog(LOG_NOTICE, "SecItemCopyMatching_osx result: %d", status_osx);
4771 }
4772
4773 status = SecItemMergeResults(can_target_ios, status_ios, result_ios,
4774 can_target_osx, status_osx, result_osx, result);
4775 secitemlog(LOG_NOTICE, "SecItemCopyMatching result: %d", status);
4776 return status;
4777 }
4778
4779 OSStatus
4780 SecItemAdd(CFDictionaryRef attributes, CFTypeRef *result)
4781 {
4782 secitemlog(LOG_NOTICE, "SecItemAdd");
4783 if (!attributes) {
4784 return errSecParam;
4785 }
4786 else if (result) {
4787 *result = NULL;
4788 }
4789 secitemshow(attributes, "SecItemAdd attrs:");
4790
4791 CFTypeRef result_osx = NULL, result_ios = NULL;
4792 bool can_target_ios, can_target_osx;
4793 OSStatus status = SecItemCategorizeQuery(attributes, can_target_ios, can_target_osx);
4794 if (status != errSecSuccess) {
4795 return status;
4796 }
4797
4798 // SecItemAdd cannot be really done on both keychains. In order to keep backward compatibility
4799 // with existing applications, we prefer to add items into legacy keychain and fallback
4800 // into iOS (modern) keychain only when the query is not suitable for legacy keychain.
4801 if (!can_target_osx) {
4802 CFDictionaryRef attrs_ios = SecItemCopyTranslatedAttributes(attributes,
4803 NULL, true, true, false, false, false, false);
4804 if (!attrs_ios) {
4805 status = errSecParam;
4806 } else {
4807 status = SecItemAdd_ios(attrs_ios, &result_ios);
4808 if(ShouldTryUnlockKeybag(status)) {
4809 // The keybag is locked. Attempt to unlock it...
4810 secitemlog(LOG_WARNING, "SecItemAdd triggering SecurityAgent");
4811 if(errSecSuccess == SecKeychainVerifyKeyStorePassphrase(3)) {
4812 CFReleaseNull(result_ios);
4813 status = SecItemAdd_ios(attrs_ios, &result_ios);
4814 }
4815 }
4816 CFRelease(attrs_ios);
4817 }
4818 secitemlog(LOG_NOTICE, "SecItemAdd_ios result: %d", status);
4819 AssignOrReleaseResult(result_ios, result);
4820 return status;
4821 } else {
4822 CFDictionaryRef attrs_osx = SecItemCopyTranslatedAttributes(attributes,
4823 NULL, false, false, true, false, false, false);
4824 if (!attrs_osx) {
4825 status = errSecParam;
4826 } else {
4827 status = SecItemAdd_osx(attrs_osx, &result_osx);
4828 CFRelease(attrs_osx);
4829 }
4830 secitemlog(LOG_NOTICE, "SecItemAdd_osx result: %d", status);
4831 AssignOrReleaseResult(result_osx, result);
4832 return status;
4833 }
4834 }
4835
4836 OSStatus
4837 SecItemUpdate(CFDictionaryRef query, CFDictionaryRef attributesToUpdate)
4838 {
4839 secitemlog(LOG_NOTICE, "SecItemUpdate");
4840 if (!query || !attributesToUpdate) {
4841 return errSecParam;
4842 }
4843 secitemshow(query, "SecItemUpdate query:");
4844 secitemshow(attributesToUpdate, "SecItemUpdate attrs:");
4845
4846 OSStatus status_osx = errSecItemNotFound, status_ios = errSecItemNotFound;
4847 bool can_target_ios, can_target_osx;
4848 OSStatus status = SecItemCategorizeQuery(query, can_target_ios, can_target_osx);
4849 if (status != errSecSuccess) {
4850 return status;
4851 }
4852
4853 if (can_target_ios) {
4854 CFDictionaryRef attrs_ios = SecItemCopyTranslatedAttributes(query,
4855 CFDictionaryGetValue(query, kSecClass), true, true, false, true, true, true);
4856 if (!attrs_ios) {
4857 status_ios = errSecParam;
4858 }
4859 else {
4860 if (SecItemHasSynchronizableUpdate(true, attributesToUpdate)) {
4861 status_ios = SecItemChangeSynchronizability(attrs_ios, attributesToUpdate, false);
4862 if(ShouldTryUnlockKeybag(status_ios)) {
4863 // The keybag is locked. Attempt to unlock it...
4864 secitemlog(LOG_WARNING, "SecItemUpdate triggering SecurityAgent");
4865 if(errSecSuccess == SecKeychainVerifyKeyStorePassphrase(1)) {
4866 status_ios = SecItemChangeSynchronizability(attrs_ios, attributesToUpdate, false);
4867 }
4868 }
4869 } else {
4870 status_ios = SecItemUpdate_ios(attrs_ios, attributesToUpdate);
4871 if(ShouldTryUnlockKeybag(status_ios)) {
4872 // The keybag is locked. Attempt to unlock it...
4873 secitemlog(LOG_WARNING, "SecItemUpdate triggering SecurityAgent");
4874 if(errSecSuccess == SecKeychainVerifyKeyStorePassphrase(1)) {
4875 status_ios = SecItemUpdate_ios(attrs_ios, attributesToUpdate);
4876 }
4877 }
4878 }
4879 CFRelease(attrs_ios);
4880 }
4881 secitemlog(LOG_NOTICE, "SecItemUpdate_ios result: %d", status_ios);
4882 }
4883
4884 if (can_target_osx) {
4885 CFDictionaryRef attrs_osx = SecItemCopyTranslatedAttributes(query,
4886 CFDictionaryGetValue(query, kSecClass), false, false, true, true, true, true);
4887 if (!attrs_osx) {
4888 status_osx = errSecParam;
4889 }
4890 else {
4891 if (SecItemHasSynchronizableUpdate(false, attributesToUpdate))
4892 status_osx = SecItemChangeSynchronizability(attrs_osx, attributesToUpdate, true);
4893 else
4894 status_osx = SecItemUpdate_osx(attrs_osx, attributesToUpdate);
4895
4896 CFRelease(attrs_osx);
4897 }
4898 secitemlog(LOG_NOTICE, "SecItemUpdate_osx result: %d", status_osx);
4899 }
4900
4901 status = SecItemMergeResults(can_target_ios, status_ios, NULL,
4902 can_target_osx, status_osx, NULL, NULL);
4903 secitemlog(LOG_NOTICE, "SecItemUpdate result: %d", status);
4904 return status;
4905 }
4906
4907 OSStatus
4908 SecItemDelete(CFDictionaryRef query)
4909 {
4910 secitemlog(LOG_NOTICE, "SecItemDelete");
4911 if (!query) {
4912 return errSecParam;
4913 }
4914 secitemshow(query, "SecItemDelete query:");
4915
4916 OSStatus status_osx = errSecItemNotFound, status_ios = errSecItemNotFound;
4917 bool can_target_ios, can_target_osx;
4918 OSStatus status = SecItemCategorizeQuery(query, can_target_ios, can_target_osx);
4919 if (status != errSecSuccess) {
4920 return status;
4921 }
4922
4923 if (can_target_ios) {
4924 CFDictionaryRef attrs_ios = SecItemCopyTranslatedAttributes(query,
4925 NULL, true, true, false, true, true, true);
4926 if (!attrs_ios) {
4927 status_ios = errSecParam;
4928 } else {
4929 status_ios = SecItemDelete_ios(attrs_ios);
4930 CFRelease(attrs_ios);
4931 }
4932 secitemlog(LOG_NOTICE, "SecItemDelete_ios result: %d", status_ios);
4933 }
4934
4935 if (can_target_osx) {
4936 CFDictionaryRef attrs_osx = SecItemCopyTranslatedAttributes(query,
4937 NULL, false, false, true, true, true, true);
4938 if (!attrs_osx) {
4939 status_osx = errSecParam;
4940 } else {
4941 status_osx = SecItemDelete_osx(attrs_osx);
4942 CFRelease(attrs_osx);
4943 }
4944 secitemlog(LOG_NOTICE, "SecItemDelete_osx result: %d", status_osx);
4945 }
4946
4947 status = SecItemMergeResults(can_target_ios, status_ios, NULL,
4948 can_target_osx, status_osx, NULL, NULL);
4949 secitemlog(LOG_NOTICE, "SecItemCopyDelete result: %d", status);
4950 return status;
4951 }
4952
4953 OSStatus
4954 SecItemUpdateTokenItems(CFTypeRef tokenID, CFArrayRef tokenItemsAttributes)
4955 {
4956 OSStatus status = SecItemUpdateTokenItems_ios(tokenID, tokenItemsAttributes);
4957 if(ShouldTryUnlockKeybag(status)) {
4958 // The keybag is locked. Attempt to unlock it...
4959 if(errSecSuccess == SecKeychainVerifyKeyStorePassphrase(1)) {
4960 secitemlog(LOG_WARNING, "SecItemUpdateTokenItems triggering SecurityAgent");
4961 status = SecItemUpdateTokenItems_ios(tokenID, tokenItemsAttributes);
4962 }
4963 }
4964 secitemlog(LOG_NOTICE, "SecItemUpdateTokenItems_ios result: %d", status);
4965 return status;
4966 }
4967
4968 OSStatus
4969 SecItemCopyMatching_osx(
4970 CFDictionaryRef query,
4971 CFTypeRef *result)
4972 {
4973 if (!query || !result)
4974 return errSecParam;
4975 else
4976 *result = NULL;
4977
4978 CFAllocatorRef allocator = CFGetAllocator(query);
4979 CFIndex matchCount = 0;
4980 CFMutableArrayRef itemArray = NULL;
4981 SecKeychainItemRef item = NULL;
4982 SecIdentityRef identity = NULL;
4983 OSStatus tmpStatus, status = errSecSuccess;
4984
4985 // validate input query parameters and create the search reference
4986 SecItemParams *itemParams = _CreateSecItemParamsFromDictionary(query, &status);
4987 require_action(itemParams != NULL, error_exit, itemParams = NULL);
4988
4989 // find the next match until we hit maxMatches, or no more matches found
4990 while ( !(!itemParams->returnAllMatches && matchCount >= itemParams->maxMatches) &&
4991 SecItemSearchCopyNext(itemParams, (CFTypeRef*)&item) == errSecSuccess) {
4992
4993 if (FilterCandidateItem((CFTypeRef*)&item, itemParams, &identity))
4994 continue; // move on to next item
4995
4996 ++matchCount; // we have a match
4997
4998 tmpStatus = AddItemResults(item, identity, itemParams, allocator, &itemArray, result);
4999 if (tmpStatus && (status == errSecSuccess))
5000 status = tmpStatus;
5001
5002 if (item) {
5003 CFRelease(item);
5004 item = NULL;
5005 }
5006 if (identity) {
5007 CFRelease(identity);
5008 identity = NULL;
5009 }
5010 }
5011
5012 if (status == errSecSuccess)
5013 status = (matchCount > 0) ? errSecSuccess : errSecItemNotFound;
5014
5015 error_exit:
5016 if (status != errSecSuccess && result != NULL && *result != NULL) {
5017 CFRelease(*result);
5018 *result = NULL;
5019 }
5020 _FreeSecItemParams(itemParams);
5021
5022 return status;
5023 }
5024
5025 OSStatus
5026 SecItemCopyDisplayNames(
5027 CFArrayRef items,
5028 CFArrayRef *displayNames)
5029 {
5030 BEGIN_SECAPI
5031 Required(items);
5032 Required(displayNames);
5033 //%%%TBI
5034 return errSecUnimplemented;
5035 END_SECAPI
5036 }
5037
5038 OSStatus
5039 SecItemAdd_osx(
5040 CFDictionaryRef attributes,
5041 CFTypeRef *result)
5042 {
5043 if (!attributes)
5044 return errSecParam;
5045 else if (result)
5046 *result = NULL;
5047
5048 CFAllocatorRef allocator = CFGetAllocator(attributes);
5049 CFMutableArrayRef itemArray = NULL;
5050 SecKeychainItemRef item = NULL;
5051 OSStatus tmpStatus, status = errSecSuccess;
5052
5053 // validate input attribute parameters
5054 SecItemParams *itemParams = _CreateSecItemParamsFromDictionary(attributes, &status);
5055 require_action(itemParams != NULL, error_exit, itemParams = NULL);
5056
5057 // currently, we don't support adding SecIdentityRef items (an aggregate item class),
5058 // since the private key should already be in a keychain by definition. We could support
5059 // this as a copy operation for the private key if a different keychain is specified,
5060 // but in any case it should try to add the certificate. See <rdar://8317887>.
5061 require_action(!itemParams->returnIdentity, error_exit, status = errSecItemInvalidValue);
5062
5063 if (itemParams->useItems == NULL) {
5064
5065 require_action(itemParams->itemData == NULL || CFGetTypeID(itemParams->itemData) == CFDataGetTypeID(),
5066 error_exit, status = errSecItemInvalidValue);
5067
5068 // create a single keychain item specified by the input attributes
5069 status = SecKeychainItemCreateFromContent(itemParams->itemClass,
5070 itemParams->attrList,
5071 (itemParams->itemData) ? (UInt32)CFDataGetLength(itemParams->itemData) : 0,
5072 (itemParams->itemData) ? CFDataGetBytePtrVoid(itemParams->itemData) : NULL,
5073 itemParams->keychain,
5074 itemParams->access,
5075 &item);
5076 require_noerr(status, error_exit);
5077
5078 // return results (if requested)
5079 if (result) {
5080 itemParams->maxMatches = 1; // in case kSecMatchLimit was set to > 1
5081 tmpStatus = AddItemResults(item, NULL, itemParams, allocator, &itemArray, result);
5082 if (tmpStatus && (status == errSecSuccess))
5083 status = tmpStatus;
5084 }
5085 CFRelease(item);
5086 }
5087 else {
5088 // add multiple items which are specified in the itemParams->useItems array.
5089 // -- SecCertificateRef or SecKeyRef items may or may not be in a keychain.
5090 // -- SecKeychainItemRef items are in a keychain (by definition), but may be copied to another keychain.
5091 // -- CFDataRef items are a persistent reference; the represented item may be copied to another keychain.
5092 //
5093 OSStatus aggregateStatus = errSecSuccess;
5094 CFIndex ix, count = CFArrayGetCount(itemParams->useItems);
5095 itemParams->maxMatches = (count > 1) ? (int)count : 2; // force results to always be returned as an array
5096 for (ix=0; ix < count; ix++) {
5097 CFTypeRef anItem = (CFTypeRef) CFArrayGetValueAtIndex(itemParams->useItems, ix);
5098 if (anItem) {
5099 if (SecCertificateGetTypeID() == CFGetTypeID(anItem)) {
5100 // SecCertificateRef item
5101 tmpStatus = SecCertificateAddToKeychain((SecCertificateRef)anItem, itemParams->keychain);
5102 if (!tmpStatus && result) {
5103 tmpStatus = AddItemResults((SecKeychainItemRef)anItem, NULL, itemParams, allocator, &itemArray, result);
5104 }
5105 aggregateStatus = _UpdateAggregateStatus(tmpStatus, aggregateStatus, errSecDuplicateItem);
5106 }
5107 else if (SecKeyGetTypeID() == CFGetTypeID(anItem)) {
5108 // SecKeyRef item
5109 SecKeychainRef itemKeychain = NULL;
5110 tmpStatus = SecKeychainItemCopyKeychain((SecKeychainItemRef)anItem, &itemKeychain);
5111 if (tmpStatus == errSecSuccess) {
5112 // key was in a keychain, so we can attempt to copy it
5113 SecKeychainItemRef itemCopy = NULL;
5114 tmpStatus = SecKeychainItemCreateCopy((SecKeychainItemRef)anItem, itemParams->keychain, itemParams->access, &itemCopy);
5115 if (!tmpStatus && result) {
5116 tmpStatus = AddItemResults(itemCopy, NULL, itemParams, allocator, &itemArray, result);
5117 }
5118 if (itemCopy) {
5119 CFRelease(itemCopy);
5120 }
5121 }
5122 else {
5123 // key was not in any keychain, so must be imported
5124 SecKeychainItemRef keyItem = NULL;
5125 tmpStatus = _ImportKey((SecKeyRef)anItem, itemParams->keychain, itemParams->access, itemParams->attrList, &keyItem);
5126 if (!tmpStatus && result) {
5127 tmpStatus = AddItemResults(keyItem, NULL, itemParams, allocator, &itemArray, result);
5128 }
5129 if (keyItem) {
5130 CFRelease(keyItem);
5131 }
5132 }
5133 if (itemKeychain) {
5134 CFRelease(itemKeychain);
5135 }
5136 aggregateStatus = _UpdateAggregateStatus(tmpStatus, aggregateStatus, errSecDuplicateItem);
5137 }
5138 else if (SecKeychainItemGetTypeID() == CFGetTypeID(anItem)) {
5139 // SecKeychainItemRef item
5140 SecKeychainItemRef itemCopy = NULL;
5141 tmpStatus = SecKeychainItemCreateCopy((SecKeychainItemRef)anItem, itemParams->keychain, itemParams->access, &itemCopy);
5142 if (!tmpStatus && result) {
5143 tmpStatus = AddItemResults(itemCopy, NULL, itemParams, allocator, &itemArray, result);
5144 }
5145 if (itemCopy) {
5146 CFRelease(itemCopy);
5147 }
5148 aggregateStatus = _UpdateAggregateStatus(tmpStatus, aggregateStatus, errSecDuplicateItem);
5149 }
5150 else if (CFDataGetTypeID() == CFGetTypeID(anItem)) {
5151 // CFDataRef item (persistent reference)
5152 SecKeychainItemRef realItem = NULL;
5153 tmpStatus = SecKeychainItemCopyFromPersistentReference((CFDataRef)anItem, &realItem);
5154 if (tmpStatus == errSecSuccess) {
5155 // persistent reference resolved to a keychain item, so we can attempt to copy it
5156 SecKeychainItemRef itemCopy = NULL;
5157 tmpStatus = SecKeychainItemCreateCopy(realItem, itemParams->keychain, itemParams->access, &itemCopy);
5158 if (!tmpStatus && result) {
5159 tmpStatus = AddItemResults(itemCopy, NULL, itemParams, allocator, &itemArray, result);
5160 }
5161 if (itemCopy) {
5162 CFRelease(itemCopy);
5163 }
5164 }
5165 if (realItem) {
5166 CFRelease(realItem);
5167 }
5168 aggregateStatus = _UpdateAggregateStatus(tmpStatus, aggregateStatus, errSecDuplicateItem);
5169 }
5170 }
5171 } // end of itemList array loop
5172 status = aggregateStatus;
5173 } // end processing multiple items
5174
5175 error_exit:
5176 if (status != errSecSuccess && result != NULL && *result != NULL) {
5177 CFRelease(*result);
5178 *result = NULL;
5179 }
5180 _FreeSecItemParams(itemParams);
5181
5182 return status;
5183 }
5184
5185 OSStatus
5186 SecItemUpdate_osx(
5187 CFDictionaryRef query,
5188 CFDictionaryRef attributesToUpdate)
5189 {
5190 if (!query || !attributesToUpdate)
5191 return errSecParam;
5192
5193 // run the provided query to get a list of items to update
5194 CFTypeRef results = NULL;
5195 OSStatus status = SecItemCopyMatching_osx(query, &results);
5196 if (status != errSecSuccess)
5197 return status; // nothing was matched, or the query was bad
5198
5199 CFArrayRef items = NULL;
5200 if (CFArrayGetTypeID() == CFGetTypeID(results)) {
5201 items = (CFArrayRef) results;
5202 }
5203 else {
5204 items = CFArrayCreate(NULL, &results, 1, &kCFTypeArrayCallBacks);
5205 CFRelease(results);
5206 }
5207
5208 OSStatus result = errSecSuccess;
5209 CFIndex ix, count = CFArrayGetCount(items);
5210 for (ix=0; ix < count; ix++) {
5211 CFTypeRef anItem = (CFTypeRef) CFArrayGetValueAtIndex(items, ix);
5212 if (anItem) {
5213 status = _UpdateKeychainItem(anItem, attributesToUpdate);
5214 result = _UpdateAggregateStatus(status, result, errSecSuccess);
5215 }
5216 }
5217
5218 if (items) {
5219 CFRelease(items);
5220 }
5221 return result;
5222 }
5223
5224 OSStatus
5225 SecItemDelete_osx(
5226 CFDictionaryRef query)
5227 {
5228 if (!query)
5229 return errSecParam;
5230
5231 // run the provided query to get a list of items to delete
5232 CFTypeRef results = NULL;
5233 OSStatus status = SecItemCopyMatching_osx(query, &results);
5234 if (status != errSecSuccess)
5235 return status; // nothing was matched, or the query was bad
5236
5237 CFArrayRef items = NULL;
5238 if (CFArrayGetTypeID() == CFGetTypeID(results)) {
5239 items = (CFArrayRef) results;
5240 }
5241 else {
5242 items = CFArrayCreate(NULL, &results, 1, &kCFTypeArrayCallBacks);
5243 CFRelease(results);
5244 }
5245
5246 OSStatus result = errSecSuccess;
5247 CFIndex ix, count = CFArrayGetCount(items);
5248 for (ix=0; ix < count; ix++) {
5249 CFTypeRef anItem = (CFTypeRef) CFArrayGetValueAtIndex(items, ix);
5250 if (anItem) {
5251 if (SecIdentityGetTypeID() == CFGetTypeID(anItem)) {
5252 status = _DeleteIdentity((SecIdentityRef)anItem);
5253 }
5254 else {
5255 status = _DeleteKeychainItem(anItem);
5256 }
5257 result = _UpdateAggregateStatus(status, result, errSecSuccess);
5258 }
5259 }
5260
5261 if (items)
5262 CFRelease(items);
5263
5264 return result;
5265 }