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