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